## Documentation - Align MCH-029 to MCH-032 with template-saas modules (SAAS-008 to SAAS-015) - Create MCH-034 (Analytics) and MCH-035 (Reports) from SAAS-016/017 - Update PLAN-DESARROLLO.md with Phase 7 and 8 - Update _MAP.md indexes (35 total epics) ## Database (5 new schemas, 14 tables) - Add storage schema: buckets, files, signed_urls - Add webhooks schema: endpoints, deliveries - Add audit schema: logs, retention_policies - Add features schema: flags, tenant_flags (14 seeds) - Add analytics schema: metrics, events, reports, report_schedules - Add auth.oauth_connections for MCH-030 - Add timestamptz_to_date() IMMUTABLE function - Update EXPECTED_SCHEMAS in recreate-database.sh ## Analysis Reports - ANALISIS-INTEGRACION-TEMPLATE-SAAS-2026-01-13.md - VALIDACION-COHERENCIA-2026-01-13.md - GAP-ANALYSIS-BD-2026-01-13.md - REPORTE-EJECUCION-2026-01-13.md Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
125 lines
3.7 KiB
PL/PgSQL
125 lines
3.7 KiB
PL/PgSQL
-- =============================================================================
|
|
-- MICHANGARRITO - 02 FUNCTIONS
|
|
-- =============================================================================
|
|
-- Funciones utilitarias del sistema
|
|
-- =============================================================================
|
|
|
|
-- Función para actualizar updated_at automáticamente
|
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Función IMMUTABLE para convertir TIMESTAMPTZ a DATE
|
|
-- Necesaria para índices funcionales sobre campos de fecha
|
|
-- Usa timezone fijo America/Mexico_City para consistencia
|
|
CREATE OR REPLACE FUNCTION public.timestamptz_to_date(ts TIMESTAMPTZ)
|
|
RETURNS DATE AS $$
|
|
SELECT (ts AT TIME ZONE 'America/Mexico_City')::date;
|
|
$$ LANGUAGE SQL IMMUTABLE PARALLEL SAFE;
|
|
|
|
COMMENT ON FUNCTION public.timestamptz_to_date IS 'Convierte TIMESTAMPTZ a DATE usando timezone America/Mexico_City (IMMUTABLE para indices)';
|
|
|
|
-- Función para generar número de ticket
|
|
CREATE OR REPLACE FUNCTION sales.generate_ticket_number(p_tenant_id UUID)
|
|
RETURNS VARCHAR(20) AS $$
|
|
DECLARE
|
|
v_date TEXT;
|
|
v_sequence INTEGER;
|
|
v_ticket VARCHAR(20);
|
|
BEGIN
|
|
v_date := TO_CHAR(CURRENT_DATE, 'YYMMDD');
|
|
|
|
SELECT COALESCE(MAX(
|
|
CAST(SUBSTRING(ticket_number FROM 8) AS INTEGER)
|
|
), 0) + 1
|
|
INTO v_sequence
|
|
FROM sales.sales
|
|
WHERE tenant_id = p_tenant_id
|
|
AND ticket_number LIKE v_date || '-%';
|
|
|
|
v_ticket := v_date || '-' || LPAD(v_sequence::TEXT, 4, '0');
|
|
|
|
RETURN v_ticket;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Función para generar número de pedido
|
|
CREATE OR REPLACE FUNCTION orders.generate_order_number(p_tenant_id UUID)
|
|
RETURNS VARCHAR(20) AS $$
|
|
DECLARE
|
|
v_date TEXT;
|
|
v_sequence INTEGER;
|
|
v_order VARCHAR(20);
|
|
BEGIN
|
|
v_date := TO_CHAR(CURRENT_DATE, 'YYMMDD');
|
|
|
|
SELECT COALESCE(MAX(
|
|
CAST(SUBSTRING(order_number FROM 8) AS INTEGER)
|
|
), 0) + 1
|
|
INTO v_sequence
|
|
FROM orders.orders
|
|
WHERE tenant_id = p_tenant_id
|
|
AND order_number LIKE 'P' || v_date || '-%';
|
|
|
|
v_order := 'P' || v_date || '-' || LPAD(v_sequence::TEXT, 4, '0');
|
|
|
|
RETURN v_order;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Función para actualizar balance de fiados del cliente
|
|
CREATE OR REPLACE FUNCTION customers.update_customer_fiado_balance()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE customers.customers
|
|
SET current_fiado_balance = (
|
|
SELECT COALESCE(SUM(remaining_amount), 0)
|
|
FROM customers.fiados
|
|
WHERE customer_id = COALESCE(NEW.customer_id, OLD.customer_id)
|
|
AND status IN ('pending', 'partial', 'overdue')
|
|
),
|
|
updated_at = NOW()
|
|
WHERE id = COALESCE(NEW.customer_id, OLD.customer_id);
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
-- Función para actualizar stock después de venta
|
|
CREATE OR REPLACE FUNCTION inventory.update_stock_on_sale()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
IF TG_OP = 'INSERT' THEN
|
|
-- Registrar movimiento de inventario
|
|
INSERT INTO inventory.inventory_movements (
|
|
tenant_id, product_id, movement_type, quantity,
|
|
previous_stock, new_stock, reference_type, reference_id
|
|
)
|
|
SELECT
|
|
s.tenant_id,
|
|
NEW.product_id,
|
|
'sale',
|
|
-NEW.quantity,
|
|
p.stock_quantity,
|
|
p.stock_quantity - NEW.quantity,
|
|
'sale',
|
|
NEW.sale_id
|
|
FROM sales.sales s
|
|
JOIN catalog.products p ON p.id = NEW.product_id
|
|
WHERE s.id = NEW.sale_id;
|
|
|
|
-- Actualizar stock del producto
|
|
UPDATE catalog.products
|
|
SET stock_quantity = stock_quantity - NEW.quantity,
|
|
updated_at = NOW()
|
|
WHERE id = NEW.product_id;
|
|
END IF;
|
|
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|