-- =============================================================
-- ARCHIVO: 24-invoices.sql
-- DESCRIPCION: Facturas de venta/compra y pagos
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-01-13
-- DEPENDE DE: 16-partners.sql, 22-sales.sql, 23-purchases.sql
-- =============================================================

-- =====================
-- SCHEMA: billing
-- =====================
CREATE SCHEMA IF NOT EXISTS billing;

-- =====================
-- TABLA: invoices
-- Facturas de venta y compra
-- =====================
CREATE TABLE IF NOT EXISTS billing.invoices (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Identificacion
    invoice_number VARCHAR(30) NOT NULL,
    invoice_type VARCHAR(20) NOT NULL DEFAULT 'sale', -- sale (venta), purchase (compra), credit_note, debit_note

    -- Referencia a origen
    sales_order_id UUID REFERENCES sales.sales_orders(id),
    purchase_order_id UUID REFERENCES purchases.purchase_orders(id),

    -- Partner
    partner_id UUID NOT NULL REFERENCES partners.partners(id) ON DELETE RESTRICT,
    partner_name VARCHAR(200),
    partner_tax_id VARCHAR(50),

    -- Direcciones
    billing_address JSONB,

    -- Fechas
    invoice_date DATE NOT NULL DEFAULT CURRENT_DATE,
    due_date DATE,
    payment_date DATE,            -- Fecha real de pago

    -- Totales
    currency VARCHAR(3) DEFAULT 'MXN',
    exchange_rate DECIMAL(10, 6) DEFAULT 1,
    subtotal DECIMAL(15, 2) NOT NULL DEFAULT 0,
    tax_amount DECIMAL(15, 2) NOT NULL DEFAULT 0,
    withholding_tax DECIMAL(15, 2) DEFAULT 0,   -- Retenciones
    discount_amount DECIMAL(15, 2) NOT NULL DEFAULT 0,
    total DECIMAL(15, 2) NOT NULL DEFAULT 0,

    -- Pagos
    amount_paid DECIMAL(15, 2) DEFAULT 0,
    amount_due DECIMAL(15, 2) GENERATED ALWAYS AS (total - COALESCE(amount_paid, 0)) STORED,

    -- Terminos
    payment_term_days INTEGER DEFAULT 0,
    payment_method VARCHAR(50),

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, validated, sent, partial, paid, cancelled, voided

    -- CFDI (Facturacion electronica Mexico)
    cfdi_uuid VARCHAR(40),        -- UUID del CFDI
    cfdi_status VARCHAR(20),      -- pending, stamped, cancelled
    cfdi_xml TEXT,                -- XML del CFDI
    cfdi_pdf_url VARCHAR(500),

    -- Notas
    notes TEXT,
    internal_notes TEXT,

    -- Metadata
    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),
    deleted_at TIMESTAMPTZ,

    UNIQUE(tenant_id, invoice_number)
);

-- Indices para invoices
CREATE INDEX IF NOT EXISTS idx_invoices_tenant ON billing.invoices(tenant_id);
CREATE INDEX IF NOT EXISTS idx_invoices_number ON billing.invoices(invoice_number);
CREATE INDEX IF NOT EXISTS idx_invoices_type ON billing.invoices(invoice_type);
CREATE INDEX IF NOT EXISTS idx_invoices_partner ON billing.invoices(partner_id);
CREATE INDEX IF NOT EXISTS idx_invoices_sales_order ON billing.invoices(sales_order_id);
CREATE INDEX IF NOT EXISTS idx_invoices_purchase_order ON billing.invoices(purchase_order_id);
CREATE INDEX IF NOT EXISTS idx_invoices_status ON billing.invoices(status);
CREATE INDEX IF NOT EXISTS idx_invoices_date ON billing.invoices(invoice_date);
CREATE INDEX IF NOT EXISTS idx_invoices_due_date ON billing.invoices(due_date);
CREATE INDEX IF NOT EXISTS idx_invoices_cfdi ON billing.invoices(cfdi_uuid);
CREATE INDEX IF NOT EXISTS idx_invoices_unpaid ON billing.invoices(status) WHERE status IN ('validated', 'sent', 'partial');

-- =====================
-- TABLA: invoice_items
-- Lineas de factura
-- =====================
CREATE TABLE IF NOT EXISTS billing.invoice_items (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    invoice_id UUID NOT NULL REFERENCES billing.invoices(id) ON DELETE CASCADE,
    product_id UUID REFERENCES products.products(id) ON DELETE SET NULL,

    -- Linea
    line_number INTEGER NOT NULL DEFAULT 1,

    -- Producto
    product_sku VARCHAR(50),
    product_name VARCHAR(200) NOT NULL,
    description TEXT,

    -- SAT (Mexico)
    sat_product_code VARCHAR(20), -- Clave de producto SAT
    sat_unit_code VARCHAR(10),    -- Clave de unidad SAT

    -- Cantidad y precio
    quantity DECIMAL(15, 4) NOT NULL DEFAULT 1,
    uom VARCHAR(20) DEFAULT 'PZA',
    unit_price DECIMAL(15, 4) NOT NULL DEFAULT 0,

    -- Descuentos
    discount_percent DECIMAL(5, 2) DEFAULT 0,
    discount_amount DECIMAL(15, 2) DEFAULT 0,

    -- Impuestos
    tax_rate DECIMAL(5, 2) DEFAULT 16.00,
    tax_amount DECIMAL(15, 2) DEFAULT 0,
    withholding_rate DECIMAL(5, 2) DEFAULT 0,
    withholding_amount DECIMAL(15, 2) DEFAULT 0,

    -- Totales
    subtotal DECIMAL(15, 2) NOT NULL DEFAULT 0,
    total DECIMAL(15, 2) NOT NULL DEFAULT 0,

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
);

