erp-construccion/docs/04-modelado/domain-models/PROJECT-CONTEXT.md

11 KiB

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)

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)

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)

interface Section {
  id: UUID;
  developmentId: UUID;
  code: string;
  name: string;
  boundary?: Polygon;
  areaM2?: number;

  totalUnits: number;
  completedUnits: number;

  housingUnits: HousingUnit[];
}

HousingUnit (Entity)

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)

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)

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)

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)

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)

interface Schedule {
  id: UUID;
  tenantId: UUID;
  projectId: UUID;

  name: string;
  description?: string;
  version: number;

  startDate: Date;
  endDate: Date;

  isActive: boolean;

  items: ScheduleItem[];
}

ScheduleItem (Entity)

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)

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)

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

interface Location {
  latitude: number;
  longitude: number;
  altitude?: number;
}

Money

interface Money {
  amount: number;
  currency: string;  // Default: MXN
}

Percentage

interface Percentage {
  value: number;  // 0-100
}

Period

interface Period {
  startDate: Date;
  endDate: Date;
}

Domain Events

Project Lifecycle

// 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

// 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

// 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


Ultima actualizacion: 2025-12-05