620 lines
11 KiB
Markdown
620 lines
11 KiB
Markdown
# DOMAIN MODEL: Project Management Context
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Modulos:** MAI-002, MAI-003, MAI-005
|
|
|
|
---
|
|
|
|
## Descripcion
|
|
|
|
El contexto de Project Management es el nucleo del ERP de Construccion. Gestiona todo el ciclo de vida de proyectos de obra, desde la planeacion hasta la entrega, incluyendo presupuestos, control de avances y estimaciones.
|
|
|
|
---
|
|
|
|
## Agregados
|
|
|
|
### 1. Project Aggregate
|
|
|
|
```
|
|
Project (Aggregate Root)
|
|
|
|
|
+-- Development (Entity)
|
|
| +-- Section (Entity)
|
|
| +-- HousingUnit (Entity)
|
|
|
|
|
+-- ProjectManager (Value Object)
|
|
+-- Location (Value Object)
|
|
+-- ProjectStatus (Value Object)
|
|
```
|
|
|
|
#### Project (Root)
|
|
```typescript
|
|
interface Project {
|
|
id: UUID;
|
|
tenantId: UUID;
|
|
code: string;
|
|
name: string;
|
|
description?: string;
|
|
|
|
// Location
|
|
address?: string;
|
|
city?: string;
|
|
state?: string;
|
|
postalCode?: string;
|
|
location?: Location;
|
|
|
|
// Timeline
|
|
startDate?: Date;
|
|
endDate?: Date;
|
|
actualStartDate?: Date;
|
|
actualEndDate?: Date;
|
|
|
|
// Status
|
|
status: ProjectStatus;
|
|
progressPercentage: Percentage;
|
|
|
|
// Financial
|
|
totalBudget?: Money;
|
|
totalSpent: Money;
|
|
|
|
// Relationships
|
|
projectManager?: User;
|
|
developments: Development[];
|
|
budgets: Budget[];
|
|
schedules: Schedule[];
|
|
|
|
// Audit
|
|
createdAt: Timestamp;
|
|
createdBy: UUID;
|
|
updatedAt?: Timestamp;
|
|
updatedBy?: UUID;
|
|
}
|
|
|
|
enum ProjectStatus {
|
|
PLANNING = 'planning',
|
|
IN_PROGRESS = 'in_progress',
|
|
PAUSED = 'paused',
|
|
COMPLETED = 'completed',
|
|
CANCELLED = 'cancelled'
|
|
}
|
|
```
|
|
|
|
#### Development (Entity)
|
|
```typescript
|
|
interface Development {
|
|
id: UUID;
|
|
projectId: UUID;
|
|
code: string;
|
|
name: string;
|
|
description?: string;
|
|
address?: string;
|
|
location?: Location;
|
|
areaM2?: number;
|
|
|
|
totalSections: number;
|
|
totalUnits: number;
|
|
completedUnits: number;
|
|
|
|
status: ProjectStatus;
|
|
sections: Section[];
|
|
}
|
|
```
|
|
|
|
#### Section (Entity)
|
|
```typescript
|
|
interface Section {
|
|
id: UUID;
|
|
developmentId: UUID;
|
|
code: string;
|
|
name: string;
|
|
boundary?: Polygon;
|
|
areaM2?: number;
|
|
|
|
totalUnits: number;
|
|
completedUnits: number;
|
|
|
|
housingUnits: HousingUnit[];
|
|
}
|
|
```
|
|
|
|
#### HousingUnit (Entity)
|
|
```typescript
|
|
interface HousingUnit {
|
|
id: UUID;
|
|
sectionId: UUID;
|
|
prototypeId?: UUID;
|
|
unitNumber: string;
|
|
lotNumber?: string;
|
|
block?: string;
|
|
|
|
location?: Location;
|
|
address?: string;
|
|
|
|
// Construction Status
|
|
status: HousingUnitStatus;
|
|
progressPercentage: Percentage;
|
|
|
|
// Timeline
|
|
constructionStartDate?: Date;
|
|
constructionEndDate?: Date;
|
|
deliveryDate?: Date;
|
|
|
|
// Sales (if sold)
|
|
buyerName?: string;
|
|
buyerContact?: string;
|
|
salePrice?: Money;
|
|
saleDate?: Date;
|
|
}
|
|
|
|
enum HousingUnitStatus {
|
|
LAND = 'land',
|
|
FOUNDATION = 'foundation',
|
|
STRUCTURE = 'structure',
|
|
FINISHING = 'finishing',
|
|
COMPLETED = 'completed',
|
|
DELIVERED = 'delivered'
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 2. Budget Aggregate
|
|
|
|
```
|
|
Budget (Aggregate Root)
|
|
|
|
|
+-- BudgetPartida (Entity)
|
|
| +-- BudgetConcept (Entity)
|
|
| +-- APUItem (Entity)
|
|
|
|
|
+-- MaterialsExplosion (Entity)
|
|
+-- BudgetVersion (Value Object)
|
|
```
|
|
|
|
#### Budget (Root)
|
|
```typescript
|
|
interface Budget {
|
|
id: UUID;
|
|
tenantId: UUID;
|
|
projectId?: UUID;
|
|
prototypeId?: UUID;
|
|
|
|
code: string;
|
|
name: string;
|
|
description?: string;
|
|
version: number;
|
|
|
|
// Type
|
|
isBase: boolean; // Base budget (prototype) vs Executive (project)
|
|
|
|
// Totals (calculated)
|
|
totalDirectCost: Money;
|
|
totalIndirectCost: Money;
|
|
totalCost: Money;
|
|
|
|
// Indirect costs
|
|
indirectPercentage: Percentage;
|
|
profitPercentage: Percentage;
|
|
|
|
// Status
|
|
status: BudgetStatus;
|
|
approvedAt?: Timestamp;
|
|
approvedBy?: UUID;
|
|
|
|
// Relationships
|
|
partidas: BudgetPartida[];
|
|
}
|
|
|
|
enum BudgetStatus {
|
|
DRAFT = 'draft',
|
|
APPROVED = 'approved',
|
|
ACTIVE = 'active',
|
|
CLOSED = 'closed'
|
|
}
|
|
```
|
|
|
|
#### BudgetPartida (Entity)
|
|
```typescript
|
|
interface BudgetPartida {
|
|
id: UUID;
|
|
budgetId: UUID;
|
|
parentId?: UUID;
|
|
|
|
code: string;
|
|
name: string;
|
|
description?: string;
|
|
|
|
sortOrder: number;
|
|
level: number;
|
|
|
|
// Calculated
|
|
totalCost: Money;
|
|
|
|
concepts: BudgetConcept[];
|
|
children: BudgetPartida[];
|
|
}
|
|
```
|
|
|
|
#### BudgetConcept (Entity)
|
|
```typescript
|
|
interface BudgetConcept {
|
|
id: UUID;
|
|
partidaId: UUID;
|
|
|
|
code: string;
|
|
name: string;
|
|
description?: string;
|
|
|
|
// Unit and quantity
|
|
unitId?: UUID;
|
|
unitCode: string;
|
|
quantity: number;
|
|
|
|
// Pricing
|
|
unitPrice: Money;
|
|
totalPrice: Money; // quantity * unitPrice
|
|
|
|
// Type
|
|
conceptType: ConceptType;
|
|
hasAPU: boolean;
|
|
|
|
sortOrder: number;
|
|
|
|
apuItems: APUItem[];
|
|
}
|
|
|
|
enum ConceptType {
|
|
MATERIAL = 'material',
|
|
LABOR = 'labor',
|
|
EQUIPMENT = 'equipment',
|
|
SUBCONTRACT = 'subcontract',
|
|
INDIRECT = 'indirect'
|
|
}
|
|
```
|
|
|
|
#### APUItem (Entity)
|
|
```typescript
|
|
interface APUItem {
|
|
id: UUID;
|
|
conceptId: UUID;
|
|
|
|
itemType: ConceptType;
|
|
|
|
// Product reference
|
|
productId?: UUID;
|
|
productCode: string;
|
|
productName: string;
|
|
|
|
// Unit and quantity
|
|
unitCode: string;
|
|
quantity: number;
|
|
|
|
// Cost
|
|
unitCost: Money;
|
|
totalCost: Money; // quantity * unitCost
|
|
|
|
// Yield (for labor)
|
|
yieldFactor: number;
|
|
|
|
sortOrder: number;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 3. Schedule Aggregate
|
|
|
|
```
|
|
Schedule (Aggregate Root)
|
|
|
|
|
+-- ScheduleItem (Entity)
|
|
| +-- Predecessor (Value Object)
|
|
|
|
|
+-- Milestone (Value Object)
|
|
```
|
|
|
|
#### Schedule (Root)
|
|
```typescript
|
|
interface Schedule {
|
|
id: UUID;
|
|
tenantId: UUID;
|
|
projectId: UUID;
|
|
|
|
name: string;
|
|
description?: string;
|
|
version: number;
|
|
|
|
startDate: Date;
|
|
endDate: Date;
|
|
|
|
isActive: boolean;
|
|
|
|
items: ScheduleItem[];
|
|
}
|
|
```
|
|
|
|
#### ScheduleItem (Entity)
|
|
```typescript
|
|
interface ScheduleItem {
|
|
id: UUID;
|
|
scheduleId: UUID;
|
|
parentId?: UUID;
|
|
conceptId?: UUID;
|
|
|
|
code: string;
|
|
name: string;
|
|
|
|
// Planned dates
|
|
plannedStart: Date;
|
|
plannedEnd: Date;
|
|
durationDays: number;
|
|
|
|
// Actual dates
|
|
actualStart?: Date;
|
|
actualEnd?: Date;
|
|
|
|
// Progress
|
|
plannedProgress: Percentage;
|
|
actualProgress: Percentage;
|
|
|
|
// Dependencies (WBS)
|
|
predecessors: UUID[];
|
|
|
|
sortOrder: number;
|
|
level: number;
|
|
|
|
children: ScheduleItem[];
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
### 4. Progress Aggregate
|
|
|
|
```
|
|
ProgressRecord (Aggregate Root)
|
|
|
|
|
+-- ProgressPhoto (Entity)
|
|
|
|
|
+-- Approval (Value Object)
|
|
|
|
LogbookEntry (Aggregate Root)
|
|
|
|
|
+-- EntryPhoto (Entity)
|
|
|
|
Estimation (Aggregate Root)
|
|
|
|
|
+-- EstimationLine (Entity)
|
|
```
|
|
|
|
#### ProgressRecord (Entity)
|
|
```typescript
|
|
interface ProgressRecord {
|
|
id: UUID;
|
|
tenantId: UUID;
|
|
projectId: UUID;
|
|
housingUnitId?: UUID;
|
|
conceptId?: UUID;
|
|
scheduleItemId?: UUID;
|
|
|
|
recordDate: Date;
|
|
|
|
progressType: ProgressType;
|
|
|
|
// Values
|
|
previousProgress: number;
|
|
currentProgress: number;
|
|
progressIncrement: number;
|
|
|
|
// For quantity type
|
|
quantityExecuted?: number;
|
|
unitCode?: string;
|
|
|
|
notes?: string;
|
|
|
|
// Approval
|
|
approvedAt?: Timestamp;
|
|
approvedBy?: UUID;
|
|
|
|
photos: ProgressPhoto[];
|
|
}
|
|
|
|
enum ProgressType {
|
|
QUANTITY = 'quantity',
|
|
PERCENTAGE = 'percentage'
|
|
}
|
|
```
|
|
|
|
#### Estimation (Entity)
|
|
```typescript
|
|
interface Estimation {
|
|
id: UUID;
|
|
tenantId: UUID;
|
|
projectId: UUID;
|
|
|
|
estimationNumber: string;
|
|
periodStart: Date;
|
|
periodEnd: Date;
|
|
|
|
// Amounts
|
|
previousAccumulated: Money;
|
|
currentPeriod: Money;
|
|
totalAccumulated: Money;
|
|
|
|
// Deductions
|
|
advanceAmortization: Money;
|
|
retentions: Money;
|
|
otherDeductions: Money;
|
|
|
|
netAmount: Money;
|
|
|
|
status: EstimationStatus;
|
|
|
|
// Workflow
|
|
submittedAt?: Timestamp;
|
|
submittedBy?: UUID;
|
|
approvedAt?: Timestamp;
|
|
approvedBy?: UUID;
|
|
|
|
lines: EstimationLine[];
|
|
}
|
|
|
|
enum EstimationStatus {
|
|
DRAFT = 'draft',
|
|
SUBMITTED = 'submitted',
|
|
APPROVED = 'approved',
|
|
REJECTED = 'rejected',
|
|
PAID = 'paid'
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Value Objects
|
|
|
|
### Location
|
|
```typescript
|
|
interface Location {
|
|
latitude: number;
|
|
longitude: number;
|
|
altitude?: number;
|
|
}
|
|
```
|
|
|
|
### Money
|
|
```typescript
|
|
interface Money {
|
|
amount: number;
|
|
currency: string; // Default: MXN
|
|
}
|
|
```
|
|
|
|
### Percentage
|
|
```typescript
|
|
interface Percentage {
|
|
value: number; // 0-100
|
|
}
|
|
```
|
|
|
|
### Period
|
|
```typescript
|
|
interface Period {
|
|
startDate: Date;
|
|
endDate: Date;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Domain Events
|
|
|
|
### Project Lifecycle
|
|
```typescript
|
|
// When a new project is created
|
|
interface ProjectCreated {
|
|
projectId: UUID;
|
|
code: string;
|
|
name: string;
|
|
tenantId: UUID;
|
|
timestamp: Timestamp;
|
|
}
|
|
|
|
// When project status changes
|
|
interface ProjectStatusChanged {
|
|
projectId: UUID;
|
|
previousStatus: ProjectStatus;
|
|
newStatus: ProjectStatus;
|
|
changedBy: UUID;
|
|
timestamp: Timestamp;
|
|
}
|
|
```
|
|
|
|
### Progress Events
|
|
```typescript
|
|
// When progress is recorded
|
|
interface ProgressRecorded {
|
|
progressRecordId: UUID;
|
|
projectId: UUID;
|
|
housingUnitId?: UUID;
|
|
conceptId?: UUID;
|
|
progressIncrement: number;
|
|
recordedBy: UUID;
|
|
timestamp: Timestamp;
|
|
}
|
|
|
|
// When estimation is approved
|
|
interface EstimationApproved {
|
|
estimationId: UUID;
|
|
projectId: UUID;
|
|
netAmount: Money;
|
|
approvedBy: UUID;
|
|
timestamp: Timestamp;
|
|
}
|
|
```
|
|
|
|
### Budget Events
|
|
```typescript
|
|
// When budget is approved
|
|
interface BudgetApproved {
|
|
budgetId: UUID;
|
|
projectId: UUID;
|
|
totalCost: Money;
|
|
approvedBy: UUID;
|
|
timestamp: Timestamp;
|
|
}
|
|
|
|
// When materials are exploded
|
|
interface MaterialsExploded {
|
|
budgetId: UUID;
|
|
materialsCount: number;
|
|
totalCost: Money;
|
|
timestamp: Timestamp;
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Business Rules
|
|
|
|
### Project Rules
|
|
1. Un proyecto debe tener al menos un presupuesto antes de iniciar
|
|
2. El status solo puede avanzar en orden: planning -> in_progress -> completed
|
|
3. Solo se puede pausar un proyecto en status in_progress
|
|
4. El progreso de proyecto se calcula como promedio ponderado de viviendas
|
|
|
|
### Budget Rules
|
|
1. Una poliza debe estar balanceada (total_debit = total_credit)
|
|
2. El presupuesto base (prototipo) no puede tener proyecto_id
|
|
3. El presupuesto ejecutivo debe derivar de un base
|
|
4. Los APU deben sumar al precio unitario del concepto
|
|
|
|
### Progress Rules
|
|
1. El avance no puede superar 100%
|
|
2. El avance actual debe ser >= avance anterior
|
|
3. Las estimaciones requieren avance registrado
|
|
4. Las fotos son obligatorias para ciertos conceptos
|
|
|
|
---
|
|
|
|
## Invariantes
|
|
|
|
1. `Project.progressPercentage = AVG(HousingUnit.progressPercentage)`
|
|
2. `Budget.totalCost = SUM(Partida.totalCost) * (1 + indirect%) * (1 + profit%)`
|
|
3. `Partida.totalCost = SUM(Concept.totalPrice)`
|
|
4. `Concept.unitPrice = SUM(APUItem.totalCost) / Concept.quantity`
|
|
5. `Estimation.totalAccumulated = previousAccumulated + currentPeriod`
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [DDL-SPEC-construction.md](../database-design/schemas/DDL-SPEC-construction.md)
|
|
- [EPIC-MAI-002](../../08-epicas/EPIC-MAI-002-proyectos.md)
|
|
- [EPIC-MAI-003](../../08-epicas/EPIC-MAI-003-presupuestos.md)
|
|
- [EPIC-MAI-005](../../08-epicas/EPIC-MAI-005-control-obra.md)
|
|
|
|
---
|
|
|
|
*Ultima actualizacion: 2025-12-05*
|