-- Indices para invoice_items
CREATE INDEX IF NOT EXISTS idx_invoice_items_invoice ON billing.invoice_items(invoice_id);
CREATE INDEX IF NOT EXISTS idx_invoice_items_product ON billing.invoice_items(product_id);
CREATE INDEX IF NOT EXISTS idx_invoice_items_line ON billing.invoice_items(invoice_id, line_number);

-- =====================
-- TABLA: payments
-- Pagos recibidos y realizados
-- =====================
CREATE TABLE IF NOT EXISTS billing.payments (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,

    -- Identificacion
    payment_number VARCHAR(30) NOT NULL,
    payment_type VARCHAR(20) NOT NULL DEFAULT 'received', -- received (cobro), made (pago)

    -- Partner
    partner_id UUID NOT NULL REFERENCES partners.partners(id) ON DELETE RESTRICT,
    partner_name VARCHAR(200),

    -- Monto
    currency VARCHAR(3) DEFAULT 'MXN',
    amount DECIMAL(15, 2) NOT NULL,
    exchange_rate DECIMAL(10, 6) DEFAULT 1,

    -- Fecha
    payment_date DATE NOT NULL DEFAULT CURRENT_DATE,

    -- Metodo de pago
    payment_method VARCHAR(50) NOT NULL, -- cash, transfer, check, credit_card, debit_card
    reference VARCHAR(100),              -- Numero de referencia, cheque, etc.

    -- Cuenta bancaria
    bank_account_id UUID REFERENCES partners.partner_bank_accounts(id),

    -- Estado
    status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, confirmed, reconciled, cancelled

    -- Notas
    notes TEXT,

    -- CFDI de pago (Mexico)
    cfdi_uuid VARCHAR(40),
    cfdi_status VARCHAR(20),

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),
    updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    deleted_at TIMESTAMPTZ,

    UNIQUE(tenant_id, payment_number)
);

-- Indices para payments
CREATE INDEX IF NOT EXISTS idx_payments_tenant ON billing.payments(tenant_id);
CREATE INDEX IF NOT EXISTS idx_payments_number ON billing.payments(payment_number);
CREATE INDEX IF NOT EXISTS idx_payments_type ON billing.payments(payment_type);
CREATE INDEX IF NOT EXISTS idx_payments_partner ON billing.payments(partner_id);
CREATE INDEX IF NOT EXISTS idx_payments_status ON billing.payments(status);
CREATE INDEX IF NOT EXISTS idx_payments_date ON billing.payments(payment_date);
CREATE INDEX IF NOT EXISTS idx_payments_method ON billing.payments(payment_method);

-- =====================
-- TABLA: payment_allocations
-- Aplicacion de pagos a facturas
-- =====================
CREATE TABLE IF NOT EXISTS billing.payment_allocations (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    payment_id UUID NOT NULL REFERENCES billing.payments(id) ON DELETE CASCADE,
    invoice_id UUID NOT NULL REFERENCES billing.invoices(id) ON DELETE CASCADE,

    -- Monto aplicado
    amount DECIMAL(15, 2) NOT NULL,

    -- Fecha de aplicacion
    allocation_date DATE NOT NULL DEFAULT CURRENT_DATE,

    -- Metadata
    created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
    created_by UUID REFERENCES auth.users(id),

    UNIQUE(payment_id, invoice_id)
);

-- Indices para payment_allocations
CREATE INDEX IF NOT EXISTS idx_payment_allocations_payment ON billing.payment_allocations(payment_id);
CREATE INDEX IF NOT EXISTS idx_payment_allocations_invoice ON billing.payment_allocations(invoice_id);

-- =====================
-- COMENTARIOS
-- =====================
COMMENT ON TABLE billing.invoices IS 'Facturas de venta y compra';
COMMENT ON COLUMN billing.invoices.invoice_type IS 'Tipo: sale (venta), purchase (compra), credit_note (nota credito), debit_note (nota debito)';
COMMENT ON COLUMN billing.invoices.status IS 'Estado: draft, validated, sent, partial (pago parcial), paid, cancelled, voided';
COMMENT ON COLUMN billing.invoices.cfdi_uuid IS 'UUID del CFDI para facturacion electronica en Mexico';
COMMENT ON COLUMN billing.invoices.amount_due IS 'Saldo pendiente de pago (calculado)';

COMMENT ON TABLE billing.invoice_items IS 'Lineas de detalle de facturas';
COMMENT ON COLUMN billing.invoice_items.sat_product_code IS 'Clave de producto del catalogo SAT (Mexico)';
COMMENT ON COLUMN billing.invoice_items.sat_unit_code IS 'Clave de unidad del catalogo SAT (Mexico)';

COMMENT ON TABLE billing.payments IS 'Registro de pagos recibidos y realizados';
COMMENT ON COLUMN billing.payments.payment_type IS 'Tipo: received (cobro a cliente), made (pago a proveedor)';
COMMENT ON COLUMN billing.payments.status IS 'Estado: draft, confirmed, reconciled, cancelled';

COMMENT ON TABLE billing.payment_allocations IS 'Aplicacion de pagos a facturas especificas';
COMMENT ON COLUMN billing.payment_allocations.amount IS 'Monto del pago aplicado a esta factura';
