-- ============================================================= -- 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';