Financial Schema (50-57): - 50-financial-schema.sql: Schema + 10 ENUMs - 51-financial-accounts.sql: account_types, accounts, account_mappings - 52-financial-journals.sql: fiscal_years, fiscal_periods, journals - 53-financial-entries.sql: journal_entries, journal_entry_lines - 54-financial-invoices.sql: invoices, invoice_lines - 55-financial-payments.sql: payments, payment_invoice_allocations - 56-financial-taxes.sql: taxes, tax_groups - 57-financial-bank-reconciliation.sql: bank_statements, bank_statement_lines, rules Purchases Matching (46): - 46-purchases-matching.sql: purchase_order_matching, purchase_matching_lines, matching_exceptions Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
168 lines
7.7 KiB
SQL
168 lines
7.7 KiB
SQL
-- =============================================================
|
|
-- ARCHIVO: 54-financial-invoices.sql
|
|
-- DESCRIPCION: Facturas contables (cliente/proveedor) y lineas de factura
|
|
-- VERSION: 1.0.0
|
|
-- PROYECTO: ERP-Core V2
|
|
-- FECHA: 2026-01-20
|
|
-- DEPENDE DE: 50-financial-schema.sql, 51-financial-accounts.sql, 52-financial-journals.sql, 53-financial-entries.sql
|
|
-- NOTA: Este modulo es para facturas desde perspectiva CONTABLE.
|
|
-- Para facturacion operativa ver 24-invoices.sql (schema billing)
|
|
-- =============================================================
|
|
|
|
-- =====================
|
|
-- TABLA: invoices
|
|
-- Facturas contables (cliente y proveedor)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS financial.invoices (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
|
|
-- Multi-tenant
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
company_id UUID,
|
|
|
|
-- Partner (cliente o proveedor)
|
|
partner_id UUID NOT NULL, -- FK a partners.partners
|
|
|
|
-- Tipo de factura
|
|
invoice_type financial.invoice_type_enum NOT NULL,
|
|
|
|
-- Identificacion
|
|
number VARCHAR(100) NOT NULL, -- Numero de factura
|
|
ref VARCHAR(255), -- Referencia externa
|
|
|
|
-- Fechas
|
|
invoice_date DATE NOT NULL,
|
|
due_date DATE,
|
|
|
|
-- Moneda
|
|
currency_id UUID, -- FK a catalogo de monedas
|
|
|
|
-- Montos
|
|
amount_untaxed DECIMAL(15, 2) DEFAULT 0, -- Subtotal sin impuestos
|
|
amount_tax DECIMAL(15, 2) DEFAULT 0, -- Total impuestos
|
|
amount_total DECIMAL(15, 2) DEFAULT 0, -- Total de la factura
|
|
amount_paid DECIMAL(15, 2) DEFAULT 0, -- Monto pagado
|
|
amount_residual DECIMAL(15, 2) GENERATED ALWAYS AS (amount_total - COALESCE(amount_paid, 0)) STORED, -- Saldo pendiente
|
|
|
|
-- Estado
|
|
status financial.invoice_status_enum DEFAULT 'draft',
|
|
|
|
-- Terminos de pago
|
|
payment_term_id UUID, -- FK a terminos de pago si existe
|
|
|
|
-- Relacion con contabilidad
|
|
journal_id UUID REFERENCES financial.journals(id) ON DELETE RESTRICT,
|
|
journal_entry_id UUID REFERENCES financial.journal_entries(id) ON DELETE SET NULL,
|
|
|
|
-- Notas
|
|
notes TEXT,
|
|
|
|
-- Audit columns
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_by UUID REFERENCES auth.users(id),
|
|
|
|
-- Validacion
|
|
validated_at TIMESTAMPTZ,
|
|
validated_by UUID REFERENCES auth.users(id),
|
|
|
|
-- Cancelacion
|
|
cancelled_at TIMESTAMPTZ,
|
|
cancelled_by UUID REFERENCES auth.users(id),
|
|
|
|
-- Unicidad
|
|
UNIQUE(tenant_id, number, invoice_type)
|
|
);
|
|
|
|
-- Indices para invoices
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_tenant ON financial.invoices(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_company ON financial.invoices(company_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_partner ON financial.invoices(partner_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_type ON financial.invoices(invoice_type);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_number ON financial.invoices(number);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_date ON financial.invoices(invoice_date);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_due_date ON financial.invoices(due_date);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_status ON financial.invoices(status);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_journal ON financial.invoices(journal_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_entry ON financial.invoices(journal_entry_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_open ON financial.invoices(tenant_id, status) WHERE status = 'open';
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_unpaid ON financial.invoices(tenant_id, due_date) WHERE status = 'open' AND amount_paid < amount_total;
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_customer ON financial.invoices(tenant_id, partner_id, invoice_type) WHERE invoice_type = 'customer';
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoices_supplier ON financial.invoices(tenant_id, partner_id, invoice_type) WHERE invoice_type = 'supplier';
|
|
|
|
-- =====================
|
|
-- TABLA: invoice_lines
|
|
-- Lineas de factura contable
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS financial.invoice_lines (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
|
|
-- Relacion con factura (cascade delete)
|
|
invoice_id UUID NOT NULL REFERENCES financial.invoices(id) ON DELETE CASCADE,
|
|
|
|
-- Multi-tenant (denormalizado para queries rapidas)
|
|
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
|
|
|
-- Producto (opcional)
|
|
product_id UUID, -- FK a products.products
|
|
|
|
-- Descripcion
|
|
description TEXT,
|
|
|
|
-- Cantidad y unidad
|
|
quantity DECIMAL(15, 4) NOT NULL DEFAULT 1,
|
|
uom_id UUID, -- FK a unidades de medida
|
|
|
|
-- Precio
|
|
price_unit DECIMAL(15, 2) NOT NULL DEFAULT 0,
|
|
|
|
-- Impuestos aplicables (array de UUIDs de taxes)
|
|
tax_ids UUID[] DEFAULT '{}',
|
|
|
|
-- Montos calculados
|
|
amount_untaxed DECIMAL(15, 2) DEFAULT 0, -- subtotal linea
|
|
amount_tax DECIMAL(15, 2) DEFAULT 0, -- impuestos linea
|
|
amount_total DECIMAL(15, 2) DEFAULT 0, -- total linea
|
|
|
|
-- Cuenta contable
|
|
account_id UUID REFERENCES financial.accounts(id) ON DELETE RESTRICT,
|
|
|
|
-- Audit columns
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES auth.users(id),
|
|
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
updated_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Indices para invoice_lines
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoice_lines_invoice ON financial.invoice_lines(invoice_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoice_lines_tenant ON financial.invoice_lines(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoice_lines_product ON financial.invoice_lines(product_id) WHERE product_id IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoice_lines_account ON financial.invoice_lines(account_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_invoice_lines_tax_ids ON financial.invoice_lines USING GIN(tax_ids);
|
|
|
|
-- =====================
|
|
-- COMENTARIOS
|
|
-- =====================
|
|
COMMENT ON TABLE financial.invoices IS 'Facturas contables (perspectiva financiera)';
|
|
COMMENT ON COLUMN financial.invoices.invoice_type IS 'Tipo: customer (venta a cliente), supplier (compra a proveedor)';
|
|
COMMENT ON COLUMN financial.invoices.number IS 'Numero unico de factura';
|
|
COMMENT ON COLUMN financial.invoices.ref IS 'Referencia externa (numero de factura del proveedor, etc.)';
|
|
COMMENT ON COLUMN financial.invoices.amount_untaxed IS 'Subtotal sin impuestos';
|
|
COMMENT ON COLUMN financial.invoices.amount_tax IS 'Total de impuestos';
|
|
COMMENT ON COLUMN financial.invoices.amount_total IS 'Total de la factura (subtotal + impuestos)';
|
|
COMMENT ON COLUMN financial.invoices.amount_paid IS 'Monto pagado hasta el momento';
|
|
COMMENT ON COLUMN financial.invoices.amount_residual IS 'Saldo pendiente de pago (calculado)';
|
|
COMMENT ON COLUMN financial.invoices.status IS 'Estado: draft, open (validada), paid, cancelled';
|
|
COMMENT ON COLUMN financial.invoices.journal_entry_id IS 'Asiento contable generado al validar la factura';
|
|
COMMENT ON COLUMN financial.invoices.validated_at IS 'Fecha y hora de validacion/apertura';
|
|
COMMENT ON COLUMN financial.invoices.cancelled_at IS 'Fecha y hora de cancelacion';
|
|
|
|
COMMENT ON TABLE financial.invoice_lines IS 'Lineas de detalle de facturas contables';
|
|
COMMENT ON COLUMN financial.invoice_lines.product_id IS 'Producto asociado (opcional)';
|
|
COMMENT ON COLUMN financial.invoice_lines.quantity IS 'Cantidad facturada';
|
|
COMMENT ON COLUMN financial.invoice_lines.price_unit IS 'Precio unitario';
|
|
COMMENT ON COLUMN financial.invoice_lines.tax_ids IS 'Array de IDs de impuestos aplicables';
|
|
COMMENT ON COLUMN financial.invoice_lines.account_id IS 'Cuenta contable para el asiento';
|