erp-core-database/ddl/56-financial-taxes.sql
rckrdmrd 4b6240311d [TASK-2026-01-20-003] feat: Add financial DDL and matching tables
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>
2026-01-20 03:47:04 -06:00

156 lines
6.7 KiB
PL/PgSQL

-- =============================================================
-- ARCHIVO: 56-financial-taxes.sql
-- DESCRIPCION: Impuestos contables (IVA, retenciones, etc.)
-- VERSION: 1.0.0
-- PROYECTO: ERP-Core V2
-- FECHA: 2026-01-20
-- DEPENDE DE: 50-financial-schema.sql, 51-financial-accounts.sql
-- =============================================================
-- =====================
-- TABLA: taxes
-- Catalogo de impuestos
-- =====================
CREATE TABLE IF NOT EXISTS financial.taxes (
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,
-- Identificacion
name VARCHAR(100) NOT NULL, -- Ej: "IVA 16%", "Retencion ISR 10%"
code VARCHAR(20) NOT NULL, -- Ej: "IVA16", "RET_ISR10"
-- Tipo de impuesto
tax_type financial.tax_type_enum NOT NULL DEFAULT 'all',
-- Tasa
amount DECIMAL(5, 2) NOT NULL, -- Porcentaje (ej: 16.00 para 16%)
-- Configuracion
included_in_price BOOLEAN DEFAULT FALSE, -- TRUE si el precio ya incluye el impuesto
-- Estado
active BOOLEAN DEFAULT TRUE,
-- Cuentas contables asociadas (opcional)
account_id UUID REFERENCES financial.accounts(id) ON DELETE SET NULL, -- Cuenta de impuesto
refund_account_id UUID REFERENCES financial.accounts(id) ON DELETE SET NULL, -- Cuenta para devoluciones
-- 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),
-- Unicidad por tenant
UNIQUE(tenant_id, code)
);
-- Indices para taxes
CREATE INDEX IF NOT EXISTS idx_financial_taxes_tenant ON financial.taxes(tenant_id);
CREATE INDEX IF NOT EXISTS idx_financial_taxes_company ON financial.taxes(company_id);
CREATE INDEX IF NOT EXISTS idx_financial_taxes_code ON financial.taxes(code);
CREATE INDEX IF NOT EXISTS idx_financial_taxes_type ON financial.taxes(tax_type);
CREATE INDEX IF NOT EXISTS idx_financial_taxes_active ON financial.taxes(tenant_id) WHERE active = TRUE;
CREATE INDEX IF NOT EXISTS idx_financial_taxes_sales ON financial.taxes(tenant_id, tax_type) WHERE tax_type IN ('sales', 'all') AND active = TRUE;
CREATE INDEX IF NOT EXISTS idx_financial_taxes_purchase ON financial.taxes(tenant_id, tax_type) WHERE tax_type IN ('purchase', 'all') AND active = TRUE;
CREATE INDEX IF NOT EXISTS idx_financial_taxes_account ON financial.taxes(account_id) WHERE account_id IS NOT NULL;
-- =====================
-- TABLA: tax_groups (opcional, para agrupar impuestos)
-- Grupos de impuestos para aplicacion conjunta
-- =====================
CREATE TABLE IF NOT EXISTS financial.tax_groups (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
-- Multi-tenant
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
-- Identificacion
name VARCHAR(100) NOT NULL,
code VARCHAR(20) NOT NULL,
-- Descripcion
description TEXT,
-- Impuestos en el grupo (array de IDs)
tax_ids UUID[] DEFAULT '{}',
-- Estado
active BOOLEAN DEFAULT TRUE,
-- 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),
UNIQUE(tenant_id, code)
);
-- Indices para tax_groups
CREATE INDEX IF NOT EXISTS idx_financial_tax_groups_tenant ON financial.tax_groups(tenant_id);
CREATE INDEX IF NOT EXISTS idx_financial_tax_groups_code ON financial.tax_groups(code);
CREATE INDEX IF NOT EXISTS idx_financial_tax_groups_active ON financial.tax_groups(tenant_id) WHERE active = TRUE;
CREATE INDEX IF NOT EXISTS idx_financial_tax_groups_tax_ids ON financial.tax_groups USING GIN(tax_ids);
-- =====================
-- DATOS SEMILLA: Impuestos comunes de Mexico
-- =====================
-- Nota: Estos se insertan condicionalmente. En produccion, los impuestos
-- se crean por tenant desde la aplicacion.
-- Funcion para insertar impuestos semilla
CREATE OR REPLACE FUNCTION financial.seed_default_taxes(p_tenant_id UUID)
RETURNS void AS $$
BEGIN
-- IVA 16% (tasa general)
INSERT INTO financial.taxes (tenant_id, code, name, tax_type, amount, included_in_price, active)
VALUES (p_tenant_id, 'IVA16', 'IVA 16%', 'all', 16.00, FALSE, TRUE)
ON CONFLICT (tenant_id, code) DO NOTHING;
-- IVA 8% (frontera)
INSERT INTO financial.taxes (tenant_id, code, name, tax_type, amount, included_in_price, active)
VALUES (p_tenant_id, 'IVA8', 'IVA 8% (Frontera)', 'all', 8.00, FALSE, TRUE)
ON CONFLICT (tenant_id, code) DO NOTHING;
-- IVA 0% (tasa cero)
INSERT INTO financial.taxes (tenant_id, code, name, tax_type, amount, included_in_price, active)
VALUES (p_tenant_id, 'IVA0', 'IVA 0%', 'all', 0.00, FALSE, TRUE)
ON CONFLICT (tenant_id, code) DO NOTHING;
-- IVA Exento
INSERT INTO financial.taxes (tenant_id, code, name, tax_type, amount, included_in_price, active)
VALUES (p_tenant_id, 'EXENTO', 'Exento de IVA', 'all', 0.00, FALSE, TRUE)
ON CONFLICT (tenant_id, code) DO NOTHING;
-- Retencion ISR 10%
INSERT INTO financial.taxes (tenant_id, code, name, tax_type, amount, included_in_price, active)
VALUES (p_tenant_id, 'RET_ISR10', 'Retencion ISR 10%', 'purchase', -10.00, FALSE, TRUE)
ON CONFLICT (tenant_id, code) DO NOTHING;
-- Retencion IVA 10.67%
INSERT INTO financial.taxes (tenant_id, code, name, tax_type, amount, included_in_price, active)
VALUES (p_tenant_id, 'RET_IVA', 'Retencion IVA 2/3', 'purchase', -10.67, FALSE, TRUE)
ON CONFLICT (tenant_id, code) DO NOTHING;
END;
$$ LANGUAGE plpgsql;
-- =====================
-- COMENTARIOS
-- =====================
COMMENT ON TABLE financial.taxes IS 'Catalogo de impuestos (IVA, retenciones, etc.)';
COMMENT ON COLUMN financial.taxes.code IS 'Codigo unico del impuesto (ej: IVA16, RET_ISR10)';
COMMENT ON COLUMN financial.taxes.tax_type IS 'Aplicacion: sales (solo ventas), purchase (solo compras), all (ambos)';
COMMENT ON COLUMN financial.taxes.amount IS 'Tasa del impuesto en porcentaje (ej: 16.00 para 16%). Negativo para retenciones.';
COMMENT ON COLUMN financial.taxes.included_in_price IS 'TRUE si el precio del producto ya incluye este impuesto';
COMMENT ON COLUMN financial.taxes.account_id IS 'Cuenta contable donde se registra el impuesto';
COMMENT ON COLUMN financial.taxes.refund_account_id IS 'Cuenta contable para devoluciones/notas de credito';
COMMENT ON TABLE financial.tax_groups IS 'Grupos de impuestos para aplicacion conjunta (ej: IVA + Retenciones)';
COMMENT ON COLUMN financial.tax_groups.tax_ids IS 'Array de IDs de impuestos que componen el grupo';
COMMENT ON FUNCTION financial.seed_default_taxes(UUID) IS 'Inserta impuestos predeterminados de Mexico para un tenant';