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>
175 lines
7.4 KiB
PL/PgSQL
175 lines
7.4 KiB
PL/PgSQL
-- =============================================================
|
|
-- ARCHIVO: 55-financial-payments.sql
|
|
-- DESCRIPCION: Pagos contables (cobros y pagos)
|
|
-- 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 pagos desde perspectiva CONTABLE.
|
|
-- Para pagos operativos ver 24-invoices.sql (schema billing)
|
|
-- =============================================================
|
|
|
|
-- =====================
|
|
-- TABLA: payments
|
|
-- Pagos contables (cobros entrantes y pagos salientes)
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS financial.payments (
|
|
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 pago
|
|
payment_type financial.payment_type_enum NOT NULL, -- inbound (cobro), outbound (pago)
|
|
|
|
-- Metodo de pago
|
|
payment_method financial.payment_method_enum NOT NULL,
|
|
|
|
-- Monto
|
|
amount DECIMAL(15, 2) NOT NULL CHECK (amount > 0),
|
|
|
|
-- Moneda
|
|
currency_id UUID, -- FK a catalogo de monedas
|
|
|
|
-- Fecha de pago
|
|
payment_date DATE NOT NULL,
|
|
|
|
-- Referencia
|
|
ref VARCHAR(255), -- Numero de cheque, referencia bancaria, etc.
|
|
|
|
-- Estado
|
|
status financial.payment_status_enum DEFAULT 'draft',
|
|
|
|
-- 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),
|
|
|
|
-- Publicacion/Contabilizacion
|
|
posted_at TIMESTAMPTZ,
|
|
posted_by UUID REFERENCES auth.users(id)
|
|
);
|
|
|
|
-- Indices para payments
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_tenant ON financial.payments(tenant_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_company ON financial.payments(company_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_partner ON financial.payments(partner_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_type ON financial.payments(payment_type);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_method ON financial.payments(payment_method);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_date ON financial.payments(payment_date);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_status ON financial.payments(status);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_journal ON financial.payments(journal_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_entry ON financial.payments(journal_entry_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_ref ON financial.payments(ref) WHERE ref IS NOT NULL;
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_posted ON financial.payments(tenant_id, status) WHERE status = 'posted';
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_inbound ON financial.payments(tenant_id, partner_id, payment_type) WHERE payment_type = 'inbound';
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_outbound ON financial.payments(tenant_id, partner_id, payment_type) WHERE payment_type = 'outbound';
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payments_date_range ON financial.payments(tenant_id, payment_date, status);
|
|
|
|
-- =====================
|
|
-- TABLA: payment_invoice_allocations
|
|
-- Aplicacion de pagos a facturas
|
|
-- =====================
|
|
CREATE TABLE IF NOT EXISTS financial.payment_invoice_allocations (
|
|
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
|
|
|
|
-- Pago
|
|
payment_id UUID NOT NULL REFERENCES financial.payments(id) ON DELETE CASCADE,
|
|
|
|
-- Factura
|
|
invoice_id UUID NOT NULL REFERENCES financial.invoices(id) ON DELETE CASCADE,
|
|
|
|
-- Monto aplicado a esta factura
|
|
amount DECIMAL(15, 2) NOT NULL CHECK (amount > 0),
|
|
|
|
-- Fecha de aplicacion
|
|
allocation_date DATE NOT NULL DEFAULT CURRENT_DATE,
|
|
|
|
-- Audit columns
|
|
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
|
created_by UUID REFERENCES auth.users(id),
|
|
|
|
-- Un pago solo puede aplicarse una vez a cada factura
|
|
UNIQUE(payment_id, invoice_id)
|
|
);
|
|
|
|
-- Indices para payment_invoice_allocations
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payment_allocations_payment ON financial.payment_invoice_allocations(payment_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payment_allocations_invoice ON financial.payment_invoice_allocations(invoice_id);
|
|
CREATE INDEX IF NOT EXISTS idx_financial_payment_allocations_date ON financial.payment_invoice_allocations(allocation_date);
|
|
|
|
-- =====================
|
|
-- FUNCION: Actualizar amount_paid en factura
|
|
-- =====================
|
|
CREATE OR REPLACE FUNCTION financial.update_invoice_amount_paid()
|
|
RETURNS TRIGGER AS $$
|
|
DECLARE
|
|
v_invoice_id UUID;
|
|
v_total_paid DECIMAL(15, 2);
|
|
BEGIN
|
|
-- Determinar la factura afectada
|
|
IF TG_OP = 'DELETE' THEN
|
|
v_invoice_id := OLD.invoice_id;
|
|
ELSE
|
|
v_invoice_id := NEW.invoice_id;
|
|
END IF;
|
|
|
|
-- Calcular total pagado para la factura
|
|
SELECT COALESCE(SUM(amount), 0)
|
|
INTO v_total_paid
|
|
FROM financial.payment_invoice_allocations
|
|
WHERE invoice_id = v_invoice_id;
|
|
|
|
-- Actualizar factura
|
|
UPDATE financial.invoices
|
|
SET
|
|
amount_paid = v_total_paid,
|
|
status = CASE
|
|
WHEN v_total_paid >= amount_total THEN 'paid'::financial.invoice_status_enum
|
|
WHEN v_total_paid > 0 THEN 'open'::financial.invoice_status_enum
|
|
ELSE status
|
|
END,
|
|
updated_at = CURRENT_TIMESTAMP
|
|
WHERE id = v_invoice_id;
|
|
|
|
RETURN COALESCE(NEW, OLD);
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Trigger para actualizar amount_paid automaticamente
|
|
DROP TRIGGER IF EXISTS trg_update_invoice_amount_paid ON financial.payment_invoice_allocations;
|
|
CREATE TRIGGER trg_update_invoice_amount_paid
|
|
AFTER INSERT OR UPDATE OR DELETE ON financial.payment_invoice_allocations
|
|
FOR EACH ROW
|
|
EXECUTE FUNCTION financial.update_invoice_amount_paid();
|
|
|
|
-- =====================
|
|
-- COMENTARIOS
|
|
-- =====================
|
|
COMMENT ON TABLE financial.payments IS 'Pagos contables (cobros y pagos a proveedores)';
|
|
COMMENT ON COLUMN financial.payments.payment_type IS 'Tipo: inbound (cobro de cliente), outbound (pago a proveedor)';
|
|
COMMENT ON COLUMN financial.payments.payment_method IS 'Metodo: cash, bank_transfer, check, card, other';
|
|
COMMENT ON COLUMN financial.payments.amount IS 'Monto del pago (siempre positivo)';
|
|
COMMENT ON COLUMN financial.payments.ref IS 'Referencia: numero de cheque, referencia bancaria, etc.';
|
|
COMMENT ON COLUMN financial.payments.status IS 'Estado: draft, posted (contabilizado), reconciled, cancelled';
|
|
COMMENT ON COLUMN financial.payments.journal_entry_id IS 'Asiento contable generado al publicar el pago';
|
|
COMMENT ON COLUMN financial.payments.posted_at IS 'Fecha y hora de publicacion/contabilizacion';
|
|
|
|
COMMENT ON TABLE financial.payment_invoice_allocations IS 'Aplicacion de pagos a facturas especificas';
|
|
COMMENT ON COLUMN financial.payment_invoice_allocations.amount IS 'Monto del pago aplicado a esta factura';
|
|
COMMENT ON COLUMN financial.payment_invoice_allocations.allocation_date IS 'Fecha de aplicacion del pago';
|
|
|
|
COMMENT ON FUNCTION financial.update_invoice_amount_paid() IS 'Actualiza automaticamente amount_paid en facturas cuando se aplican pagos';
|