# Directiva: Control de Obra ## Metadatos | Campo | Valor | |-------|-------| | **ID** | DIR-CONST-002 | | **Versión** | 1.0.0 | | **Ámbito** | Vertical Construcción | | **Módulos** | MAI-002, MAI-005, MAI-012 | | **Estado** | Activa | ## Propósito Define las reglas y patrones para el control de obra, gestión de proyectos de construcción, avances y recursos. ## Alcance Esta directiva aplica a: - Estructura jerárquica de proyectos - Control de avances de obra - Gestión de recursos y materiales - Presupuestos y costos - Subcontratos y contratos - Estimaciones de obra ## Estructura Jerárquica de Proyectos ```yaml jerarquia: nivel_1: nombre: Proyecto/Desarrollo entidad: Project ejemplo: "Fraccionamiento Los Álamos" nivel_2: nombre: Etapa/Fase entidad: Phase ejemplo: "Etapa 3 - 120 viviendas" nivel_3: nombre: Manzana/Torre entidad: Block ejemplo: "Manzana 15" nivel_4: nombre: Lote/Vivienda/Departamento entidad: Unit ejemplo: "Lote 15-A Casa Modelo Roma" tipos_proyecto: HORIZONTAL: niveles: [Desarrollo, Etapa, Manzana, Lote] ejemplo: Fraccionamiento de casas VERTICAL: niveles: [Desarrollo, Torre, Piso, Departamento] ejemplo: Edificio de departamentos MIXTO: niveles: [Desarrollo, Zona, Bloque, Unidad] ejemplo: Centro comercial con oficinas ``` ## Reglas Obligatorias ### 1. Entidades de Proyecto ```typescript // Proyecto base interface ProjectEntity { id: string; tenantId: string; // Identificación code: string; // Código único: PROJ-2025-001 name: string; projectType: ProjectType; // HORIZONTAL | VERTICAL | MIXTO // Ubicación address: string; city: string; state: string; coordinates: GeoPoint | null; // Estado y fechas status: ProjectStatus; plannedStartDate: Date; plannedEndDate: Date; actualStartDate: Date | null; actualEndDate: Date | null; // Métricas totalUnits: number; completedUnits: number; progressPercentage: number; // Presupuesto budgetAmount: number; actualCost: number; costVariance: number; // Auditoría createdAt: Date; updatedAt: Date; createdBy: string; updatedBy: string; } type ProjectStatus = | 'PLANEACION' | 'EN_CONSTRUCCION' | 'PAUSADO' | 'FINALIZADO' | 'CANCELADO'; type ProjectType = 'HORIZONTAL' | 'VERTICAL' | 'MIXTO'; ``` ### 2. Control de Avances ```yaml avances: tipos: - FISICO: # Porcentaje de obra ejecutada - FINANCIERO: # Porcentaje de presupuesto ejercido - PROGRAMATICO: # Cumplimiento del programa granularidad: - Por unidad (casa/depto) - Por partida de presupuesto - Por concepto de obra - Por subcontrato frecuencia_registro: minima: Semanal recomendada: Diaria para conceptos críticos ``` ```typescript // Registro de avance interface ProgressEntry { id: string; tenantId: string; projectId: string; unitId: string; // Avance date: Date; conceptId: string; // Concepto de obra previousProgress: number; // % anterior currentProgress: number; // % actual incrementProgress: number; // Incremento // Cantidades quantity: number; unitOfMeasure: string; // Validación registeredBy: string; validatedBy: string | null; validatedAt: Date | null; // Evidencia photos: string[]; // URLs de fotos notes: string; } ``` ### 3. Gestión de Recursos ```typescript // Asignación de recursos interface ResourceAssignment { id: string; tenantId: string; projectId: string; // Recurso resourceType: ResourceType; resourceId: string; // Asignación assignedFrom: Date; assignedTo: Date; quantity: number; // Costo dailyRate: number; estimatedCost: number; actualCost: number; } type ResourceType = | 'MANO_OBRA' | 'EQUIPO' | 'MAQUINARIA' | 'MATERIAL'; ``` ### 4. Presupuesto y Costos ```yaml estructura_presupuesto: niveles: - Partida: # Ej: "01 - Preliminares" - Subpartida: # Ej: "01.01 - Limpieza" - Concepto: # Ej: "01.01.001 - Trazo y nivelación" tipos_costo: - MANO_OBRA: directa: true indirecta: true - MATERIALES: locales: true importados: true - EQUIPOS: propios: true rentados: true - SUBCONTRATOS: mano_obra: true paquete: true - INDIRECTOS: administracion: true financiamiento: true ``` ```typescript interface BudgetItem { id: string; tenantId: string; projectId: string; // Jerarquía partitionCode: string; // "01.01.001" parentId: string | null; level: number; // 1, 2, 3 // Concepto description: string; unitOfMeasure: string; // Cantidades budgetedQuantity: number; executedQuantity: number; // Precios unitPrice: number; budgetedAmount: number; executedAmount: number; // Variación quantityVariance: number; amountVariance: number; variancePercentage: number; } ``` ### 5. Subcontratos ```typescript interface SubcontractEntity { id: string; tenantId: string; projectId: string; // Contratista contractorId: string; contractorName: string; // Contrato contractNumber: string; contractType: SubcontractType; description: string; // Alcance budgetItems: string[]; // IDs de conceptos // Montos contractAmount: number; amortizations: number; // Anticipos amortizados deductions: number; // Deducciones retentions: number; // Retenciones netPayable: number; // Estado status: SubcontractStatus; startDate: Date; endDate: Date; // Estimaciones estimationsCount: number; estimatedAmount: number; paidAmount: number; } type SubcontractType = | 'MANO_OBRA' // Solo mano de obra | 'SUMINISTRO' // Solo materiales | 'PAQUETE' // Todo incluido | 'PRECIO_UNITARIO' // Por unidad ejecutada | 'PRECIO_ALZADO'; // Monto fijo type SubcontractStatus = | 'BORRADOR' | 'ACTIVO' | 'PAUSADO' | 'FINIQUITADO' | 'CANCELADO'; ``` ## Patrones de Implementación ### Schema de Base de Datos ```sql -- Schema de proyectos CREATE SCHEMA IF NOT EXISTS project_management; -- Proyectos principales CREATE TABLE project_management.projects ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL, code VARCHAR(50) NOT NULL, name VARCHAR(200) NOT NULL, project_type VARCHAR(20) NOT NULL, status VARCHAR(30) NOT NULL DEFAULT 'PLANEACION', -- Ubicación address TEXT, city VARCHAR(100), state VARCHAR(100), coordinates POINT, -- Fechas planned_start_date DATE, planned_end_date DATE, actual_start_date DATE, actual_end_date DATE, -- Métricas total_units INTEGER DEFAULT 0, completed_units INTEGER DEFAULT 0, progress_percentage DECIMAL(5,2) DEFAULT 0, -- Presupuesto budget_amount DECIMAL(15,2) DEFAULT 0, actual_cost DECIMAL(15,2) DEFAULT 0, -- Auditoría created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), created_by UUID, updated_by UUID, is_active BOOLEAN DEFAULT TRUE, UNIQUE(tenant_id, code) ); -- Índices obligatorios CREATE INDEX idx_projects_tenant_id ON project_management.projects(tenant_id); CREATE INDEX idx_projects_status ON project_management.projects(status); CREATE INDEX idx_projects_type ON project_management.projects(project_type); -- RLS ALTER TABLE project_management.projects ENABLE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON project_management.projects USING (tenant_id = current_setting('app.current_tenant_id')::uuid); -- Fases/Etapas CREATE TABLE project_management.phases ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL, project_id UUID NOT NULL REFERENCES project_management.projects(id), code VARCHAR(50) NOT NULL, name VARCHAR(200) NOT NULL, sequence INTEGER NOT NULL, status VARCHAR(30) NOT NULL DEFAULT 'PLANEACION', total_units INTEGER DEFAULT 0, completed_units INTEGER DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); -- Unidades (viviendas/departamentos) CREATE TABLE project_management.units ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL, project_id UUID NOT NULL REFERENCES project_management.projects(id), phase_id UUID REFERENCES project_management.phases(id), block_id UUID, code VARCHAR(50) NOT NULL, unit_type VARCHAR(50), -- Casa, Depto, Local prototype_id UUID, -- Modelo de vivienda status VARCHAR(30) NOT NULL DEFAULT 'PENDIENTE', progress_percentage DECIMAL(5,2) DEFAULT 0, -- Venta sale_status VARCHAR(30) DEFAULT 'DISPONIBLE', client_id UUID, created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW() ); ``` ### Servicio de Avances ```typescript @Injectable() export class ProgressService { constructor( @InjectRepository(ProgressEntryEntity) private progressRepo: Repository, @InjectRepository(UnitEntity) private unitRepo: Repository, private eventEmitter: EventEmitter2, ) {} async registerProgress( dto: RegisterProgressDto, tenantId: string, userId: string ): Promise { // Obtener avance anterior const lastProgress = await this.progressRepo.findOne({ where: { unitId: dto.unitId, conceptId: dto.conceptId, tenantId }, order: { date: 'DESC' } }); const previousProgress = lastProgress?.currentProgress ?? 0; // Validar que no retroceda if (dto.currentProgress < previousProgress) { throw new BadRequestException( 'El avance no puede ser menor al registrado anteriormente' ); } // Crear registro const entry = this.progressRepo.create({ ...dto, tenantId, previousProgress, incrementProgress: dto.currentProgress - previousProgress, registeredBy: userId, }); const saved = await this.progressRepo.save(entry); // Actualizar avance de unidad await this.updateUnitProgress(dto.unitId, tenantId); // Emitir evento this.eventEmitter.emit('progress.registered', { tenantId, projectId: dto.projectId, unitId: dto.unitId, progress: dto.currentProgress }); return saved; } private async updateUnitProgress( unitId: string, tenantId: string ): Promise { // Calcular promedio ponderado de avances por concepto const result = await this.progressRepo .createQueryBuilder('p') .select('AVG(p.current_progress)', 'avgProgress') .where('p.unit_id = :unitId', { unitId }) .andWhere('p.tenant_id = :tenantId', { tenantId }) .getRawOne(); await this.unitRepo.update( { id: unitId, tenantId }, { progressPercentage: result.avgProgress ?? 0 } ); } } ``` ## Nomenclatura de Códigos ```yaml proyectos: formato: "PROJ-{YYYY}-{NNN}" ejemplo: "PROJ-2025-001" fases: formato: "{PROJ_CODE}-E{NN}" ejemplo: "PROJ-2025-001-E03" unidades: horizontal: "M{MZ}-L{LT}" # M15-L07 vertical: "T{TW}-P{PO}-D{DP}" # T01-P05-D02 presupuesto: partida: "{NN}" # 01 subpartida: "{NN}.{NN}" # 01.03 concepto: "{NN}.{NN}.{NNN}" # 01.03.015 ``` ## Validaciones Pre-Commit - [ ] Jerarquía de proyecto correctamente implementada - [ ] Control de avances con validación de retroceso - [ ] Presupuesto estructurado por niveles - [ ] RLS habilitado en todas las tablas - [ ] Eventos emitidos en operaciones críticas - [ ] Códigos generados según nomenclatura ## Referencias - Documentación proyectos: `/docs/01-fase-alcance-inicial/MAI-002-proyectos-estructura/` - Documentación avances: `/docs/01-fase-alcance-inicial/MAI-005-control-obra/` - Core directivas: `../../erp-core/orchestration/directivas/` --- *Directiva específica de Vertical Construcción*