# Directiva: Estimaciones y Facturación ## Metadatos | Campo | Valor | |-------|-------| | **ID** | DIR-CONST-003 | | **Versión** | 1.0.0 | | **Ámbito** | Vertical Construcción | | **Módulo** | MAI-008 | | **Estado** | Activa | ## Propósito Define las reglas y patrones para el proceso de estimaciones de obra, aplicación de anticipos, retenciones y generación de documentos de cobro. ## Alcance Esta directiva aplica a: - Estimaciones a clientes - Estimaciones a subcontratistas - Anticipos y amortizaciones - Retenciones (fondo de garantía, IMSS, etc.) - Generación de reportes y documentos ## Tipos de Estimación ```yaml tipos: CLIENTE: descripcion: Cobro al cliente/desarrollador flujo: Contratista → Cliente SUBCONTRATISTA: descripcion: Pago a subcontratistas flujo: Subcontratista → Contratista DESTAJO: descripcion: Pago a cuadrillas por obra ejecutada flujo: Trabajadores → Contratista ``` ## Reglas Obligatorias ### 1. Estructura de Estimación ```typescript interface EstimationEntity { id: string; tenantId: string; projectId: string; // Identificación number: number; // Número consecutivo code: string; // EST-{PROJ}-{NNN} type: EstimationType; // CLIENTE | SUBCONTRATISTA | DESTAJO period: EstimationPeriod; // Período de corte // Relaciones contractId: string | null; // Contrato origen subcontractId: string | null; // Subcontrato (si aplica) // Fechas cutoffDate: Date; // Fecha de corte createdAt: Date; submittedAt: Date | null; approvedAt: Date | null; // Estado status: EstimationStatus; // Montos (todos calculados) grossAmount: number; // Importe bruto previousAmount: number; // Estimado anteriormente currentAmount: number; // Este período // Deducciones advanceAmortization: number; // Amortización de anticipo retentionGuarantee: number; // Fondo de garantía (5-10%) retentionImss: number; // Retención IMSS (5%) retentionIsr: number; // Retención ISR (1.25%) otherDeductions: number; // Otras deducciones // Neto netAmount: number; // Importe neto a pagar // IVA subtotal: number; iva: number; // IVA 16% total: number; // Total con IVA // Aprobaciones preparedBy: string; reviewedBy: string | null; approvedBy: string | null; } type EstimationType = 'CLIENTE' | 'SUBCONTRATISTA' | 'DESTAJO'; type EstimationStatus = | 'BORRADOR' | 'EN_REVISION' | 'OBSERVACIONES' | 'APROBADA' | 'RECHAZADA' | 'FACTURADA' | 'PAGADA' | 'CANCELADA'; ``` ### 2. Detalle de Estimación ```typescript interface EstimationDetail { id: string; tenantId: string; estimationId: string; // Concepto budgetItemId: string; // Concepto de presupuesto budgetItemCode: string; // Código (01.03.015) description: string; unitOfMeasure: string; // Cantidades contractedQuantity: number; // Cantidad contratada previousQuantity: number; // Estimado anterior currentQuantity: number; // Este período accumulatedQuantity: number; // Acumulado remainingQuantity: number; // Pendiente // Precios unitPrice: number; previousAmount: number; currentAmount: number; accumulatedAmount: number; // % de avance progressPercentage: number; } ``` ### 3. Anticipos y Amortización ```yaml anticipo: porcentaje_maximo: 30% amortizacion: metodo: PROPORCIONAL formula: "(monto_estimacion / monto_contrato) * monto_anticipo" reglas: - Anticipo se registra al inicio del contrato - Amortización automática en cada estimación - No puede exceder el anticipo otorgado - Saldo de anticipo visible en cada estimación ``` ```typescript interface AdvancePayment { id: string; tenantId: string; contractId: string; // Monto amount: number; percentage: number; // % sobre contrato // Estado paidDate: Date; status: AdvanceStatus; // Amortización amortizedAmount: number; pendingAmount: number; } // Cálculo de amortización function calculateAmortization( estimationAmount: number, contractAmount: number, advanceAmount: number, alreadyAmortized: number ): number { const proportionalAmortization = (estimationAmount / contractAmount) * advanceAmount; const pendingAdvance = advanceAmount - alreadyAmortized; // No puede exceder el pendiente return Math.min(proportionalAmortization, pendingAdvance); } ``` ### 4. Retenciones ```yaml retenciones: fondo_garantia: porcentaje: 5% - 10% descripcion: Garantía de vicios ocultos liberacion: 6-12 meses después de finiquito imss: porcentaje: 5% descripcion: Retención IMSS subcontratistas aplica_a: SUBCONTRATISTA isr: porcentaje: 1.25% descripcion: Retención ISR por servicios aplica_a: SUBCONTRATISTA otros: - Penalizaciones - Daños a terceros - Cargos por atraso ``` ```typescript interface RetentionConfig { tenantId: string; contractId: string; // Fondo de garantía guaranteeFundPercentage: number; guaranteeFundAccumulated: number; guaranteeFundReleaseDate: Date | null; // IMSS (solo subcontratos) imssRetentionPercentage: number; // ISR (solo subcontratos) isrRetentionPercentage: number; // Otros otherRetentions: OtherRetention[]; } // Cálculo de retenciones function calculateRetentions( amount: number, config: RetentionConfig, type: EstimationType ): RetentionBreakdown { const guaranteeFund = amount * (config.guaranteeFundPercentage / 100); let imss = 0; let isr = 0; if (type === 'SUBCONTRATISTA') { imss = amount * (config.imssRetentionPercentage / 100); isr = amount * (config.isrRetentionPercentage / 100); } return { guaranteeFund, imss, isr, total: guaranteeFund + imss + isr }; } ``` ### 5. Workflow de Aprobación ```yaml workflow: estados: BORRADOR: acciones: [editar, eliminar, enviar_revision] permisos: [preparador] EN_REVISION: acciones: [aprobar, rechazar, devolver_observaciones] permisos: [revisor, supervisor] OBSERVACIONES: acciones: [editar, enviar_revision] permisos: [preparador] APROBADA: acciones: [generar_factura, cancelar] permisos: [autorizador] FACTURADA: acciones: [registrar_pago] permisos: [tesoreria] PAGADA: acciones: [] # Estado final permisos: [] niveles_aprobacion: - nivel: 1 monto_maximo: 100000 aprobador: supervisor_obra - nivel: 2 monto_maximo: 500000 aprobador: gerente_proyecto - nivel: 3 monto_maximo: null # Sin límite aprobador: director_operaciones ``` ## Patrones de Implementación ### Schema de Base de Datos ```sql -- Schema de estimaciones CREATE SCHEMA IF NOT EXISTS financial_management; -- Estimaciones CREATE TABLE financial_management.estimations ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL, project_id UUID NOT NULL, -- Identificación number INTEGER NOT NULL, code VARCHAR(50) NOT NULL, type VARCHAR(20) NOT NULL, -- Relaciones contract_id UUID, subcontract_id UUID, -- Período period_start DATE NOT NULL, period_end DATE NOT NULL, cutoff_date DATE NOT NULL, -- Estado status VARCHAR(30) NOT NULL DEFAULT 'BORRADOR', -- Montos gross_amount DECIMAL(15,2) DEFAULT 0, previous_amount DECIMAL(15,2) DEFAULT 0, current_amount DECIMAL(15,2) DEFAULT 0, -- Deducciones advance_amortization DECIMAL(15,2) DEFAULT 0, retention_guarantee DECIMAL(15,2) DEFAULT 0, retention_imss DECIMAL(15,2) DEFAULT 0, retention_isr DECIMAL(15,2) DEFAULT 0, other_deductions DECIMAL(15,2) DEFAULT 0, -- Totales net_amount DECIMAL(15,2) DEFAULT 0, subtotal DECIMAL(15,2) DEFAULT 0, iva DECIMAL(15,2) DEFAULT 0, total DECIMAL(15,2) DEFAULT 0, -- Fechas de flujo submitted_at TIMESTAMPTZ, approved_at TIMESTAMPTZ, invoiced_at TIMESTAMPTZ, paid_at TIMESTAMPTZ, -- Responsables prepared_by UUID NOT NULL, reviewed_by UUID, approved_by UUID, -- Auditoría created_at TIMESTAMPTZ DEFAULT NOW(), updated_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(tenant_id, project_id, number, type) ); -- Detalle de estimación CREATE TABLE financial_management.estimation_details ( id UUID PRIMARY KEY DEFAULT uuid_generate_v4(), tenant_id UUID NOT NULL, estimation_id UUID NOT NULL REFERENCES financial_management.estimations(id), -- Concepto budget_item_id UUID NOT NULL, budget_item_code VARCHAR(50) NOT NULL, description TEXT NOT NULL, unit_of_measure VARCHAR(20), -- Cantidades contracted_quantity DECIMAL(15,4) DEFAULT 0, previous_quantity DECIMAL(15,4) DEFAULT 0, current_quantity DECIMAL(15,4) DEFAULT 0, accumulated_quantity DECIMAL(15,4) DEFAULT 0, -- Precios unit_price DECIMAL(15,4) NOT NULL, previous_amount DECIMAL(15,2) DEFAULT 0, current_amount DECIMAL(15,2) DEFAULT 0, accumulated_amount DECIMAL(15,2) DEFAULT 0, created_at TIMESTAMPTZ DEFAULT NOW() ); -- Índices CREATE INDEX idx_estimations_tenant ON financial_management.estimations(tenant_id); CREATE INDEX idx_estimations_project ON financial_management.estimations(project_id); CREATE INDEX idx_estimations_status ON financial_management.estimations(status); CREATE INDEX idx_estimation_details_estimation ON financial_management.estimation_details(estimation_id); -- RLS ALTER TABLE financial_management.estimations ENABLE ROW LEVEL SECURITY; ALTER TABLE financial_management.estimation_details ENABLE ROW LEVEL SECURITY; CREATE POLICY "tenant_isolation" ON financial_management.estimations USING (tenant_id = current_setting('app.current_tenant_id')::uuid); CREATE POLICY "tenant_isolation" ON financial_management.estimation_details USING (tenant_id = current_setting('app.current_tenant_id')::uuid); ``` ### Servicio de Estimaciones ```typescript @Injectable() export class EstimationService { constructor( @InjectRepository(EstimationEntity) private estimationRepo: Repository, @InjectRepository(EstimationDetailEntity) private detailRepo: Repository, private advanceService: AdvancePaymentService, private retentionService: RetentionService, ) {} async createEstimation( dto: CreateEstimationDto, tenantId: string, userId: string ): Promise { // Generar número consecutivo const lastNumber = await this.getLastEstimationNumber( dto.projectId, dto.type, tenantId ); // Calcular montos const amounts = await this.calculateAmounts(dto, tenantId); // Calcular deducciones const deductions = await this.calculateDeductions( amounts.currentAmount, dto.contractId, dto.type, tenantId ); const estimation = this.estimationRepo.create({ ...dto, tenantId, number: lastNumber + 1, code: this.generateCode(dto.projectId, lastNumber + 1), ...amounts, ...deductions, netAmount: amounts.currentAmount - deductions.totalDeductions, subtotal: amounts.currentAmount - deductions.totalDeductions, iva: (amounts.currentAmount - deductions.totalDeductions) * 0.16, total: (amounts.currentAmount - deductions.totalDeductions) * 1.16, preparedBy: userId, status: 'BORRADOR' }); return this.estimationRepo.save(estimation); } private async calculateDeductions( amount: number, contractId: string, type: EstimationType, tenantId: string ): Promise { // Obtener configuración de retenciones const config = await this.retentionService.getConfig(contractId, tenantId); // Calcular amortización de anticipo const amortization = await this.advanceService.calculateAmortization( amount, contractId, tenantId ); // Calcular retenciones const retentions = calculateRetentions(amount, config, type); return { advanceAmortization: amortization, retentionGuarantee: retentions.guaranteeFund, retentionImss: retentions.imss, retentionIsr: retentions.isr, otherDeductions: 0, totalDeductions: amortization + retentions.total }; } async submitForReview( id: string, tenantId: string, userId: string ): Promise { const estimation = await this.findOne(id, tenantId); if (estimation.status !== 'BORRADOR' && estimation.status !== 'OBSERVACIONES') { throw new BadRequestException('Solo borradores pueden enviarse a revisión'); } estimation.status = 'EN_REVISION'; estimation.submittedAt = new Date(); return this.estimationRepo.save(estimation); } } ``` ## Documentos Generados ```yaml documentos: estimacion_pdf: contenido: - Datos del proyecto y contrato - Período de estimación - Tabla de conceptos con cantidades - Resumen de montos - Desglose de deducciones - Firmas de elaboró, revisó, autorizó caratula: contenido: - Resumen ejecutivo - Comparativo vs presupuesto - Gráfica de avance reporte_acumulado: contenido: - Histórico de estimaciones - Avance acumulado por concepto - Proyección de cierre ``` ## Validaciones Pre-Commit - [ ] Cálculo correcto de amortización de anticipos - [ ] Retenciones calculadas según configuración - [ ] Workflow de aprobación implementado - [ ] Números consecutivos sin duplicados - [ ] RLS habilitado en todas las tablas - [ ] Validación de cantidades vs presupuesto ## Referencias - Documentación: `/docs/01-fase-alcance-inicial/MAI-008-estimaciones-facturacion/` - Especificaciones: `/docs/01-fase-alcance-inicial/MAI-008-estimaciones-facturacion/especificaciones/` - Core directivas: `../../erp-core/orchestration/directivas/` --- *Directiva específica de Vertical Construcción*