feat(catalogs): Add core catalogs DDL and seed data (MGN-005)
DDL (20-core-catalogs.sql): - Countries, States tables with ISO codes - Currencies with ISO 4217 - Currency rates with historical tracking - UoM categories and units with conversion factors - Product categories hierarchy - Sequences, Payment terms, Discount rules Seed data (04-seed-catalogs.sql): - 33 countries (Americas, Europe, Asia) - 32 Mexican states with timezones - 16 currencies (MXN, USD, EUR, etc.) - Initial exchange rates - 6 UoM categories with 30+ units Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
5043a640e4
commit
49c64e74a8
467
ddl/20-core-catalogs.sql
Normal file
467
ddl/20-core-catalogs.sql
Normal file
@ -0,0 +1,467 @@
|
||||
-- =============================================================
|
||||
-- ARCHIVO: 20-core-catalogs.sql
|
||||
-- DESCRIPCION: Catalogos maestros - paises, estados, monedas, UoM
|
||||
-- VERSION: 1.0.0
|
||||
-- PROYECTO: ERP-Core V2
|
||||
-- FECHA: 2026-01-18
|
||||
-- =============================================================
|
||||
|
||||
-- =====================
|
||||
-- SCHEMA: core (si no existe)
|
||||
-- =====================
|
||||
CREATE SCHEMA IF NOT EXISTS core;
|
||||
|
||||
-- =====================
|
||||
-- TABLA: countries
|
||||
-- Paises ISO 3166-1
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.countries (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
code VARCHAR(2) NOT NULL UNIQUE, -- ISO 3166-1 alpha-2
|
||||
code_alpha3 VARCHAR(3), -- ISO 3166-1 alpha-3
|
||||
name VARCHAR(255) NOT NULL,
|
||||
phone_code VARCHAR(10),
|
||||
currency_code VARCHAR(3), -- Default currency
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_countries_code ON core.countries(code);
|
||||
CREATE INDEX IF NOT EXISTS idx_countries_name ON core.countries(name);
|
||||
|
||||
COMMENT ON TABLE core.countries IS 'Catalogo de paises ISO 3166-1';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: states
|
||||
-- Estados/Provincias/Regiones
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.states (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
country_id UUID NOT NULL REFERENCES core.countries(id) ON DELETE CASCADE,
|
||||
code VARCHAR(10) NOT NULL, -- Codigo del estado (ej: JAL, NL, QRO)
|
||||
name VARCHAR(255) NOT NULL,
|
||||
|
||||
-- Datos adicionales
|
||||
timezone VARCHAR(50), -- Zona horaria predominante
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(country_id, code)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_states_country ON core.states(country_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_states_code ON core.states(code);
|
||||
CREATE INDEX IF NOT EXISTS idx_states_active ON core.states(is_active) WHERE is_active = TRUE;
|
||||
|
||||
COMMENT ON TABLE core.states IS 'Catalogo de estados/provincias/regiones por pais';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: currencies
|
||||
-- Monedas ISO 4217
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.currencies (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
code VARCHAR(3) NOT NULL UNIQUE, -- ISO 4217
|
||||
name VARCHAR(100) NOT NULL,
|
||||
symbol VARCHAR(10) NOT NULL,
|
||||
decimals INTEGER NOT NULL DEFAULT 2,
|
||||
rounding DECIMAL(12, 6) DEFAULT 0.01,
|
||||
active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_currencies_code ON core.currencies(code);
|
||||
CREATE INDEX IF NOT EXISTS idx_currencies_active ON core.currencies(active) WHERE active = TRUE;
|
||||
|
||||
COMMENT ON TABLE core.currencies IS 'Catalogo de monedas ISO 4217';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: currency_rates
|
||||
-- Tipos de cambio historicos
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.currency_rates (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE, -- NULL = global
|
||||
|
||||
from_currency_id UUID NOT NULL REFERENCES core.currencies(id),
|
||||
to_currency_id UUID NOT NULL REFERENCES core.currencies(id),
|
||||
|
||||
rate DECIMAL(18, 8) NOT NULL, -- Tipo de cambio
|
||||
rate_date DATE NOT NULL, -- Fecha del tipo de cambio
|
||||
|
||||
-- Fuente del tipo de cambio
|
||||
source VARCHAR(50) DEFAULT 'manual', -- manual, banxico, xe, openexchange
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
created_by UUID REFERENCES auth.users(id),
|
||||
|
||||
UNIQUE(tenant_id, from_currency_id, to_currency_id, rate_date)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_currency_rates_tenant ON core.currency_rates(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_currency_rates_from ON core.currency_rates(from_currency_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_currency_rates_to ON core.currency_rates(to_currency_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_currency_rates_date ON core.currency_rates(rate_date DESC);
|
||||
CREATE INDEX IF NOT EXISTS idx_currency_rates_lookup ON core.currency_rates(from_currency_id, to_currency_id, rate_date DESC);
|
||||
|
||||
COMMENT ON TABLE core.currency_rates IS 'Historico de tipos de cambio entre monedas';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: uom_categories
|
||||
-- Categorias de unidades de medida
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.uom_categories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE, -- NULL = global
|
||||
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(tenant_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_uom_categories_tenant ON core.uom_categories(tenant_id);
|
||||
|
||||
COMMENT ON TABLE core.uom_categories IS 'Categorias de unidades de medida (peso, volumen, longitud, etc.)';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: uom
|
||||
-- Unidades de medida
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.uom (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID REFERENCES auth.tenants(id) ON DELETE CASCADE, -- NULL = global
|
||||
category_id UUID NOT NULL REFERENCES core.uom_categories(id) ON DELETE CASCADE,
|
||||
|
||||
name VARCHAR(100) NOT NULL,
|
||||
symbol VARCHAR(20) NOT NULL,
|
||||
|
||||
-- Tipo: reference = unidad base de la categoria
|
||||
uom_type VARCHAR(20) NOT NULL DEFAULT 'reference', -- reference, bigger, smaller
|
||||
|
||||
-- Factor de conversion respecto a la unidad de referencia de la categoria
|
||||
factor DECIMAL(18, 8) NOT NULL DEFAULT 1,
|
||||
rounding DECIMAL(12, 6) DEFAULT 0.01,
|
||||
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(tenant_id, category_id, name)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_uom_tenant ON core.uom(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_uom_category ON core.uom(category_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_uom_active ON core.uom(is_active) WHERE is_active = TRUE;
|
||||
|
||||
COMMENT ON TABLE core.uom IS 'Unidades de medida con factores de conversion';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: product_categories
|
||||
-- Categorias jerarquicas de productos
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.product_categories (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||
parent_id UUID REFERENCES core.product_categories(id) ON DELETE SET NULL,
|
||||
|
||||
code VARCHAR(50),
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Jerarquia
|
||||
hierarchy_path TEXT,
|
||||
hierarchy_level INTEGER DEFAULT 0,
|
||||
|
||||
-- Configuracion
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
|
||||
UNIQUE(tenant_id, code)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_product_categories_tenant ON core.product_categories(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_product_categories_parent ON core.product_categories(parent_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_product_categories_path ON core.product_categories(hierarchy_path);
|
||||
CREATE INDEX IF NOT EXISTS idx_product_categories_active ON core.product_categories(is_active) WHERE is_active = TRUE;
|
||||
|
||||
COMMENT ON TABLE core.product_categories IS 'Categorias jerarquicas de productos';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: sequences
|
||||
-- Secuencias para numeracion automatica
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.sequences (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||
|
||||
code VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Formato
|
||||
prefix VARCHAR(20),
|
||||
suffix VARCHAR(20),
|
||||
padding INTEGER DEFAULT 5,
|
||||
|
||||
-- Contador
|
||||
next_number BIGINT DEFAULT 1,
|
||||
|
||||
-- Reset
|
||||
reset_period VARCHAR(20) DEFAULT 'never', -- never, daily, monthly, yearly
|
||||
last_reset_at TIMESTAMPTZ,
|
||||
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
UNIQUE(tenant_id, code)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sequences_tenant ON core.sequences(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sequences_code ON core.sequences(code);
|
||||
|
||||
COMMENT ON TABLE core.sequences IS 'Secuencias para numeracion automatica de documentos';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: payment_terms
|
||||
-- Condiciones de pago
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.payment_terms (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||
|
||||
code VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Configuracion
|
||||
is_immediate BOOLEAN DEFAULT FALSE, -- Pago inmediato/contado
|
||||
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
|
||||
UNIQUE(tenant_id, code)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_terms_tenant ON core.payment_terms(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_terms_code ON core.payment_terms(code);
|
||||
|
||||
-- =====================
|
||||
-- TABLA: payment_term_lines
|
||||
-- Lineas de condiciones de pago
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.payment_term_lines (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
payment_term_id UUID NOT NULL REFERENCES core.payment_terms(id) ON DELETE CASCADE,
|
||||
|
||||
sequence INTEGER DEFAULT 0,
|
||||
|
||||
line_type VARCHAR(20) NOT NULL DEFAULT 'balance', -- percent, fixed, balance
|
||||
value_percent DECIMAL(5, 2), -- Para type=percent
|
||||
value_fixed DECIMAL(12, 2), -- Para type=fixed
|
||||
|
||||
days INTEGER DEFAULT 0, -- Dias para vencimiento
|
||||
day_of_month INTEGER, -- Dia especifico del mes (1-31)
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_payment_term_lines_term ON core.payment_term_lines(payment_term_id);
|
||||
|
||||
COMMENT ON TABLE core.payment_term_lines IS 'Lineas que componen una condicion de pago';
|
||||
|
||||
-- =====================
|
||||
-- TABLA: discount_rules
|
||||
-- Reglas de descuento
|
||||
-- =====================
|
||||
CREATE TABLE IF NOT EXISTS core.discount_rules (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
tenant_id UUID NOT NULL REFERENCES auth.tenants(id) ON DELETE CASCADE,
|
||||
|
||||
code VARCHAR(50) NOT NULL,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
description TEXT,
|
||||
|
||||
-- Tipo de descuento
|
||||
discount_type VARCHAR(20) NOT NULL DEFAULT 'percent', -- percent, fixed
|
||||
value DECIMAL(12, 2) NOT NULL,
|
||||
|
||||
-- A que aplica
|
||||
applies_to VARCHAR(30) DEFAULT 'all', -- all, product, category, customer, order
|
||||
|
||||
-- Condiciones (JSONB para flexibilidad)
|
||||
conditions JSONB DEFAULT '{}',
|
||||
-- Ejemplo: {"min_qty": 10, "min_amount": 1000, "product_ids": [...]}
|
||||
|
||||
-- Vigencia
|
||||
valid_from TIMESTAMPTZ,
|
||||
valid_until TIMESTAMPTZ,
|
||||
|
||||
-- Prioridad (para resolver conflictos)
|
||||
priority INTEGER DEFAULT 0,
|
||||
|
||||
is_active BOOLEAN DEFAULT TRUE,
|
||||
|
||||
created_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMPTZ DEFAULT CURRENT_TIMESTAMP,
|
||||
deleted_at TIMESTAMPTZ,
|
||||
|
||||
UNIQUE(tenant_id, code)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_discount_rules_tenant ON core.discount_rules(tenant_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_discount_rules_code ON core.discount_rules(code);
|
||||
CREATE INDEX IF NOT EXISTS idx_discount_rules_active ON core.discount_rules(is_active) WHERE is_active = TRUE;
|
||||
CREATE INDEX IF NOT EXISTS idx_discount_rules_validity ON core.discount_rules(valid_from, valid_until);
|
||||
|
||||
COMMENT ON TABLE core.discount_rules IS 'Reglas de descuento configurables';
|
||||
|
||||
-- =====================
|
||||
-- RLS POLICIES
|
||||
-- =====================
|
||||
|
||||
-- Currency rates: tenant isolation (NULL tenant = global, available to all)
|
||||
ALTER TABLE core.currency_rates ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_currency_rates ON core.currency_rates
|
||||
USING (
|
||||
tenant_id IS NULL OR
|
||||
tenant_id = current_setting('app.current_tenant_id', true)::uuid
|
||||
);
|
||||
|
||||
-- UoM Categories: tenant isolation
|
||||
ALTER TABLE core.uom_categories ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_uom_categories ON core.uom_categories
|
||||
USING (
|
||||
tenant_id IS NULL OR
|
||||
tenant_id = current_setting('app.current_tenant_id', true)::uuid
|
||||
);
|
||||
|
||||
-- UoM: tenant isolation
|
||||
ALTER TABLE core.uom ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_uom ON core.uom
|
||||
USING (
|
||||
tenant_id IS NULL OR
|
||||
tenant_id = current_setting('app.current_tenant_id', true)::uuid
|
||||
);
|
||||
|
||||
-- Product Categories: tenant isolation
|
||||
ALTER TABLE core.product_categories ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_product_categories ON core.product_categories
|
||||
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
-- Sequences: tenant isolation
|
||||
ALTER TABLE core.sequences ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_sequences ON core.sequences
|
||||
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
-- Payment Terms: tenant isolation
|
||||
ALTER TABLE core.payment_terms ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_payment_terms ON core.payment_terms
|
||||
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
-- Discount Rules: tenant isolation
|
||||
ALTER TABLE core.discount_rules ENABLE ROW LEVEL SECURITY;
|
||||
CREATE POLICY tenant_isolation_discount_rules ON core.discount_rules
|
||||
USING (tenant_id = current_setting('app.current_tenant_id', true)::uuid);
|
||||
|
||||
-- =====================
|
||||
-- FUNCIONES DE UTILIDAD
|
||||
-- =====================
|
||||
|
||||
-- Funcion para obtener tipo de cambio mas reciente
|
||||
CREATE OR REPLACE FUNCTION core.get_currency_rate(
|
||||
p_from_currency VARCHAR(3),
|
||||
p_to_currency VARCHAR(3),
|
||||
p_date DATE DEFAULT CURRENT_DATE,
|
||||
p_tenant_id UUID DEFAULT NULL
|
||||
)
|
||||
RETURNS DECIMAL(18, 8) AS $$
|
||||
DECLARE
|
||||
v_rate DECIMAL(18, 8);
|
||||
BEGIN
|
||||
-- Si son la misma moneda, retornar 1
|
||||
IF p_from_currency = p_to_currency THEN
|
||||
RETURN 1.0;
|
||||
END IF;
|
||||
|
||||
-- Buscar tipo de cambio directo (mas reciente hasta la fecha dada)
|
||||
SELECT cr.rate INTO v_rate
|
||||
FROM core.currency_rates cr
|
||||
JOIN core.currencies fc ON fc.id = cr.from_currency_id AND fc.code = p_from_currency
|
||||
JOIN core.currencies tc ON tc.id = cr.to_currency_id AND tc.code = p_to_currency
|
||||
WHERE cr.rate_date <= p_date
|
||||
AND (cr.tenant_id IS NULL OR cr.tenant_id = p_tenant_id)
|
||||
ORDER BY cr.rate_date DESC, cr.tenant_id DESC NULLS LAST
|
||||
LIMIT 1;
|
||||
|
||||
IF v_rate IS NOT NULL THEN
|
||||
RETURN v_rate;
|
||||
END IF;
|
||||
|
||||
-- Buscar tipo de cambio inverso
|
||||
SELECT 1.0 / cr.rate INTO v_rate
|
||||
FROM core.currency_rates cr
|
||||
JOIN core.currencies fc ON fc.id = cr.from_currency_id AND fc.code = p_to_currency
|
||||
JOIN core.currencies tc ON tc.id = cr.to_currency_id AND tc.code = p_from_currency
|
||||
WHERE cr.rate_date <= p_date
|
||||
AND (cr.tenant_id IS NULL OR cr.tenant_id = p_tenant_id)
|
||||
ORDER BY cr.rate_date DESC, cr.tenant_id DESC NULLS LAST
|
||||
LIMIT 1;
|
||||
|
||||
RETURN v_rate; -- NULL si no se encuentra
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Funcion para convertir cantidad entre UoM
|
||||
CREATE OR REPLACE FUNCTION core.convert_uom(
|
||||
p_quantity DECIMAL,
|
||||
p_from_uom_id UUID,
|
||||
p_to_uom_id UUID
|
||||
)
|
||||
RETURNS DECIMAL(18, 8) AS $$
|
||||
DECLARE
|
||||
v_from_factor DECIMAL(18, 8);
|
||||
v_to_factor DECIMAL(18, 8);
|
||||
v_from_category UUID;
|
||||
v_to_category UUID;
|
||||
BEGIN
|
||||
-- Si son la misma UoM, retornar la cantidad
|
||||
IF p_from_uom_id = p_to_uom_id THEN
|
||||
RETURN p_quantity;
|
||||
END IF;
|
||||
|
||||
-- Obtener factores y categorias
|
||||
SELECT factor, category_id INTO v_from_factor, v_from_category
|
||||
FROM core.uom WHERE id = p_from_uom_id;
|
||||
|
||||
SELECT factor, category_id INTO v_to_factor, v_to_category
|
||||
FROM core.uom WHERE id = p_to_uom_id;
|
||||
|
||||
-- Validar que sean de la misma categoria
|
||||
IF v_from_category != v_to_category THEN
|
||||
RAISE EXCEPTION 'Cannot convert between different UoM categories';
|
||||
END IF;
|
||||
|
||||
-- Convertir: primero a unidad de referencia, luego a destino
|
||||
RETURN p_quantity * v_from_factor / v_to_factor;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- =====================
|
||||
-- FIN DEL ARCHIVO
|
||||
-- =====================
|
||||
259
seeds/dev/04-seed-catalogs.sql
Normal file
259
seeds/dev/04-seed-catalogs.sql
Normal file
@ -0,0 +1,259 @@
|
||||
-- =============================================================
|
||||
-- ARCHIVO: 04-seed-catalogs.sql
|
||||
-- DESCRIPCION: Datos semilla para catalogos maestros
|
||||
-- VERSION: 1.0.0
|
||||
-- PROYECTO: ERP-Core V2
|
||||
-- FECHA: 2026-01-18
|
||||
-- =============================================================
|
||||
|
||||
-- =====================
|
||||
-- PAISES (ISO 3166-1)
|
||||
-- =====================
|
||||
|
||||
INSERT INTO core.countries (code, code_alpha3, name, phone_code, currency_code) VALUES
|
||||
-- America del Norte
|
||||
('MX', 'MEX', 'México', '+52', 'MXN'),
|
||||
('US', 'USA', 'Estados Unidos', '+1', 'USD'),
|
||||
('CA', 'CAN', 'Canadá', '+1', 'CAD'),
|
||||
-- America Central
|
||||
('GT', 'GTM', 'Guatemala', '+502', 'GTQ'),
|
||||
('BZ', 'BLZ', 'Belice', '+501', 'BZD'),
|
||||
('SV', 'SLV', 'El Salvador', '+503', 'USD'),
|
||||
('HN', 'HND', 'Honduras', '+504', 'HNL'),
|
||||
('NI', 'NIC', 'Nicaragua', '+505', 'NIO'),
|
||||
('CR', 'CRI', 'Costa Rica', '+506', 'CRC'),
|
||||
('PA', 'PAN', 'Panamá', '+507', 'PAB'),
|
||||
-- America del Sur
|
||||
('CO', 'COL', 'Colombia', '+57', 'COP'),
|
||||
('VE', 'VEN', 'Venezuela', '+58', 'VES'),
|
||||
('EC', 'ECU', 'Ecuador', '+593', 'USD'),
|
||||
('PE', 'PER', 'Perú', '+51', 'PEN'),
|
||||
('BO', 'BOL', 'Bolivia', '+591', 'BOB'),
|
||||
('CL', 'CHL', 'Chile', '+56', 'CLP'),
|
||||
('AR', 'ARG', 'Argentina', '+54', 'ARS'),
|
||||
('UY', 'URY', 'Uruguay', '+598', 'UYU'),
|
||||
('PY', 'PRY', 'Paraguay', '+595', 'PYG'),
|
||||
('BR', 'BRA', 'Brasil', '+55', 'BRL'),
|
||||
-- Europa
|
||||
('ES', 'ESP', 'España', '+34', 'EUR'),
|
||||
('DE', 'DEU', 'Alemania', '+49', 'EUR'),
|
||||
('FR', 'FRA', 'Francia', '+33', 'EUR'),
|
||||
('IT', 'ITA', 'Italia', '+39', 'EUR'),
|
||||
('GB', 'GBR', 'Reino Unido', '+44', 'GBP'),
|
||||
('PT', 'PRT', 'Portugal', '+351', 'EUR'),
|
||||
('NL', 'NLD', 'Países Bajos', '+31', 'EUR'),
|
||||
('BE', 'BEL', 'Bélgica', '+32', 'EUR'),
|
||||
('CH', 'CHE', 'Suiza', '+41', 'CHF'),
|
||||
-- Asia
|
||||
('CN', 'CHN', 'China', '+86', 'CNY'),
|
||||
('JP', 'JPN', 'Japón', '+81', 'JPY'),
|
||||
('KR', 'KOR', 'Corea del Sur', '+82', 'KRW'),
|
||||
('IN', 'IND', 'India', '+91', 'INR')
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- =====================
|
||||
-- ESTADOS DE MEXICO
|
||||
-- =====================
|
||||
|
||||
-- Obtener ID de México
|
||||
DO $$
|
||||
DECLARE
|
||||
mexico_id UUID;
|
||||
BEGIN
|
||||
SELECT id INTO mexico_id FROM core.countries WHERE code = 'MX';
|
||||
|
||||
IF mexico_id IS NOT NULL THEN
|
||||
INSERT INTO core.states (country_id, code, name, timezone, is_active) VALUES
|
||||
(mexico_id, 'AGU', 'Aguascalientes', 'America/Mexico_City', true),
|
||||
(mexico_id, 'BCN', 'Baja California', 'America/Tijuana', true),
|
||||
(mexico_id, 'BCS', 'Baja California Sur', 'America/Mazatlan', true),
|
||||
(mexico_id, 'CAM', 'Campeche', 'America/Merida', true),
|
||||
(mexico_id, 'CHP', 'Chiapas', 'America/Mexico_City', true),
|
||||
(mexico_id, 'CHH', 'Chihuahua', 'America/Chihuahua', true),
|
||||
(mexico_id, 'CMX', 'Ciudad de México', 'America/Mexico_City', true),
|
||||
(mexico_id, 'COA', 'Coahuila', 'America/Monterrey', true),
|
||||
(mexico_id, 'COL', 'Colima', 'America/Mexico_City', true),
|
||||
(mexico_id, 'DUR', 'Durango', 'America/Monterrey', true),
|
||||
(mexico_id, 'GUA', 'Guanajuato', 'America/Mexico_City', true),
|
||||
(mexico_id, 'GRO', 'Guerrero', 'America/Mexico_City', true),
|
||||
(mexico_id, 'HID', 'Hidalgo', 'America/Mexico_City', true),
|
||||
(mexico_id, 'JAL', 'Jalisco', 'America/Mexico_City', true),
|
||||
(mexico_id, 'MEX', 'Estado de México', 'America/Mexico_City', true),
|
||||
(mexico_id, 'MIC', 'Michoacán', 'America/Mexico_City', true),
|
||||
(mexico_id, 'MOR', 'Morelos', 'America/Mexico_City', true),
|
||||
(mexico_id, 'NAY', 'Nayarit', 'America/Mazatlan', true),
|
||||
(mexico_id, 'NLE', 'Nuevo León', 'America/Monterrey', true),
|
||||
(mexico_id, 'OAX', 'Oaxaca', 'America/Mexico_City', true),
|
||||
(mexico_id, 'PUE', 'Puebla', 'America/Mexico_City', true),
|
||||
(mexico_id, 'QUE', 'Querétaro', 'America/Mexico_City', true),
|
||||
(mexico_id, 'ROO', 'Quintana Roo', 'America/Cancun', true),
|
||||
(mexico_id, 'SLP', 'San Luis Potosí', 'America/Mexico_City', true),
|
||||
(mexico_id, 'SIN', 'Sinaloa', 'America/Mazatlan', true),
|
||||
(mexico_id, 'SON', 'Sonora', 'America/Hermosillo', true),
|
||||
(mexico_id, 'TAB', 'Tabasco', 'America/Mexico_City', true),
|
||||
(mexico_id, 'TAM', 'Tamaulipas', 'America/Monterrey', true),
|
||||
(mexico_id, 'TLA', 'Tlaxcala', 'America/Mexico_City', true),
|
||||
(mexico_id, 'VER', 'Veracruz', 'America/Mexico_City', true),
|
||||
(mexico_id, 'YUC', 'Yucatán', 'America/Merida', true),
|
||||
(mexico_id, 'ZAC', 'Zacatecas', 'America/Mexico_City', true)
|
||||
ON CONFLICT (country_id, code) DO NOTHING;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- =====================
|
||||
-- MONEDAS (ISO 4217)
|
||||
-- =====================
|
||||
|
||||
INSERT INTO core.currencies (code, name, symbol, decimals, rounding, active) VALUES
|
||||
-- Americas
|
||||
('MXN', 'Peso Mexicano', '$', 2, 0.01, true),
|
||||
('USD', 'Dólar Estadounidense', '$', 2, 0.01, true),
|
||||
('CAD', 'Dólar Canadiense', '$', 2, 0.01, true),
|
||||
('BRL', 'Real Brasileño', 'R$', 2, 0.01, true),
|
||||
('ARS', 'Peso Argentino', '$', 2, 0.01, true),
|
||||
('CLP', 'Peso Chileno', '$', 0, 1, true),
|
||||
('COP', 'Peso Colombiano', '$', 0, 1, true),
|
||||
('PEN', 'Sol Peruano', 'S/', 2, 0.01, true),
|
||||
('GTQ', 'Quetzal', 'Q', 2, 0.01, true),
|
||||
-- Europa
|
||||
('EUR', 'Euro', '€', 2, 0.01, true),
|
||||
('GBP', 'Libra Esterlina', '£', 2, 0.01, true),
|
||||
('CHF', 'Franco Suizo', 'Fr', 2, 0.01, true),
|
||||
-- Asia
|
||||
('JPY', 'Yen Japonés', '¥', 0, 1, true),
|
||||
('CNY', 'Yuan Chino', '¥', 2, 0.01, true),
|
||||
('KRW', 'Won Surcoreano', '₩', 0, 1, true),
|
||||
('INR', 'Rupia India', '₹', 2, 0.01, true)
|
||||
ON CONFLICT (code) DO NOTHING;
|
||||
|
||||
-- =====================
|
||||
-- TIPOS DE CAMBIO INICIALES (MXN base)
|
||||
-- =====================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
mxn_id UUID;
|
||||
usd_id UUID;
|
||||
eur_id UUID;
|
||||
cad_id UUID;
|
||||
BEGIN
|
||||
SELECT id INTO mxn_id FROM core.currencies WHERE code = 'MXN';
|
||||
SELECT id INTO usd_id FROM core.currencies WHERE code = 'USD';
|
||||
SELECT id INTO eur_id FROM core.currencies WHERE code = 'EUR';
|
||||
SELECT id INTO cad_id FROM core.currencies WHERE code = 'CAD';
|
||||
|
||||
IF mxn_id IS NOT NULL AND usd_id IS NOT NULL THEN
|
||||
INSERT INTO core.currency_rates (tenant_id, from_currency_id, to_currency_id, rate, rate_date, source)
|
||||
VALUES
|
||||
(NULL, usd_id, mxn_id, 17.50, CURRENT_DATE, 'manual'),
|
||||
(NULL, eur_id, mxn_id, 19.20, CURRENT_DATE, 'manual'),
|
||||
(NULL, cad_id, mxn_id, 13.10, CURRENT_DATE, 'manual')
|
||||
ON CONFLICT DO NOTHING;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- =====================
|
||||
-- CATEGORIAS DE UOM (Globales)
|
||||
-- =====================
|
||||
|
||||
INSERT INTO core.uom_categories (tenant_id, name, description) VALUES
|
||||
(NULL, 'Unidad', 'Unidades contables'),
|
||||
(NULL, 'Peso', 'Unidades de peso/masa'),
|
||||
(NULL, 'Longitud', 'Unidades de longitud'),
|
||||
(NULL, 'Volumen', 'Unidades de volumen'),
|
||||
(NULL, 'Área', 'Unidades de área'),
|
||||
(NULL, 'Tiempo', 'Unidades de tiempo')
|
||||
ON CONFLICT (tenant_id, name) DO NOTHING;
|
||||
|
||||
-- =====================
|
||||
-- UOM (Globales)
|
||||
-- =====================
|
||||
|
||||
DO $$
|
||||
DECLARE
|
||||
cat_unit UUID;
|
||||
cat_peso UUID;
|
||||
cat_longitud UUID;
|
||||
cat_volumen UUID;
|
||||
cat_area UUID;
|
||||
cat_tiempo UUID;
|
||||
BEGIN
|
||||
SELECT id INTO cat_unit FROM core.uom_categories WHERE name = 'Unidad' AND tenant_id IS NULL;
|
||||
SELECT id INTO cat_peso FROM core.uom_categories WHERE name = 'Peso' AND tenant_id IS NULL;
|
||||
SELECT id INTO cat_longitud FROM core.uom_categories WHERE name = 'Longitud' AND tenant_id IS NULL;
|
||||
SELECT id INTO cat_volumen FROM core.uom_categories WHERE name = 'Volumen' AND tenant_id IS NULL;
|
||||
SELECT id INTO cat_area FROM core.uom_categories WHERE name = 'Área' AND tenant_id IS NULL;
|
||||
SELECT id INTO cat_tiempo FROM core.uom_categories WHERE name = 'Tiempo' AND tenant_id IS NULL;
|
||||
|
||||
-- Unidad
|
||||
IF cat_unit IS NOT NULL THEN
|
||||
INSERT INTO core.uom (tenant_id, category_id, name, symbol, uom_type, factor, is_active) VALUES
|
||||
(NULL, cat_unit, 'Pieza', 'pz', 'reference', 1, true),
|
||||
(NULL, cat_unit, 'Docena', 'doc', 'bigger', 12, true),
|
||||
(NULL, cat_unit, 'Ciento', 'cto', 'bigger', 100, true),
|
||||
(NULL, cat_unit, 'Millar', 'mil', 'bigger', 1000, true),
|
||||
(NULL, cat_unit, 'Par', 'par', 'bigger', 2, true)
|
||||
ON CONFLICT (tenant_id, category_id, name) DO NOTHING;
|
||||
END IF;
|
||||
|
||||
-- Peso
|
||||
IF cat_peso IS NOT NULL THEN
|
||||
INSERT INTO core.uom (tenant_id, category_id, name, symbol, uom_type, factor, is_active) VALUES
|
||||
(NULL, cat_peso, 'Kilogramo', 'kg', 'reference', 1, true),
|
||||
(NULL, cat_peso, 'Gramo', 'g', 'smaller', 0.001, true),
|
||||
(NULL, cat_peso, 'Miligramo', 'mg', 'smaller', 0.000001, true),
|
||||
(NULL, cat_peso, 'Tonelada', 't', 'bigger', 1000, true),
|
||||
(NULL, cat_peso, 'Libra', 'lb', 'smaller', 0.453592, true),
|
||||
(NULL, cat_peso, 'Onza', 'oz', 'smaller', 0.0283495, true)
|
||||
ON CONFLICT (tenant_id, category_id, name) DO NOTHING;
|
||||
END IF;
|
||||
|
||||
-- Longitud
|
||||
IF cat_longitud IS NOT NULL THEN
|
||||
INSERT INTO core.uom (tenant_id, category_id, name, symbol, uom_type, factor, is_active) VALUES
|
||||
(NULL, cat_longitud, 'Metro', 'm', 'reference', 1, true),
|
||||
(NULL, cat_longitud, 'Centímetro', 'cm', 'smaller', 0.01, true),
|
||||
(NULL, cat_longitud, 'Milímetro', 'mm', 'smaller', 0.001, true),
|
||||
(NULL, cat_longitud, 'Kilómetro', 'km', 'bigger', 1000, true),
|
||||
(NULL, cat_longitud, 'Pulgada', 'in', 'smaller', 0.0254, true),
|
||||
(NULL, cat_longitud, 'Pie', 'ft', 'smaller', 0.3048, true),
|
||||
(NULL, cat_longitud, 'Yarda', 'yd', 'smaller', 0.9144, true)
|
||||
ON CONFLICT (tenant_id, category_id, name) DO NOTHING;
|
||||
END IF;
|
||||
|
||||
-- Volumen
|
||||
IF cat_volumen IS NOT NULL THEN
|
||||
INSERT INTO core.uom (tenant_id, category_id, name, symbol, uom_type, factor, is_active) VALUES
|
||||
(NULL, cat_volumen, 'Litro', 'L', 'reference', 1, true),
|
||||
(NULL, cat_volumen, 'Mililitro', 'mL', 'smaller', 0.001, true),
|
||||
(NULL, cat_volumen, 'Metro cúbico', 'm³', 'bigger', 1000, true),
|
||||
(NULL, cat_volumen, 'Galón US', 'gal', 'bigger', 3.78541, true),
|
||||
(NULL, cat_volumen, 'Onza líquida', 'fl oz', 'smaller', 0.0295735, true)
|
||||
ON CONFLICT (tenant_id, category_id, name) DO NOTHING;
|
||||
END IF;
|
||||
|
||||
-- Área
|
||||
IF cat_area IS NOT NULL THEN
|
||||
INSERT INTO core.uom (tenant_id, category_id, name, symbol, uom_type, factor, is_active) VALUES
|
||||
(NULL, cat_area, 'Metro cuadrado', 'm²', 'reference', 1, true),
|
||||
(NULL, cat_area, 'Centímetro cuadrado', 'cm²', 'smaller', 0.0001, true),
|
||||
(NULL, cat_area, 'Kilómetro cuadrado', 'km²', 'bigger', 1000000, true),
|
||||
(NULL, cat_area, 'Hectárea', 'ha', 'bigger', 10000, true),
|
||||
(NULL, cat_area, 'Pie cuadrado', 'ft²', 'smaller', 0.092903, true)
|
||||
ON CONFLICT (tenant_id, category_id, name) DO NOTHING;
|
||||
END IF;
|
||||
|
||||
-- Tiempo
|
||||
IF cat_tiempo IS NOT NULL THEN
|
||||
INSERT INTO core.uom (tenant_id, category_id, name, symbol, uom_type, factor, is_active) VALUES
|
||||
(NULL, cat_tiempo, 'Hora', 'h', 'reference', 1, true),
|
||||
(NULL, cat_tiempo, 'Minuto', 'min', 'smaller', 0.0166667, true),
|
||||
(NULL, cat_tiempo, 'Día', 'd', 'bigger', 24, true),
|
||||
(NULL, cat_tiempo, 'Semana', 'sem', 'bigger', 168, true)
|
||||
ON CONFLICT (tenant_id, category_id, name) DO NOTHING;
|
||||
END IF;
|
||||
END $$;
|
||||
|
||||
-- =====================
|
||||
-- FIN DEL ARCHIVO
|
||||
-- =====================
|
||||
Loading…
Reference in New Issue
Block a user