1004 lines
30 KiB
Markdown
1004 lines
30 KiB
Markdown
# DDL SPECIFICATION: Schema Finance
|
|
|
|
**Version:** 1.0.0
|
|
**Fecha:** 2025-12-05
|
|
**Schema:** `finance`
|
|
**Modulos:** MAE-014 (Finanzas y Controlling)
|
|
|
|
---
|
|
|
|
## Resumen
|
|
|
|
| Metrica | Valor |
|
|
|---------|-------|
|
|
| Total Tablas | 15 |
|
|
| ENUMs | 7 |
|
|
| Funciones | 8 |
|
|
| Triggers | 5 |
|
|
| Indices | 40+ |
|
|
|
|
---
|
|
|
|
## 1. ENUMs
|
|
|
|
```sql
|
|
-- Tipos de cuenta contable
|
|
CREATE TYPE finance.account_type AS ENUM (
|
|
'asset', -- Activo
|
|
'liability', -- Pasivo
|
|
'equity', -- Capital
|
|
'income', -- Ingreso
|
|
'expense' -- Gasto
|
|
);
|
|
|
|
-- Naturaleza de cuenta
|
|
CREATE TYPE finance.account_nature AS ENUM (
|
|
'debit', -- Deudora
|
|
'credit' -- Acreedora
|
|
);
|
|
|
|
-- Tipos de poliza/entrada contable
|
|
CREATE TYPE finance.entry_type AS ENUM (
|
|
'income', -- Ingreso
|
|
'expense', -- Egreso
|
|
'journal', -- Diario
|
|
'transfer', -- Traspaso
|
|
'adjustment', -- Ajuste
|
|
'opening', -- Apertura
|
|
'closing' -- Cierre
|
|
);
|
|
|
|
-- Estados de pago
|
|
CREATE TYPE finance.payment_status AS ENUM (
|
|
'pending', -- Pendiente
|
|
'partial', -- Pago parcial
|
|
'paid', -- Pagado
|
|
'overdue', -- Vencido
|
|
'cancelled' -- Cancelado
|
|
);
|
|
|
|
-- Metodos de pago
|
|
CREATE TYPE finance.payment_method AS ENUM (
|
|
'cash', -- Efectivo
|
|
'check', -- Cheque
|
|
'transfer', -- Transferencia
|
|
'card', -- Tarjeta
|
|
'deposit', -- Deposito
|
|
'other' -- Otro
|
|
);
|
|
|
|
-- Estados de conciliacion
|
|
CREATE TYPE finance.reconciliation_status AS ENUM (
|
|
'pending', -- Pendiente
|
|
'matched', -- Conciliado
|
|
'partial', -- Parcialmente conciliado
|
|
'unmatched' -- Sin conciliar
|
|
);
|
|
|
|
-- Tipos de movimiento de cash flow
|
|
CREATE TYPE finance.cash_flow_type AS ENUM (
|
|
'operating', -- Operativo
|
|
'investing', -- Inversion
|
|
'financing' -- Financiamiento
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Tablas de Catalogo de Cuentas
|
|
|
|
### 2.1 chart_of_accounts (Catalogo de Cuentas)
|
|
|
|
```sql
|
|
CREATE TABLE finance.chart_of_accounts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(30) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Clasificacion
|
|
account_type finance.account_type NOT NULL,
|
|
nature finance.account_nature NOT NULL,
|
|
level INTEGER NOT NULL DEFAULT 1,
|
|
|
|
-- Jerarquia
|
|
parent_id UUID REFERENCES finance.chart_of_accounts(id),
|
|
full_path VARCHAR(500), -- Ej: "5000/5100/5101"
|
|
|
|
-- Configuracion
|
|
cost_center_required BOOLEAN DEFAULT false,
|
|
project_required BOOLEAN DEFAULT false,
|
|
allows_transactions BOOLEAN DEFAULT true,
|
|
|
|
-- Codigos externos
|
|
sap_code VARCHAR(50),
|
|
contpaqi_code VARCHAR(50),
|
|
aspel_code VARCHAR(50),
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_accounts_code UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_accounts_tenant ON finance.chart_of_accounts(tenant_id);
|
|
CREATE INDEX idx_accounts_type ON finance.chart_of_accounts(account_type);
|
|
CREATE INDEX idx_accounts_parent ON finance.chart_of_accounts(parent_id);
|
|
CREATE INDEX idx_accounts_active ON finance.chart_of_accounts(tenant_id) WHERE is_active = true AND allows_transactions = true;
|
|
CREATE INDEX idx_accounts_path ON finance.chart_of_accounts(tenant_id, full_path);
|
|
```
|
|
|
|
### 2.2 cost_centers (Centros de Costo)
|
|
|
|
```sql
|
|
CREATE TABLE finance.cost_centers (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
code VARCHAR(30) NOT NULL,
|
|
name VARCHAR(200) NOT NULL,
|
|
description TEXT,
|
|
|
|
-- Jerarquia
|
|
parent_id UUID REFERENCES finance.cost_centers(id),
|
|
level INTEGER DEFAULT 1,
|
|
|
|
-- Vinculacion
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
department_id UUID,
|
|
|
|
-- Presupuesto
|
|
budget_amount DECIMAL(18,2),
|
|
spent_amount DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_cost_centers_code UNIQUE (tenant_id, code)
|
|
);
|
|
|
|
CREATE INDEX idx_cost_centers_tenant ON finance.cost_centers(tenant_id);
|
|
CREATE INDEX idx_cost_centers_project ON finance.cost_centers(project_id);
|
|
CREATE INDEX idx_cost_centers_parent ON finance.cost_centers(parent_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Tablas de Polizas Contables
|
|
|
|
### 3.1 accounting_entries (Polizas)
|
|
|
|
```sql
|
|
CREATE TABLE finance.accounting_entries (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
entry_number VARCHAR(30) NOT NULL,
|
|
entry_type finance.entry_type NOT NULL,
|
|
entry_date DATE NOT NULL,
|
|
|
|
-- Descripcion
|
|
description TEXT NOT NULL,
|
|
reference VARCHAR(100),
|
|
|
|
-- Origen
|
|
source_module VARCHAR(50), -- purchases, estimations, payroll, etc.
|
|
source_id UUID,
|
|
source_reference VARCHAR(100),
|
|
|
|
-- Vinculacion
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
cost_center_id UUID REFERENCES finance.cost_centers(id),
|
|
|
|
-- Totales
|
|
total_debit DECIMAL(18,2) NOT NULL DEFAULT 0,
|
|
total_credit DECIMAL(18,2) NOT NULL DEFAULT 0,
|
|
is_balanced BOOLEAN DEFAULT false,
|
|
|
|
-- Estados
|
|
status VARCHAR(20) DEFAULT 'draft', -- draft, pending, posted, cancelled
|
|
posted_at TIMESTAMP,
|
|
posted_by UUID REFERENCES core.users(id),
|
|
|
|
-- Periodo contable
|
|
fiscal_year INTEGER,
|
|
fiscal_period INTEGER,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_entries_number UNIQUE (tenant_id, entry_number),
|
|
CONSTRAINT chk_entries_balanced CHECK (total_debit = total_credit OR status = 'draft')
|
|
);
|
|
|
|
CREATE INDEX idx_entries_tenant ON finance.accounting_entries(tenant_id);
|
|
CREATE INDEX idx_entries_date ON finance.accounting_entries(entry_date);
|
|
CREATE INDEX idx_entries_type ON finance.accounting_entries(entry_type);
|
|
CREATE INDEX idx_entries_project ON finance.accounting_entries(project_id);
|
|
CREATE INDEX idx_entries_status ON finance.accounting_entries(status);
|
|
CREATE INDEX idx_entries_period ON finance.accounting_entries(fiscal_year, fiscal_period);
|
|
CREATE INDEX idx_entries_source ON finance.accounting_entries(source_module, source_id);
|
|
```
|
|
|
|
### 3.2 accounting_entry_lines (Detalle de Polizas)
|
|
|
|
```sql
|
|
CREATE TABLE finance.accounting_entry_lines (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
entry_id UUID NOT NULL REFERENCES finance.accounting_entries(id) ON DELETE CASCADE,
|
|
|
|
-- Linea
|
|
line_number INTEGER NOT NULL,
|
|
|
|
-- Cuenta
|
|
account_id UUID NOT NULL REFERENCES finance.chart_of_accounts(id),
|
|
account_code VARCHAR(30) NOT NULL,
|
|
|
|
-- Descripcion
|
|
description TEXT,
|
|
|
|
-- Montos
|
|
debit_amount DECIMAL(18,2) DEFAULT 0,
|
|
credit_amount DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Imputacion
|
|
cost_center_id UUID REFERENCES finance.cost_centers(id),
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
|
|
-- Tercero
|
|
partner_id UUID, -- Proveedor o cliente
|
|
partner_name VARCHAR(200),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
|
|
CONSTRAINT chk_line_amount CHECK (
|
|
(debit_amount > 0 AND credit_amount = 0) OR
|
|
(credit_amount > 0 AND debit_amount = 0)
|
|
)
|
|
);
|
|
|
|
CREATE INDEX idx_entry_lines_entry ON finance.accounting_entry_lines(entry_id);
|
|
CREATE INDEX idx_entry_lines_account ON finance.accounting_entry_lines(account_id);
|
|
CREATE INDEX idx_entry_lines_cost_center ON finance.accounting_entry_lines(cost_center_id);
|
|
CREATE INDEX idx_entry_lines_project ON finance.accounting_entry_lines(project_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Tablas de Cuentas por Pagar (AP)
|
|
|
|
### 4.1 accounts_payable (Cuentas por Pagar)
|
|
|
|
```sql
|
|
CREATE TABLE finance.accounts_payable (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
document_number VARCHAR(50) NOT NULL,
|
|
document_type VARCHAR(30), -- invoice, credit_note, debit_note
|
|
|
|
-- Proveedor
|
|
supplier_id UUID NOT NULL,
|
|
supplier_name VARCHAR(200) NOT NULL,
|
|
supplier_rfc VARCHAR(20),
|
|
|
|
-- Origen
|
|
purchase_order_id UUID,
|
|
contract_id UUID,
|
|
|
|
-- Montos
|
|
subtotal DECIMAL(18,2) NOT NULL,
|
|
tax_amount DECIMAL(18,2) DEFAULT 0,
|
|
retention_amount DECIMAL(18,2) DEFAULT 0,
|
|
total_amount DECIMAL(18,2) NOT NULL,
|
|
paid_amount DECIMAL(18,2) DEFAULT 0,
|
|
balance DECIMAL(18,2) GENERATED ALWAYS AS (total_amount - paid_amount) STORED,
|
|
|
|
-- Fechas
|
|
document_date DATE NOT NULL,
|
|
due_date DATE NOT NULL,
|
|
received_date DATE,
|
|
|
|
-- Proyecto
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
cost_center_id UUID REFERENCES finance.cost_centers(id),
|
|
|
|
-- Estado
|
|
status finance.payment_status NOT NULL DEFAULT 'pending',
|
|
|
|
-- Poliza
|
|
accounting_entry_id UUID REFERENCES finance.accounting_entries(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_ap_document UNIQUE (tenant_id, supplier_id, document_number)
|
|
);
|
|
|
|
CREATE INDEX idx_ap_tenant ON finance.accounts_payable(tenant_id);
|
|
CREATE INDEX idx_ap_supplier ON finance.accounts_payable(supplier_id);
|
|
CREATE INDEX idx_ap_project ON finance.accounts_payable(project_id);
|
|
CREATE INDEX idx_ap_status ON finance.accounts_payable(status);
|
|
CREATE INDEX idx_ap_due_date ON finance.accounts_payable(due_date);
|
|
CREATE INDEX idx_ap_overdue ON finance.accounts_payable(tenant_id)
|
|
WHERE status IN ('pending', 'partial') AND due_date < CURRENT_DATE;
|
|
```
|
|
|
|
### 4.2 ap_payments (Pagos a Proveedores)
|
|
|
|
```sql
|
|
CREATE TABLE finance.ap_payments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
payable_id UUID NOT NULL REFERENCES finance.accounts_payable(id),
|
|
|
|
-- Pago
|
|
payment_number VARCHAR(30) NOT NULL,
|
|
payment_date DATE NOT NULL,
|
|
amount DECIMAL(18,2) NOT NULL,
|
|
|
|
-- Metodo
|
|
payment_method finance.payment_method NOT NULL,
|
|
bank_account_id UUID REFERENCES finance.bank_accounts(id),
|
|
reference VARCHAR(100),
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'completed',
|
|
|
|
-- Poliza
|
|
accounting_entry_id UUID REFERENCES finance.accounting_entries(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_ap_payments_payable ON finance.ap_payments(payable_id);
|
|
CREATE INDEX idx_ap_payments_date ON finance.ap_payments(payment_date);
|
|
CREATE INDEX idx_ap_payments_bank ON finance.ap_payments(bank_account_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Tablas de Cuentas por Cobrar (AR)
|
|
|
|
### 5.1 accounts_receivable (Cuentas por Cobrar)
|
|
|
|
```sql
|
|
CREATE TABLE finance.accounts_receivable (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
document_number VARCHAR(50) NOT NULL,
|
|
document_type VARCHAR(30), -- invoice, credit_note, estimation
|
|
|
|
-- Cliente
|
|
customer_id UUID NOT NULL,
|
|
customer_name VARCHAR(200) NOT NULL,
|
|
customer_rfc VARCHAR(20),
|
|
|
|
-- Origen
|
|
estimation_id UUID,
|
|
sale_order_id UUID,
|
|
contract_id UUID,
|
|
|
|
-- Montos
|
|
subtotal DECIMAL(18,2) NOT NULL,
|
|
tax_amount DECIMAL(18,2) DEFAULT 0,
|
|
retention_amount DECIMAL(18,2) DEFAULT 0,
|
|
total_amount DECIMAL(18,2) NOT NULL,
|
|
collected_amount DECIMAL(18,2) DEFAULT 0,
|
|
balance DECIMAL(18,2) GENERATED ALWAYS AS (total_amount - collected_amount) STORED,
|
|
|
|
-- Fechas
|
|
document_date DATE NOT NULL,
|
|
due_date DATE NOT NULL,
|
|
|
|
-- Proyecto
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
|
|
-- Estado
|
|
status finance.payment_status NOT NULL DEFAULT 'pending',
|
|
|
|
-- Poliza
|
|
accounting_entry_id UUID REFERENCES finance.accounting_entries(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_ar_document UNIQUE (tenant_id, document_number)
|
|
);
|
|
|
|
CREATE INDEX idx_ar_tenant ON finance.accounts_receivable(tenant_id);
|
|
CREATE INDEX idx_ar_customer ON finance.accounts_receivable(customer_id);
|
|
CREATE INDEX idx_ar_project ON finance.accounts_receivable(project_id);
|
|
CREATE INDEX idx_ar_status ON finance.accounts_receivable(status);
|
|
CREATE INDEX idx_ar_due_date ON finance.accounts_receivable(due_date);
|
|
CREATE INDEX idx_ar_overdue ON finance.accounts_receivable(tenant_id)
|
|
WHERE status IN ('pending', 'partial') AND due_date < CURRENT_DATE;
|
|
```
|
|
|
|
### 5.2 ar_collections (Cobros)
|
|
|
|
```sql
|
|
CREATE TABLE finance.ar_collections (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
receivable_id UUID NOT NULL REFERENCES finance.accounts_receivable(id),
|
|
|
|
-- Cobro
|
|
collection_number VARCHAR(30) NOT NULL,
|
|
collection_date DATE NOT NULL,
|
|
amount DECIMAL(18,2) NOT NULL,
|
|
|
|
-- Metodo
|
|
payment_method finance.payment_method NOT NULL,
|
|
bank_account_id UUID REFERENCES finance.bank_accounts(id),
|
|
reference VARCHAR(100),
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'completed',
|
|
|
|
-- Poliza
|
|
accounting_entry_id UUID REFERENCES finance.accounting_entries(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_ar_collections_receivable ON finance.ar_collections(receivable_id);
|
|
CREATE INDEX idx_ar_collections_date ON finance.ar_collections(collection_date);
|
|
CREATE INDEX idx_ar_collections_bank ON finance.ar_collections(bank_account_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 6. Tablas Bancarias y Conciliacion
|
|
|
|
### 6.1 bank_accounts (Cuentas Bancarias)
|
|
|
|
```sql
|
|
CREATE TABLE finance.bank_accounts (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
|
|
-- Identificacion
|
|
account_number VARCHAR(30) NOT NULL,
|
|
clabe VARCHAR(18),
|
|
name VARCHAR(200) NOT NULL,
|
|
|
|
-- Banco
|
|
bank_name VARCHAR(100) NOT NULL,
|
|
bank_code VARCHAR(10),
|
|
|
|
-- Tipo
|
|
account_type VARCHAR(30), -- checking, savings, credit
|
|
|
|
-- Moneda
|
|
currency VARCHAR(3) DEFAULT 'MXN',
|
|
|
|
-- Saldos
|
|
current_balance DECIMAL(18,2) DEFAULT 0,
|
|
available_balance DECIMAL(18,2) DEFAULT 0,
|
|
last_reconciled_balance DECIMAL(18,2),
|
|
last_reconciled_date DATE,
|
|
|
|
-- Cuenta contable
|
|
ledger_account_id UUID REFERENCES finance.chart_of_accounts(id),
|
|
|
|
-- Proyecto (si es cuenta dedicada)
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
|
|
-- Estado
|
|
is_active BOOLEAN DEFAULT true,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
deleted_at TIMESTAMP,
|
|
|
|
CONSTRAINT uq_bank_accounts UNIQUE (tenant_id, account_number)
|
|
);
|
|
|
|
CREATE INDEX idx_bank_accounts_tenant ON finance.bank_accounts(tenant_id);
|
|
CREATE INDEX idx_bank_accounts_project ON finance.bank_accounts(project_id);
|
|
CREATE INDEX idx_bank_accounts_active ON finance.bank_accounts(tenant_id) WHERE is_active = true;
|
|
```
|
|
|
|
### 6.2 bank_movements (Movimientos Bancarios)
|
|
|
|
```sql
|
|
CREATE TABLE finance.bank_movements (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
bank_account_id UUID NOT NULL REFERENCES finance.bank_accounts(id),
|
|
|
|
-- Movimiento
|
|
movement_date DATE NOT NULL,
|
|
value_date DATE,
|
|
reference VARCHAR(100),
|
|
description TEXT,
|
|
|
|
-- Montos
|
|
debit_amount DECIMAL(18,2) DEFAULT 0,
|
|
credit_amount DECIMAL(18,2) DEFAULT 0,
|
|
balance_after DECIMAL(18,2),
|
|
|
|
-- Origen (del estado de cuenta)
|
|
bank_reference VARCHAR(100),
|
|
import_batch_id UUID,
|
|
|
|
-- Conciliacion
|
|
reconciliation_status finance.reconciliation_status DEFAULT 'pending',
|
|
reconciled_with_id UUID, -- ID de pago/cobro conciliado
|
|
reconciled_with_type VARCHAR(30), -- ap_payment, ar_collection, entry_line
|
|
reconciled_at TIMESTAMP,
|
|
reconciled_by UUID REFERENCES core.users(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_bank_movements_account ON finance.bank_movements(bank_account_id);
|
|
CREATE INDEX idx_bank_movements_date ON finance.bank_movements(movement_date);
|
|
CREATE INDEX idx_bank_movements_status ON finance.bank_movements(reconciliation_status);
|
|
CREATE INDEX idx_bank_movements_pending ON finance.bank_movements(bank_account_id)
|
|
WHERE reconciliation_status = 'pending';
|
|
```
|
|
|
|
### 6.3 bank_reconciliations (Conciliaciones)
|
|
|
|
```sql
|
|
CREATE TABLE finance.bank_reconciliations (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
bank_account_id UUID NOT NULL REFERENCES finance.bank_accounts(id),
|
|
|
|
-- Periodo
|
|
period_start DATE NOT NULL,
|
|
period_end DATE NOT NULL,
|
|
|
|
-- Saldos
|
|
bank_opening_balance DECIMAL(18,2) NOT NULL,
|
|
bank_closing_balance DECIMAL(18,2) NOT NULL,
|
|
book_opening_balance DECIMAL(18,2) NOT NULL,
|
|
book_closing_balance DECIMAL(18,2) NOT NULL,
|
|
|
|
-- Partidas en conciliacion
|
|
deposits_in_transit DECIMAL(18,2) DEFAULT 0,
|
|
checks_in_transit DECIMAL(18,2) DEFAULT 0,
|
|
bank_errors DECIMAL(18,2) DEFAULT 0,
|
|
book_errors DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Resultado
|
|
reconciled_balance DECIMAL(18,2),
|
|
difference DECIMAL(18,2),
|
|
is_reconciled BOOLEAN DEFAULT false,
|
|
|
|
-- Estado
|
|
status VARCHAR(20) DEFAULT 'draft', -- draft, in_progress, completed, approved
|
|
|
|
-- Aprobacion
|
|
approved_at TIMESTAMP,
|
|
approved_by UUID REFERENCES core.users(id),
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_reconciliations_account ON finance.bank_reconciliations(bank_account_id);
|
|
CREATE INDEX idx_reconciliations_period ON finance.bank_reconciliations(period_end);
|
|
```
|
|
|
|
---
|
|
|
|
## 7. Tablas de Cash Flow
|
|
|
|
### 7.1 cash_flow_projections (Proyecciones)
|
|
|
|
```sql
|
|
CREATE TABLE finance.cash_flow_projections (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
project_id UUID REFERENCES construction.projects(id),
|
|
|
|
-- Periodo
|
|
period_date DATE NOT NULL,
|
|
period_type VARCHAR(20) NOT NULL, -- daily, weekly, monthly
|
|
|
|
-- Ingresos proyectados
|
|
projected_income DECIMAL(18,2) DEFAULT 0,
|
|
projected_collections DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Egresos proyectados
|
|
projected_expenses DECIMAL(18,2) DEFAULT 0,
|
|
projected_payments DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Reales (actualizados)
|
|
actual_income DECIMAL(18,2) DEFAULT 0,
|
|
actual_expenses DECIMAL(18,2) DEFAULT 0,
|
|
|
|
-- Saldos
|
|
opening_balance DECIMAL(18,2) DEFAULT 0,
|
|
projected_closing DECIMAL(18,2) DEFAULT 0,
|
|
actual_closing DECIMAL(18,2),
|
|
|
|
-- Clasificacion
|
|
cash_flow_type finance.cash_flow_type DEFAULT 'operating',
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id),
|
|
|
|
CONSTRAINT uq_cash_flow_projection UNIQUE (tenant_id, project_id, period_date, period_type)
|
|
);
|
|
|
|
CREATE INDEX idx_cash_flow_tenant ON finance.cash_flow_projections(tenant_id);
|
|
CREATE INDEX idx_cash_flow_project ON finance.cash_flow_projections(project_id);
|
|
CREATE INDEX idx_cash_flow_date ON finance.cash_flow_projections(period_date);
|
|
```
|
|
|
|
### 7.2 cash_flow_items (Detalle de Proyeccion)
|
|
|
|
```sql
|
|
CREATE TABLE finance.cash_flow_items (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
tenant_id UUID NOT NULL REFERENCES core.tenants(id),
|
|
projection_id UUID NOT NULL REFERENCES finance.cash_flow_projections(id),
|
|
|
|
-- Tipo
|
|
item_type VARCHAR(30) NOT NULL, -- income, expense
|
|
category VARCHAR(50),
|
|
|
|
-- Descripcion
|
|
description TEXT NOT NULL,
|
|
|
|
-- Origen
|
|
source_type VARCHAR(30), -- estimation, purchase_order, contract, payroll
|
|
source_id UUID,
|
|
|
|
-- Montos
|
|
projected_amount DECIMAL(18,2) NOT NULL,
|
|
actual_amount DECIMAL(18,2),
|
|
|
|
-- Fecha esperada
|
|
expected_date DATE,
|
|
actual_date DATE,
|
|
|
|
-- Auditoria
|
|
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES core.users(id),
|
|
updated_at TIMESTAMP,
|
|
updated_by UUID REFERENCES core.users(id)
|
|
);
|
|
|
|
CREATE INDEX idx_cash_flow_items_projection ON finance.cash_flow_items(projection_id);
|
|
CREATE INDEX idx_cash_flow_items_type ON finance.cash_flow_items(item_type);
|
|
CREATE INDEX idx_cash_flow_items_source ON finance.cash_flow_items(source_type, source_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 8. Funciones
|
|
|
|
### 8.1 Calcular Totales de Poliza
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION finance.calculate_entry_totals()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE finance.accounting_entries ae
|
|
SET
|
|
total_debit = (
|
|
SELECT COALESCE(SUM(debit_amount), 0)
|
|
FROM finance.accounting_entry_lines
|
|
WHERE entry_id = ae.id
|
|
),
|
|
total_credit = (
|
|
SELECT COALESCE(SUM(credit_amount), 0)
|
|
FROM finance.accounting_entry_lines
|
|
WHERE entry_id = ae.id
|
|
),
|
|
is_balanced = (
|
|
SELECT COALESCE(SUM(debit_amount), 0) = COALESCE(SUM(credit_amount), 0)
|
|
FROM finance.accounting_entry_lines
|
|
WHERE entry_id = ae.id
|
|
),
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = COALESCE(NEW.entry_id, OLD.entry_id);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_calculate_entry_totals
|
|
AFTER INSERT OR UPDATE OR DELETE ON finance.accounting_entry_lines
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION finance.calculate_entry_totals();
|
|
```
|
|
|
|
### 8.2 Actualizar Saldo de Cuenta por Pagar
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION finance.update_ap_balance()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE finance.accounts_payable ap
|
|
SET
|
|
paid_amount = (
|
|
SELECT COALESCE(SUM(amount), 0)
|
|
FROM finance.ap_payments
|
|
WHERE payable_id = ap.id AND status = 'completed'
|
|
),
|
|
status = CASE
|
|
WHEN paid_amount >= total_amount THEN 'paid'
|
|
WHEN paid_amount > 0 THEN 'partial'
|
|
WHEN due_date < CURRENT_DATE THEN 'overdue'
|
|
ELSE 'pending'
|
|
END,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = COALESCE(NEW.payable_id, OLD.payable_id);
|
|
|
|
RETURN NEW;
|
|
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();
|
|
```
|
|
|
|
### 8.3 Actualizar Saldo de Cuenta por Cobrar
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION finance.update_ar_balance()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE finance.accounts_receivable ar
|
|
SET
|
|
collected_amount = (
|
|
SELECT COALESCE(SUM(amount), 0)
|
|
FROM finance.ar_collections
|
|
WHERE receivable_id = ar.id AND status = 'completed'
|
|
),
|
|
status = CASE
|
|
WHEN collected_amount >= total_amount THEN 'paid'
|
|
WHEN collected_amount > 0 THEN 'partial'
|
|
WHEN due_date < CURRENT_DATE THEN 'overdue'
|
|
ELSE 'pending'
|
|
END,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = COALESCE(NEW.receivable_id, OLD.receivable_id);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER trg_update_ar_balance
|
|
AFTER INSERT OR UPDATE OR DELETE ON finance.ar_collections
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION finance.update_ar_balance();
|
|
```
|
|
|
|
### 8.4 Generar Poliza desde Compra
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION finance.create_entry_from_purchase(
|
|
p_tenant_id UUID,
|
|
p_purchase_id UUID,
|
|
p_created_by UUID
|
|
)
|
|
RETURNS UUID AS $$
|
|
DECLARE
|
|
v_entry_id UUID;
|
|
v_purchase RECORD;
|
|
v_entry_number VARCHAR(30);
|
|
BEGIN
|
|
-- Obtener datos de la compra
|
|
SELECT * INTO v_purchase
|
|
FROM purchasing.purchase_orders
|
|
WHERE id = p_purchase_id;
|
|
|
|
-- Generar numero de poliza
|
|
SELECT 'POL-' || TO_CHAR(CURRENT_DATE, 'YYYY') || '-' ||
|
|
LPAD((COUNT(*) + 1)::TEXT, 6, '0')
|
|
INTO v_entry_number
|
|
FROM finance.accounting_entries
|
|
WHERE tenant_id = p_tenant_id
|
|
AND fiscal_year = EXTRACT(YEAR FROM CURRENT_DATE);
|
|
|
|
-- Crear poliza
|
|
INSERT INTO finance.accounting_entries (
|
|
tenant_id, entry_number, entry_type, entry_date,
|
|
description, reference, source_module, source_id,
|
|
project_id, fiscal_year, fiscal_period, created_by
|
|
)
|
|
VALUES (
|
|
p_tenant_id,
|
|
v_entry_number,
|
|
'expense',
|
|
CURRENT_DATE,
|
|
'Compra: ' || v_purchase.po_number,
|
|
v_purchase.po_number,
|
|
'purchases',
|
|
p_purchase_id,
|
|
v_purchase.project_id,
|
|
EXTRACT(YEAR FROM CURRENT_DATE),
|
|
EXTRACT(MONTH FROM CURRENT_DATE),
|
|
p_created_by
|
|
)
|
|
RETURNING id INTO v_entry_id;
|
|
|
|
-- TODO: Crear lineas de poliza segun configuracion de cuentas
|
|
|
|
RETURN v_entry_id;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
### 8.5 Calcular Aging de Cuentas
|
|
|
|
```sql
|
|
CREATE OR REPLACE FUNCTION finance.calculate_ap_aging(p_tenant_id UUID, p_as_of_date DATE DEFAULT CURRENT_DATE)
|
|
RETURNS TABLE (
|
|
supplier_id UUID,
|
|
supplier_name VARCHAR,
|
|
current_amount DECIMAL,
|
|
days_1_30 DECIMAL,
|
|
days_31_60 DECIMAL,
|
|
days_61_90 DECIMAL,
|
|
days_over_90 DECIMAL,
|
|
total_amount DECIMAL
|
|
) AS $$
|
|
BEGIN
|
|
RETURN QUERY
|
|
SELECT
|
|
ap.supplier_id,
|
|
ap.supplier_name,
|
|
SUM(CASE WHEN ap.due_date >= p_as_of_date THEN ap.balance ELSE 0 END) as current_amount,
|
|
SUM(CASE WHEN ap.due_date BETWEEN p_as_of_date - 30 AND p_as_of_date - 1 THEN ap.balance ELSE 0 END) as days_1_30,
|
|
SUM(CASE WHEN ap.due_date BETWEEN p_as_of_date - 60 AND p_as_of_date - 31 THEN ap.balance ELSE 0 END) as days_31_60,
|
|
SUM(CASE WHEN ap.due_date BETWEEN p_as_of_date - 90 AND p_as_of_date - 61 THEN ap.balance ELSE 0 END) as days_61_90,
|
|
SUM(CASE WHEN ap.due_date < p_as_of_date - 90 THEN ap.balance ELSE 0 END) as days_over_90,
|
|
SUM(ap.balance) as total_amount
|
|
FROM finance.accounts_payable ap
|
|
WHERE ap.tenant_id = p_tenant_id
|
|
AND ap.status IN ('pending', 'partial', 'overdue')
|
|
AND ap.deleted_at IS NULL
|
|
GROUP BY ap.supplier_id, ap.supplier_name;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Row Level Security
|
|
|
|
```sql
|
|
-- Habilitar RLS en todas las tablas
|
|
ALTER TABLE finance.chart_of_accounts ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE finance.cost_centers 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.ap_payments ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE finance.accounts_receivable ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE finance.ar_collections 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.cash_flow_projections ENABLE ROW LEVEL SECURITY;
|
|
ALTER TABLE finance.cash_flow_items ENABLE ROW LEVEL SECURITY;
|
|
|
|
-- Crear politicas de aislamiento por tenant
|
|
DO $$
|
|
DECLARE
|
|
t TEXT;
|
|
BEGIN
|
|
FOR t IN SELECT tablename FROM pg_tables WHERE schemaname = 'finance'
|
|
LOOP
|
|
EXECUTE format('
|
|
CREATE POLICY tenant_isolation ON finance.%I
|
|
USING (tenant_id = current_setting(''app.current_tenant_id'')::uuid)
|
|
', t);
|
|
END LOOP;
|
|
END $$;
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Seeds Iniciales
|
|
|
|
```sql
|
|
-- Catalogo de cuentas base
|
|
INSERT INTO finance.chart_of_accounts (tenant_id, code, name, account_type, nature, level)
|
|
VALUES
|
|
-- Activos
|
|
('{{TENANT_ID}}', '1000', 'ACTIVO', 'asset', 'debit', 1),
|
|
('{{TENANT_ID}}', '1100', 'Activo Circulante', 'asset', 'debit', 2),
|
|
('{{TENANT_ID}}', '1101', 'Bancos', 'asset', 'debit', 3),
|
|
('{{TENANT_ID}}', '1102', 'Cuentas por Cobrar', 'asset', 'debit', 3),
|
|
('{{TENANT_ID}}', '1103', 'Inventarios', 'asset', 'debit', 3),
|
|
-- Pasivos
|
|
('{{TENANT_ID}}', '2000', 'PASIVO', 'liability', 'credit', 1),
|
|
('{{TENANT_ID}}', '2100', 'Pasivo a Corto Plazo', 'liability', 'credit', 2),
|
|
('{{TENANT_ID}}', '2101', 'Proveedores', 'liability', 'credit', 3),
|
|
('{{TENANT_ID}}', '2102', 'Acreedores Diversos', 'liability', 'credit', 3),
|
|
-- Capital
|
|
('{{TENANT_ID}}', '3000', 'CAPITAL', 'equity', 'credit', 1),
|
|
('{{TENANT_ID}}', '3101', 'Capital Social', 'equity', 'credit', 2),
|
|
-- Ingresos
|
|
('{{TENANT_ID}}', '4000', 'INGRESOS', 'income', 'credit', 1),
|
|
('{{TENANT_ID}}', '4101', 'Ingresos por Obra', 'income', 'credit', 2),
|
|
-- Gastos
|
|
('{{TENANT_ID}}', '5000', 'GASTOS', 'expense', 'debit', 1),
|
|
('{{TENANT_ID}}', '5100', 'Costo de Ventas', 'expense', 'debit', 2),
|
|
('{{TENANT_ID}}', '5101', 'Materiales', 'expense', 'debit', 3),
|
|
('{{TENANT_ID}}', '5102', 'Mano de Obra', 'expense', 'debit', 3),
|
|
('{{TENANT_ID}}', '5103', 'Subcontratos', 'expense', 'debit', 3);
|
|
```
|
|
|
|
---
|
|
|
|
## Referencias
|
|
|
|
- [MAE-014: Finanzas y Controlling](../../02-definicion-modulos/MAE-014-finanzas-controlling/)
|
|
- [EPIC-MAE-014](../../08-epicas/EPIC-MAE-014-finanzas.md)
|
|
- [ADR-007: Database Design](../../97-adr/ADR-007-database-design.md)
|
|
|
|
---
|
|
|
|
*Ultima actualizacion: 2025-12-05*
|