diff --git a/schemas/08-finance-schema-ddl.sql b/schemas/08-finance-schema-ddl.sql new file mode 100644 index 0000000..4e6abf0 --- /dev/null +++ b/schemas/08-finance-schema-ddl.sql @@ -0,0 +1,1235 @@ +-- ============================================================================ +-- 08-finance-schema-ddl.sql +-- Schema: finance +-- ERP Construccion - Modulo Finanzas y Controlling (MAE-014) +-- ============================================================================ +-- Descripcion: Gestion financiera completa incluyendo: +-- - Catalogo de cuentas contables (Chart of Accounts) +-- - Polizas contables (Journal Entries) +-- - Cuentas por pagar (Accounts Payable) +-- - Cuentas por cobrar (Accounts Receivable) +-- - Cuentas bancarias y movimientos +-- - Conciliacion bancaria +-- - Proyecciones de flujo de efectivo +-- ============================================================================ +-- Autor: Claude-Arquitecto-Orquestador +-- Fecha: 2026-01-25 +-- Version: 1.0.0 +-- ============================================================================ + +-- Crear schema si no existe +CREATE SCHEMA IF NOT EXISTS finance; + +-- ============================================================================ +-- ENUMS +-- ============================================================================ + +-- Tipo de cuenta contable +CREATE TYPE finance.account_type AS ENUM ( + 'asset', -- Activo + 'liability', -- Pasivo + 'equity', -- Capital + 'income', -- Ingreso + 'expense' -- Gasto +); + +-- Naturaleza de cuenta (deudora/acreedora) +CREATE TYPE finance.account_nature AS ENUM ( + 'debit', -- Deudora + 'credit' -- Acreedora +); + +-- Estado de cuenta contable +CREATE TYPE finance.account_status AS ENUM ( + 'active', -- Activa + 'inactive', -- Inactiva + 'blocked' -- Bloqueada +); + +-- Tipo de poliza contable +CREATE TYPE finance.entry_type AS ENUM ( + 'purchase', -- Compra + 'sale', -- Venta + 'payment', -- Pago + 'collection', -- Cobro + 'payroll', -- Nomina + 'adjustment', -- Ajuste + 'depreciation', -- Depreciacion + 'transfer', -- Traspaso + 'opening', -- Apertura + 'closing' -- Cierre +); + +-- Estado de poliza contable +CREATE TYPE finance.entry_status AS ENUM ( + 'draft', -- Borrador + 'pending_approval', -- Pendiente de aprobacion + 'approved', -- Aprobada + 'posted', -- Contabilizada + 'cancelled', -- Cancelada + 'reversed' -- Reversada +); + +-- Tipo de documento CxP +CREATE TYPE finance.ap_document_type AS ENUM ( + 'invoice', -- Factura + 'credit_note', -- Nota de credito + 'debit_note', -- Nota de debito + 'advance', -- Anticipo + 'retention' -- Retencion +); + +-- Estado de CxP +CREATE TYPE finance.ap_status AS ENUM ( + 'pending', -- Pendiente + 'partial', -- Parcialmente pagado + 'paid', -- Pagado + 'overdue', -- Vencido + 'cancelled', -- Cancelado + 'disputed' -- En disputa +); + +-- Tipo de documento CxC +CREATE TYPE finance.ar_document_type AS ENUM ( + 'invoice', -- Factura + 'credit_note', -- Nota de credito + 'debit_note', -- Nota de debito + 'advance', -- Anticipo + 'estimation' -- Estimacion +); + +-- Estado de CxC +CREATE TYPE finance.ar_status AS ENUM ( + 'pending', -- Pendiente + 'partial', -- Parcialmente cobrado + 'collected', -- Cobrado + 'overdue', -- Vencido + 'cancelled', -- Cancelado + 'written_off' -- Incobrable +); + +-- Tipo de cuenta bancaria +CREATE TYPE finance.bank_account_type AS ENUM ( + 'checking', -- Cuenta de cheques + 'savings', -- Cuenta de ahorro + 'investment', -- Inversion + 'credit_line', -- Linea de credito + 'other' -- Otro +); + +-- Estado de cuenta bancaria +CREATE TYPE finance.bank_account_status AS ENUM ( + 'active', -- Activa + 'inactive', -- Inactiva + 'blocked', -- Bloqueada + 'closed' -- Cerrada +); + +-- Tipo de movimiento bancario +CREATE TYPE finance.bank_movement_type AS ENUM ( + 'debit', -- Cargo + 'credit' -- Abono +); + +-- Estado de movimiento bancario +CREATE TYPE finance.bank_movement_status AS ENUM ( + 'pending', -- Pendiente + 'matched', -- Coincidente + 'reconciled', -- Conciliado + 'unreconciled', -- No conciliado + 'ignored' -- Ignorado +); + +-- Origen de movimiento bancario +CREATE TYPE finance.bank_movement_source AS ENUM ( + 'manual', -- Manual + 'import_file', -- Importacion de archivo + 'api', -- API bancaria + 'system' -- Sistema +); + +-- Metodo de pago +CREATE TYPE finance.payment_method AS ENUM ( + 'cash', -- Efectivo + 'check', -- Cheque + 'transfer', -- Transferencia + 'card', -- Tarjeta + 'compensation', -- Compensacion + 'other' -- Otro +); + +-- Estado de pago (CxP) +CREATE TYPE finance.payment_status AS ENUM ( + 'pending', -- Pendiente + 'processed', -- Procesado + 'reconciled', -- Conciliado + 'cancelled', -- Cancelado + 'returned' -- Devuelto +); + +-- Metodo de cobro +CREATE TYPE finance.collection_method AS ENUM ( + 'cash', -- Efectivo + 'check', -- Cheque + 'transfer', -- Transferencia + 'card', -- Tarjeta + 'compensation', -- Compensacion + 'other' -- Otro +); + +-- Estado de cobro (CxC) +CREATE TYPE finance.collection_status AS ENUM ( + 'pending', -- Pendiente + 'deposited', -- Depositado + 'reconciled', -- Conciliado + 'cancelled', -- Cancelado + 'returned' -- Devuelto +); + +-- Estado de conciliacion +CREATE TYPE finance.reconciliation_status AS ENUM ( + 'draft', -- Borrador + 'in_progress', -- En proceso + 'completed', -- Completada + 'approved', -- Aprobada + 'cancelled' -- Cancelada +); + +-- Tipo de flujo de efectivo +CREATE TYPE finance.cash_flow_type AS ENUM ( + 'projected', -- Proyectado + 'actual', -- Real + 'comparison' -- Comparativo +); + +-- Tipo de periodo de flujo +CREATE TYPE finance.cash_flow_period_type AS ENUM ( + 'daily', -- Diario + 'weekly', -- Semanal + 'monthly', -- Mensual + 'quarterly' -- Trimestral +); + +-- ============================================================================ +-- TABLAS +-- ============================================================================ + +-- ---------------------------------------------------------------------------- +-- 1. Catalogo de Cuentas Contables (Chart of Accounts) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.chart_of_accounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Codigo jerarquico de cuenta + code VARCHAR(50) NOT NULL, + name VARCHAR(255) NOT NULL, + description TEXT, + + -- Tipo y naturaleza + account_type finance.account_type NOT NULL, + nature finance.account_nature NOT NULL, + status finance.account_status NOT NULL DEFAULT 'active', + + -- Jerarquia + level INTEGER NOT NULL DEFAULT 1, + parent_id UUID REFERENCES finance.chart_of_accounts(id), + + -- Configuracion de imputacion + cost_center_required BOOLEAN NOT NULL DEFAULT FALSE, + project_required BOOLEAN NOT NULL DEFAULT FALSE, + allows_direct_posting BOOLEAN NOT NULL DEFAULT TRUE, + + -- Codigos de integracion con ERPs externos + sap_code VARCHAR(50), + contpaqi_code VARCHAR(50), + aspel_code VARCHAR(50), + + -- Saldos (actualizados periodicamente) + initial_balance DECIMAL(18,2) NOT NULL DEFAULT 0, + current_balance DECIMAL(18,2) NOT NULL DEFAULT 0, + balance_updated_at TIMESTAMPTZ, + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + + -- Constraints + CONSTRAINT uq_chart_of_accounts_tenant_code UNIQUE (tenant_id, code) +); + +-- Tabla de closure para jerarquia (TypeORM Tree closure-table) +CREATE TABLE finance.chart_of_accounts_closure ( + id_ancestor UUID NOT NULL REFERENCES finance.chart_of_accounts(id) ON DELETE CASCADE, + id_descendant UUID NOT NULL REFERENCES finance.chart_of_accounts(id) ON DELETE CASCADE, + PRIMARY KEY (id_ancestor, id_descendant) +); + +-- ---------------------------------------------------------------------------- +-- 2. Polizas Contables (Accounting Entries) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.accounting_entries ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Numero de poliza + entry_number VARCHAR(50) NOT NULL, + + -- Tipo y fecha + entry_type finance.entry_type NOT NULL, + entry_date DATE NOT NULL, + status finance.entry_status NOT NULL DEFAULT 'draft', + + -- Descripcion + description TEXT NOT NULL, + reference VARCHAR(255), + + -- Origen (modulo que genero la poliza) + source_module VARCHAR(50), + source_id UUID, + + -- Proyecto asociado + project_id UUID, + + -- Periodo contable + fiscal_year INTEGER NOT NULL, + fiscal_period INTEGER NOT NULL, + + -- Totales (calculados) + total_debit DECIMAL(18,2) NOT NULL DEFAULT 0, + total_credit DECIMAL(18,2) NOT NULL DEFAULT 0, + is_balanced BOOLEAN NOT NULL DEFAULT FALSE, + + -- Moneda + currency VARCHAR(3) NOT NULL DEFAULT 'MXN', + exchange_rate DECIMAL(12,6) NOT NULL DEFAULT 1, + + -- Aprobacion + approved_by_id UUID, + approved_at TIMESTAMPTZ, + + -- Contabilizacion + posted_at TIMESTAMPTZ, + posted_by_id UUID, + + -- Reversion + reversed_entry_id UUID REFERENCES finance.accounting_entries(id), + reversal_reason TEXT, + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + + -- Constraints + CONSTRAINT uq_accounting_entries_tenant_number UNIQUE (tenant_id, entry_number) +); + +-- ---------------------------------------------------------------------------- +-- 3. Lineas de Poliza Contable (Accounting Entry Lines) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.accounting_entry_lines ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Referencia a la poliza + entry_id UUID NOT NULL REFERENCES finance.accounting_entries(id) ON DELETE CASCADE, + + -- Numero de linea + line_number INTEGER NOT NULL, + + -- Cuenta contable + account_id UUID NOT NULL REFERENCES finance.chart_of_accounts(id), + account_code VARCHAR(50) NOT NULL, + + -- Descripcion de la linea + description TEXT NOT NULL, + + -- Montos + debit DECIMAL(18,2) NOT NULL DEFAULT 0, + credit DECIMAL(18,2) NOT NULL DEFAULT 0, + + -- Centro de costo (opcional) + cost_center_id UUID, + cost_center_code VARCHAR(50), + + -- Proyecto (opcional) + project_id UUID, + project_code VARCHAR(50), + + -- Tercero (proveedor/cliente) + partner_id UUID, + partner_name VARCHAR(255), + + -- Documento de referencia + document_type VARCHAR(50), + document_number VARCHAR(100), + + -- Moneda original (si es diferente) + original_currency VARCHAR(3), + original_amount DECIMAL(18,2), + + -- Metadatos + metadata JSONB, + + -- Auditoria + created_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- ---------------------------------------------------------------------------- +-- 4. Cuentas por Pagar (Accounts Payable) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.accounts_payable ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Numero de documento + document_number VARCHAR(100) NOT NULL, + document_type finance.ap_document_type NOT NULL DEFAULT 'invoice', + + -- Proveedor + supplier_id UUID NOT NULL, + supplier_name VARCHAR(255) NOT NULL, + supplier_rfc VARCHAR(13), + + -- Estado + status finance.ap_status NOT NULL DEFAULT 'pending', + + -- Fechas + invoice_date DATE NOT NULL, + received_date DATE, + due_date DATE NOT NULL, + payment_date DATE, + + -- Montos + subtotal DECIMAL(18,2) NOT NULL, + tax_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + retention_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + total_amount DECIMAL(18,2) NOT NULL, + paid_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + balance DECIMAL(18,2) NOT NULL, + + -- Moneda + currency VARCHAR(3) NOT NULL DEFAULT 'MXN', + exchange_rate DECIMAL(12,6) NOT NULL DEFAULT 1, + + -- Origen (orden de compra, contrato) + source_module VARCHAR(50), + source_id UUID, + purchase_order_number VARCHAR(50), + + -- Proyecto asociado + project_id UUID, + project_code VARCHAR(50), + + -- Centro de costo + cost_center_id UUID, + + -- Cuenta contable de contrapartida + expense_account_id UUID REFERENCES finance.chart_of_accounts(id), + + -- Condiciones de pago + payment_terms VARCHAR(100), + payment_days INTEGER NOT NULL DEFAULT 30, + + -- Dias de atraso (calculado) + days_overdue INTEGER NOT NULL DEFAULT 0, + + -- CFDI (facturacion electronica Mexico) + cfdi_uuid VARCHAR(36), + cfdi_xml_path VARCHAR(500), + cfdi_pdf_path VARCHAR(500), + + -- Aprobacion + approved_for_payment BOOLEAN NOT NULL DEFAULT FALSE, + approved_by_id UUID, + approved_at TIMESTAMPTZ, + + -- Poliza contable generada + accounting_entry_id UUID REFERENCES finance.accounting_entries(id), + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- ---------------------------------------------------------------------------- +-- 5. Cuentas por Cobrar (Accounts Receivable) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.accounts_receivable ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Numero de documento + document_number VARCHAR(100) NOT NULL, + document_type finance.ar_document_type NOT NULL DEFAULT 'invoice', + + -- Cliente + customer_id UUID NOT NULL, + customer_name VARCHAR(255) NOT NULL, + customer_rfc VARCHAR(13), + + -- Estado + status finance.ar_status NOT NULL DEFAULT 'pending', + + -- Fechas + invoice_date DATE NOT NULL, + due_date DATE NOT NULL, + collection_date DATE, + + -- Montos + subtotal DECIMAL(18,2) NOT NULL, + tax_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + retention_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + total_amount DECIMAL(18,2) NOT NULL, + collected_amount DECIMAL(18,2) NOT NULL DEFAULT 0, + balance DECIMAL(18,2) NOT NULL, + + -- Moneda + currency VARCHAR(3) NOT NULL DEFAULT 'MXN', + exchange_rate DECIMAL(12,6) NOT NULL DEFAULT 1, + + -- Origen (estimacion, venta) + source_module VARCHAR(50), + source_id UUID, + estimation_number VARCHAR(50), + + -- Proyecto asociado + project_id UUID, + project_code VARCHAR(50), + + -- Condiciones de cobro + payment_terms VARCHAR(100), + payment_days INTEGER NOT NULL DEFAULT 30, + + -- Dias de atraso (calculado) + days_overdue INTEGER NOT NULL DEFAULT 0, + + -- CFDI (facturacion electronica Mexico) + cfdi_uuid VARCHAR(36), + cfdi_xml_path VARCHAR(500), + cfdi_pdf_path VARCHAR(500), + + -- Cuenta contable + income_account_id UUID REFERENCES finance.chart_of_accounts(id), + + -- Poliza contable generada + accounting_entry_id UUID REFERENCES finance.accounting_entries(id), + + -- Seguimiento de cobranza + last_collection_attempt DATE, + collection_attempts INTEGER NOT NULL DEFAULT 0, + collection_notes TEXT, + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- ---------------------------------------------------------------------------- +-- 6. Cuentas Bancarias (Bank Accounts) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.bank_accounts ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Informacion de la cuenta + name VARCHAR(100) NOT NULL, + account_number VARCHAR(50) NOT NULL, + clabe VARCHAR(18), + account_type finance.bank_account_type NOT NULL DEFAULT 'checking', + status finance.bank_account_status NOT NULL DEFAULT 'active', + + -- Banco + bank_name VARCHAR(100) NOT NULL, + bank_code VARCHAR(10), + branch_name VARCHAR(100), + branch_code VARCHAR(20), + + -- Moneda + currency VARCHAR(3) NOT NULL DEFAULT 'MXN', + + -- Saldos + initial_balance DECIMAL(18,2) NOT NULL DEFAULT 0, + current_balance DECIMAL(18,2) NOT NULL DEFAULT 0, + available_balance DECIMAL(18,2) NOT NULL DEFAULT 0, + balance_updated_at TIMESTAMPTZ, + + -- Limites (para lineas de credito) + credit_limit DECIMAL(18,2), + minimum_balance DECIMAL(18,2), + + -- Proyecto asociado (si es cuenta especifica de proyecto) + project_id UUID, + project_code VARCHAR(50), + + -- Cuenta contable vinculada + ledger_account_id UUID REFERENCES finance.chart_of_accounts(id), + + -- Contacto del banco + bank_contact_name VARCHAR(255), + bank_contact_phone VARCHAR(50), + bank_contact_email VARCHAR(255), + + -- Conciliacion + last_reconciliation_date DATE, + last_reconciled_balance DECIMAL(18,2), + + -- Acceso banca en linea + online_banking_user VARCHAR(100), + supports_api BOOLEAN NOT NULL DEFAULT FALSE, + + -- Flags + is_default BOOLEAN NOT NULL DEFAULT FALSE, + allows_payments BOOLEAN NOT NULL DEFAULT TRUE, + allows_collections BOOLEAN NOT NULL DEFAULT TRUE, + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ, + + -- Constraints + CONSTRAINT uq_bank_accounts_tenant_number UNIQUE (tenant_id, account_number) +); + +-- ---------------------------------------------------------------------------- +-- 7. Movimientos Bancarios (Bank Movements) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.bank_movements ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Cuenta bancaria + bank_account_id UUID NOT NULL REFERENCES finance.bank_accounts(id), + + -- Referencia del movimiento + movement_reference VARCHAR(100), + bank_reference VARCHAR(100), + + -- Tipo y fecha + movement_type finance.bank_movement_type NOT NULL, + movement_date DATE NOT NULL, + value_date DATE, + + -- Descripcion del banco + description TEXT NOT NULL, + bank_description TEXT, + + -- Monto + amount DECIMAL(18,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'MXN', + + -- Saldo despues del movimiento + balance_after DECIMAL(18,2), + + -- Estado de conciliacion + status finance.bank_movement_status NOT NULL DEFAULT 'pending', + + -- Origen del movimiento + source finance.bank_movement_source NOT NULL DEFAULT 'manual', + import_batch_id UUID, + + -- Coincidencia automatica + matched_payment_id UUID, + matched_collection_id UUID, + matched_entry_id UUID REFERENCES finance.accounting_entries(id), + match_confidence DECIMAL(5,2), + + -- Conciliacion + reconciliation_id UUID, + reconciled_at TIMESTAMPTZ, + reconciled_by_id UUID, + + -- Categorizacion + category VARCHAR(100), + subcategory VARCHAR(100), + + -- Tercero identificado + partner_id UUID, + partner_name VARCHAR(255), + + -- Flags + is_duplicate BOOLEAN NOT NULL DEFAULT FALSE, + requires_review BOOLEAN NOT NULL DEFAULT FALSE, + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Datos originales del banco + raw_data JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- ---------------------------------------------------------------------------- +-- 8. Conciliaciones Bancarias (Bank Reconciliations) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.bank_reconciliations ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Cuenta bancaria + bank_account_id UUID NOT NULL REFERENCES finance.bank_accounts(id), + + -- Periodo de conciliacion + period_start DATE NOT NULL, + period_end DATE NOT NULL, + + -- Estado + status finance.reconciliation_status NOT NULL DEFAULT 'draft', + + -- Saldos segun banco + bank_opening_balance DECIMAL(18,2) NOT NULL, + bank_closing_balance DECIMAL(18,2) NOT NULL, + + -- Saldos segun libros + book_opening_balance DECIMAL(18,2) NOT NULL, + book_closing_balance DECIMAL(18,2) NOT NULL, + + -- Partidas de conciliacion + deposits_in_transit DECIMAL(18,2) NOT NULL DEFAULT 0, + checks_in_transit DECIMAL(18,2) NOT NULL DEFAULT 0, + bank_charges_not_recorded DECIMAL(18,2) NOT NULL DEFAULT 0, + interest_not_recorded DECIMAL(18,2) NOT NULL DEFAULT 0, + other_adjustments DECIMAL(18,2) NOT NULL DEFAULT 0, + + -- Saldo conciliado + reconciled_balance DECIMAL(18,2) NOT NULL, + + -- Diferencia (debe ser 0 si esta conciliado) + difference DECIMAL(18,2) NOT NULL DEFAULT 0, + is_balanced BOOLEAN NOT NULL DEFAULT FALSE, + + -- Contadores + total_movements INTEGER NOT NULL DEFAULT 0, + reconciled_movements INTEGER NOT NULL DEFAULT 0, + pending_movements INTEGER NOT NULL DEFAULT 0, + + -- Estado de cuenta bancario (archivo importado) + statement_file_path VARCHAR(500), + statement_import_date TIMESTAMPTZ, + + -- Fechas de proceso + started_at TIMESTAMPTZ, + completed_at TIMESTAMPTZ, + + -- Aprobacion + approved_by_id UUID, + approved_at TIMESTAMPTZ, + + -- Poliza de ajuste generada + adjustment_entry_id UUID REFERENCES finance.accounting_entries(id), + + -- Notas y metadatos + notes TEXT, + reconciliation_items JSONB, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- ---------------------------------------------------------------------------- +-- 9. Pagos a Proveedores (AP Payments) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.ap_payments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Numero de pago + payment_number VARCHAR(50) NOT NULL, + + -- Cuenta por pagar asociada + account_payable_id UUID NOT NULL REFERENCES finance.accounts_payable(id), + + -- Metodo de pago + payment_method finance.payment_method NOT NULL, + status finance.payment_status NOT NULL DEFAULT 'pending', + + -- Fecha de pago + payment_date DATE NOT NULL, + + -- Monto + amount DECIMAL(18,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'MXN', + exchange_rate DECIMAL(12,6) NOT NULL DEFAULT 1, + + -- Cuenta bancaria de origen + bank_account_id UUID REFERENCES finance.bank_accounts(id), + + -- Detalles del instrumento de pago + check_number VARCHAR(50), + transfer_reference VARCHAR(100), + authorization_code VARCHAR(50), + + -- Beneficiario + beneficiary_name VARCHAR(255), + beneficiary_bank VARCHAR(100), + beneficiary_account VARCHAR(50), + beneficiary_clabe VARCHAR(18), + + -- Poliza contable generada + accounting_entry_id UUID REFERENCES finance.accounting_entries(id), + + -- Conciliacion bancaria + bank_movement_id UUID REFERENCES finance.bank_movements(id), + reconciled_at TIMESTAMPTZ, + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- ---------------------------------------------------------------------------- +-- 10. Cobros de Clientes (AR Payments) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.ar_payments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Numero de cobro + collection_number VARCHAR(50) NOT NULL, + + -- Cuenta por cobrar asociada + account_receivable_id UUID NOT NULL REFERENCES finance.accounts_receivable(id), + + -- Metodo de cobro + collection_method finance.collection_method NOT NULL, + status finance.collection_status NOT NULL DEFAULT 'pending', + + -- Fecha de cobro + collection_date DATE NOT NULL, + deposit_date DATE, + + -- Monto + amount DECIMAL(18,2) NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'MXN', + exchange_rate DECIMAL(12,6) NOT NULL DEFAULT 1, + + -- Cuenta bancaria de destino + bank_account_id UUID REFERENCES finance.bank_accounts(id), + + -- Detalles del instrumento de cobro + check_number VARCHAR(50), + check_bank VARCHAR(100), + transfer_reference VARCHAR(100), + + -- Pagador (si es diferente al cliente) + payer_name VARCHAR(255), + payer_bank VARCHAR(100), + payer_account VARCHAR(50), + + -- Poliza contable generada + accounting_entry_id UUID REFERENCES finance.accounting_entries(id), + + -- Conciliacion bancaria + bank_movement_id UUID REFERENCES finance.bank_movements(id), + reconciled_at TIMESTAMPTZ, + + -- Notas y metadatos + notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- ---------------------------------------------------------------------------- +-- 11. Proyecciones de Flujo de Efectivo (Cash Flow Projections) +-- ---------------------------------------------------------------------------- +CREATE TABLE finance.cash_flow_projections ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + tenant_id UUID NOT NULL, + + -- Tipo de flujo + flow_type finance.cash_flow_type NOT NULL, + period_type finance.cash_flow_period_type NOT NULL DEFAULT 'weekly', + + -- Periodo + period_start DATE NOT NULL, + period_end DATE NOT NULL, + fiscal_year INTEGER NOT NULL, + fiscal_period INTEGER NOT NULL, + + -- Proyecto (opcional, si es por proyecto) + project_id UUID, + project_code VARCHAR(50), + + -- Saldo inicial + opening_balance DECIMAL(18,2) NOT NULL, + + -- INGRESOS OPERATIVOS + income_estimations DECIMAL(18,2) NOT NULL DEFAULT 0, + income_sales DECIMAL(18,2) NOT NULL DEFAULT 0, + income_advances DECIMAL(18,2) NOT NULL DEFAULT 0, + income_other DECIMAL(18,2) NOT NULL DEFAULT 0, + total_income DECIMAL(18,2) NOT NULL DEFAULT 0, + + -- EGRESOS OPERATIVOS + expense_suppliers DECIMAL(18,2) NOT NULL DEFAULT 0, + expense_subcontractors DECIMAL(18,2) NOT NULL DEFAULT 0, + expense_payroll DECIMAL(18,2) NOT NULL DEFAULT 0, + expense_taxes DECIMAL(18,2) NOT NULL DEFAULT 0, + expense_operating DECIMAL(18,2) NOT NULL DEFAULT 0, + expense_other DECIMAL(18,2) NOT NULL DEFAULT 0, + total_expenses DECIMAL(18,2) NOT NULL DEFAULT 0, + + -- FLUJO NETO OPERATIVO + net_operating_flow DECIMAL(18,2) NOT NULL DEFAULT 0, + + -- INVERSION + investing_income DECIMAL(18,2) NOT NULL DEFAULT 0, + investing_expense DECIMAL(18,2) NOT NULL DEFAULT 0, + net_investing_flow DECIMAL(18,2) NOT NULL DEFAULT 0, + + -- FINANCIAMIENTO + financing_income DECIMAL(18,2) NOT NULL DEFAULT 0, + financing_expense DECIMAL(18,2) NOT NULL DEFAULT 0, + net_financing_flow DECIMAL(18,2) NOT NULL DEFAULT 0, + + -- TOTALES + net_cash_flow DECIMAL(18,2) NOT NULL DEFAULT 0, + closing_balance DECIMAL(18,2) NOT NULL, + + -- Varianza (para comparaciones) + projected_amount DECIMAL(18,2), + actual_amount DECIMAL(18,2), + variance_amount DECIMAL(18,2), + variance_percentage DECIMAL(8,2), + + -- Desglose detallado (JSON) + income_breakdown JSONB, + expense_breakdown JSONB, + + -- Estado + is_locked BOOLEAN NOT NULL DEFAULT FALSE, + locked_at TIMESTAMPTZ, + + -- Notas y metadatos + notes TEXT, + variance_notes TEXT, + metadata JSONB, + + -- Auditoria + created_by UUID, + updated_by UUID, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + deleted_at TIMESTAMPTZ +); + +-- ============================================================================ +-- INDICES +-- ============================================================================ + +-- Chart of Accounts +CREATE INDEX idx_chart_of_accounts_tenant ON finance.chart_of_accounts(tenant_id); +CREATE INDEX idx_chart_of_accounts_tenant_type ON finance.chart_of_accounts(tenant_id, account_type); +CREATE INDEX idx_chart_of_accounts_parent ON finance.chart_of_accounts(parent_id); + +-- Accounting Entries +CREATE INDEX idx_accounting_entries_tenant ON finance.accounting_entries(tenant_id); +CREATE INDEX idx_accounting_entries_tenant_date ON finance.accounting_entries(tenant_id, entry_date); +CREATE INDEX idx_accounting_entries_tenant_status ON finance.accounting_entries(tenant_id, status); +CREATE INDEX idx_accounting_entries_source ON finance.accounting_entries(tenant_id, source_module, source_id); + +-- Accounting Entry Lines +CREATE INDEX idx_accounting_entry_lines_tenant ON finance.accounting_entry_lines(tenant_id); +CREATE INDEX idx_accounting_entry_lines_entry ON finance.accounting_entry_lines(tenant_id, entry_id); +CREATE INDEX idx_accounting_entry_lines_account ON finance.accounting_entry_lines(tenant_id, account_id); + +-- Accounts Payable +CREATE INDEX idx_accounts_payable_tenant ON finance.accounts_payable(tenant_id); +CREATE INDEX idx_accounts_payable_supplier ON finance.accounts_payable(tenant_id, supplier_id); +CREATE INDEX idx_accounts_payable_status ON finance.accounts_payable(tenant_id, status); +CREATE INDEX idx_accounts_payable_due_date ON finance.accounts_payable(tenant_id, due_date); +CREATE INDEX idx_accounts_payable_project ON finance.accounts_payable(tenant_id, project_id); + +-- Accounts Receivable +CREATE INDEX idx_accounts_receivable_tenant ON finance.accounts_receivable(tenant_id); +CREATE INDEX idx_accounts_receivable_customer ON finance.accounts_receivable(tenant_id, customer_id); +CREATE INDEX idx_accounts_receivable_status ON finance.accounts_receivable(tenant_id, status); +CREATE INDEX idx_accounts_receivable_due_date ON finance.accounts_receivable(tenant_id, due_date); +CREATE INDEX idx_accounts_receivable_project ON finance.accounts_receivable(tenant_id, project_id); + +-- Bank Accounts +CREATE INDEX idx_bank_accounts_tenant ON finance.bank_accounts(tenant_id); +CREATE INDEX idx_bank_accounts_status ON finance.bank_accounts(tenant_id, status); + +-- Bank Movements +CREATE INDEX idx_bank_movements_tenant ON finance.bank_movements(tenant_id); +CREATE INDEX idx_bank_movements_account ON finance.bank_movements(tenant_id, bank_account_id); +CREATE INDEX idx_bank_movements_date ON finance.bank_movements(tenant_id, movement_date); +CREATE INDEX idx_bank_movements_status ON finance.bank_movements(tenant_id, status); + +-- Bank Reconciliations +CREATE INDEX idx_bank_reconciliations_tenant ON finance.bank_reconciliations(tenant_id); +CREATE INDEX idx_bank_reconciliations_account ON finance.bank_reconciliations(tenant_id, bank_account_id); +CREATE INDEX idx_bank_reconciliations_period ON finance.bank_reconciliations(tenant_id, period_end); +CREATE INDEX idx_bank_reconciliations_status ON finance.bank_reconciliations(tenant_id, status); + +-- AP Payments +CREATE INDEX idx_ap_payments_tenant ON finance.ap_payments(tenant_id); +CREATE INDEX idx_ap_payments_ap ON finance.ap_payments(tenant_id, account_payable_id); +CREATE INDEX idx_ap_payments_date ON finance.ap_payments(tenant_id, payment_date); +CREATE INDEX idx_ap_payments_bank ON finance.ap_payments(tenant_id, bank_account_id); + +-- AR Payments +CREATE INDEX idx_ar_payments_tenant ON finance.ar_payments(tenant_id); +CREATE INDEX idx_ar_payments_ar ON finance.ar_payments(tenant_id, account_receivable_id); +CREATE INDEX idx_ar_payments_date ON finance.ar_payments(tenant_id, collection_date); +CREATE INDEX idx_ar_payments_bank ON finance.ar_payments(tenant_id, bank_account_id); + +-- Cash Flow Projections +CREATE INDEX idx_cash_flow_tenant ON finance.cash_flow_projections(tenant_id); +CREATE INDEX idx_cash_flow_project ON finance.cash_flow_projections(tenant_id, project_id); +CREATE INDEX idx_cash_flow_period ON finance.cash_flow_projections(tenant_id, period_start, period_end); +CREATE INDEX idx_cash_flow_type ON finance.cash_flow_projections(tenant_id, flow_type); + +-- ============================================================================ +-- ROW LEVEL SECURITY (RLS) +-- ============================================================================ + +-- Habilitar RLS en todas las tablas +ALTER TABLE finance.chart_of_accounts ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.accounting_entries ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.accounting_entry_lines ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.accounts_payable ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.accounts_receivable ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.bank_accounts ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.bank_movements ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.bank_reconciliations ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.ap_payments ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.ar_payments ENABLE ROW LEVEL SECURITY; +ALTER TABLE finance.cash_flow_projections ENABLE ROW LEVEL SECURITY; + +-- Crear politicas de tenant isolation (asumiendo funcion current_tenant_id() existe) +-- Estas politicas deben crearse despues de que exista la funcion en el schema auth + +-- ============================================================================ +-- TRIGGERS DE AUDITORIA +-- ============================================================================ + +-- Trigger para updated_at +CREATE OR REPLACE FUNCTION finance.set_updated_at() +RETURNS TRIGGER AS $$ +BEGIN + NEW.updated_at = NOW(); + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +-- Aplicar trigger a todas las tablas con updated_at +CREATE TRIGGER trg_chart_of_accounts_updated_at + BEFORE UPDATE ON finance.chart_of_accounts + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_accounting_entries_updated_at + BEFORE UPDATE ON finance.accounting_entries + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_accounting_entry_lines_updated_at + BEFORE UPDATE ON finance.accounting_entry_lines + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_accounts_payable_updated_at + BEFORE UPDATE ON finance.accounts_payable + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_accounts_receivable_updated_at + BEFORE UPDATE ON finance.accounts_receivable + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_bank_accounts_updated_at + BEFORE UPDATE ON finance.bank_accounts + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_bank_movements_updated_at + BEFORE UPDATE ON finance.bank_movements + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_bank_reconciliations_updated_at + BEFORE UPDATE ON finance.bank_reconciliations + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_ap_payments_updated_at + BEFORE UPDATE ON finance.ap_payments + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_ar_payments_updated_at + BEFORE UPDATE ON finance.ar_payments + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +CREATE TRIGGER trg_cash_flow_projections_updated_at + BEFORE UPDATE ON finance.cash_flow_projections + FOR EACH ROW EXECUTE FUNCTION finance.set_updated_at(); + +-- ============================================================================ +-- FUNCIONES AUXILIARES +-- ============================================================================ + +-- Funcion para validar balance de poliza +CREATE OR REPLACE FUNCTION finance.validate_entry_balance() +RETURNS TRIGGER AS $$ +DECLARE + v_total_debit DECIMAL(18,2); + v_total_credit DECIMAL(18,2); +BEGIN + SELECT COALESCE(SUM(debit), 0), COALESCE(SUM(credit), 0) + INTO v_total_debit, v_total_credit + FROM finance.accounting_entry_lines + WHERE entry_id = NEW.entry_id; + + UPDATE finance.accounting_entries + SET total_debit = v_total_debit, + total_credit = v_total_credit, + is_balanced = (v_total_debit = v_total_credit) + WHERE id = NEW.entry_id; + + RETURN NEW; +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_validate_entry_balance + AFTER INSERT OR UPDATE OR DELETE ON finance.accounting_entry_lines + FOR EACH ROW EXECUTE FUNCTION finance.validate_entry_balance(); + +-- Funcion para actualizar saldo de CxP +CREATE OR REPLACE FUNCTION finance.update_ap_balance() +RETURNS TRIGGER AS $$ +DECLARE + v_paid_amount DECIMAL(18,2); +BEGIN + SELECT COALESCE(SUM(amount), 0) + INTO v_paid_amount + FROM finance.ap_payments + WHERE account_payable_id = COALESCE(NEW.account_payable_id, OLD.account_payable_id) + AND status NOT IN ('cancelled', 'returned') + AND deleted_at IS NULL; + + UPDATE finance.accounts_payable + SET paid_amount = v_paid_amount, + balance = total_amount - v_paid_amount, + status = CASE + WHEN v_paid_amount >= total_amount THEN 'paid'::finance.ap_status + WHEN v_paid_amount > 0 THEN 'partial'::finance.ap_status + ELSE status + END + WHERE id = COALESCE(NEW.account_payable_id, OLD.account_payable_id); + + RETURN COALESCE(NEW, OLD); +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_update_ap_balance + AFTER INSERT OR UPDATE OR DELETE ON finance.ap_payments + FOR EACH ROW EXECUTE FUNCTION finance.update_ap_balance(); + +-- Funcion para actualizar saldo de CxC +CREATE OR REPLACE FUNCTION finance.update_ar_balance() +RETURNS TRIGGER AS $$ +DECLARE + v_collected_amount DECIMAL(18,2); +BEGIN + SELECT COALESCE(SUM(amount), 0) + INTO v_collected_amount + FROM finance.ar_payments + WHERE account_receivable_id = COALESCE(NEW.account_receivable_id, OLD.account_receivable_id) + AND status NOT IN ('cancelled', 'returned') + AND deleted_at IS NULL; + + UPDATE finance.accounts_receivable + SET collected_amount = v_collected_amount, + balance = total_amount - v_collected_amount, + status = CASE + WHEN v_collected_amount >= total_amount THEN 'collected'::finance.ar_status + WHEN v_collected_amount > 0 THEN 'partial'::finance.ar_status + ELSE status + END + WHERE id = COALESCE(NEW.account_receivable_id, OLD.account_receivable_id); + + RETURN COALESCE(NEW, OLD); +END; +$$ LANGUAGE plpgsql; + +CREATE TRIGGER trg_update_ar_balance + AFTER INSERT OR UPDATE OR DELETE ON finance.ar_payments + FOR EACH ROW EXECUTE FUNCTION finance.update_ar_balance(); + +-- ============================================================================ +-- COMENTARIOS DE DOCUMENTACION +-- ============================================================================ + +COMMENT ON SCHEMA finance IS 'Modulo de Finanzas y Controlling (MAE-014) - ERP Construccion'; + +COMMENT ON TABLE finance.chart_of_accounts IS 'Catalogo de cuentas contables con jerarquia'; +COMMENT ON TABLE finance.accounting_entries IS 'Polizas contables (asientos contables)'; +COMMENT ON TABLE finance.accounting_entry_lines IS 'Lineas de detalle de polizas contables'; +COMMENT ON TABLE finance.accounts_payable IS 'Cuentas por pagar a proveedores'; +COMMENT ON TABLE finance.accounts_receivable IS 'Cuentas por cobrar de clientes'; +COMMENT ON TABLE finance.bank_accounts IS 'Cuentas bancarias de la empresa'; +COMMENT ON TABLE finance.bank_movements IS 'Movimientos bancarios importados'; +COMMENT ON TABLE finance.bank_reconciliations IS 'Procesos de conciliacion bancaria'; +COMMENT ON TABLE finance.ap_payments IS 'Pagos realizados a proveedores'; +COMMENT ON TABLE finance.ar_payments IS 'Cobros recibidos de clientes'; +COMMENT ON TABLE finance.cash_flow_projections IS 'Proyecciones de flujo de efectivo'; + +-- ============================================================================ +-- FIN DEL SCRIPT +-- ============================================================================