12 KiB
12 KiB
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
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
// 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
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
// 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
// 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
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
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
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
-- 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
@Injectable()
export class ProgressService {
constructor(
@InjectRepository(ProgressEntryEntity)
private progressRepo: Repository<ProgressEntryEntity>,
@InjectRepository(UnitEntity)
private unitRepo: Repository<UnitEntity>,
private eventEmitter: EventEmitter2,
) {}
async registerProgress(
dto: RegisterProgressDto,
tenantId: string,
userId: string
): Promise<ProgressEntryEntity> {
// 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<void> {
// 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
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