FASE 4-5-6: Infraestructura, CRM, Service Orders, Notificaciones, Ahorro, Logistica, API Publica

FASE 4:
- Redis cache de stock con fallback graceful
- Multi-moneda (MXN/USD) con contabilidad en MXN
- Proveedores y ordenes de compra completo
- Meilisearch 1.5M+ partes indexadas
- Metabase KPIs con dashboard auto-generado

FASE 5:
- CRM mejorado: activities, tags, loyalty program, analytics
- Imagenes de partes: upload, resize, thumbnails WebP
- Ordenes de servicio Kanban: received->diagnosis->repair->ready->delivered
- Garantias/RMA, alertas de reorden, multi-sucursal
- Stubs BNPL (APLAZO) y ERP Sync (Aspel/Contpaqi)

FASE 6:
- Notificaciones automaticas: push/WhatsApp/email/in-app
- Reportes de ahorro vs retail_price
- Logistica + tracking: DHL, FedEx, Estafeta, 99min, Uber
- API Publica: API keys, rate limiting, catalog search

Migraciones: v1.9-v3.0
Tests: 93/93 pasando
Backup: nexus_backup_20260427_045859.tar.gz
This commit is contained in:
Nexus Dev
2026-04-27 05:23:30 +00:00
parent b70cb3042b
commit 9ff3dc4c8b
71 changed files with 10939 additions and 420 deletions

View File

@@ -0,0 +1,81 @@
-- v2.1_suppliers.sql
-- Mejora #3: Proveedores y Órdenes de Compra
--
-- Adds supplier management and purchase order workflow to tenant databases.
--
-- Workflow:
-- 1. Create supplier (suppliers table)
-- 2. Create PO with items (purchase_orders + purchase_order_items)
-- 3. Send PO to supplier (status = 'sent')
-- 4. Receive partial or full delivery (status = 'partial' | 'received')
-- → On receive: update stock via inventory_engine.record_purchase()
-- → On receive: create accounting entry via record_purchase_entry()
-- ═══════════════════════════════════════════════════════════════════════════
-- SUPPLIERS
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE IF NOT EXISTS suppliers (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
contact_name VARCHAR(200),
phone VARCHAR(50),
email VARCHAR(200),
rfc VARCHAR(13),
address TEXT,
payment_terms VARCHAR(100), -- e.g., "30 dias", "contado"
notes TEXT,
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_suppliers_active ON suppliers(is_active);
CREATE INDEX IF NOT EXISTS idx_suppliers_name ON suppliers(name);
-- ═══════════════════════════════════════════════════════════════════════════
-- PURCHASE ORDERS
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE IF NOT EXISTS purchase_orders (
id SERIAL PRIMARY KEY,
supplier_id INTEGER REFERENCES suppliers(id) ON DELETE SET NULL,
branch_id INTEGER REFERENCES branches(id),
employee_id INTEGER REFERENCES employees(id),
status VARCHAR(20) DEFAULT 'draft' NOT NULL, -- draft, sent, partial, received, cancelled
subtotal NUMERIC(12,2) DEFAULT 0 NOT NULL,
tax_total NUMERIC(12,2) DEFAULT 0 NOT NULL,
total NUMERIC(12,2) DEFAULT 0 NOT NULL,
currency VARCHAR(3) DEFAULT 'MXN',
exchange_rate NUMERIC(12,6) DEFAULT 1.0,
notes TEXT,
supplier_invoice VARCHAR(100),
expected_date DATE,
sent_at TIMESTAMPTZ,
received_at TIMESTAMPTZ,
cancelled_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_po_supplier ON purchase_orders(supplier_id);
CREATE INDEX IF NOT EXISTS idx_po_status ON purchase_orders(status);
CREATE INDEX IF NOT EXISTS idx_po_branch ON purchase_orders(branch_id);
CREATE INDEX IF NOT EXISTS idx_po_created ON purchase_orders(created_at DESC);
-- ═══════════════════════════════════════════════════════════════════════════
-- PURCHASE ORDER ITEMS
-- ═══════════════════════════════════════════════════════════════════════════
CREATE TABLE IF NOT EXISTS purchase_order_items (
id SERIAL PRIMARY KEY,
po_id INTEGER NOT NULL REFERENCES purchase_orders(id) ON DELETE CASCADE,
inventory_id INTEGER REFERENCES inventory(id) ON DELETE SET NULL,
part_number VARCHAR(100),
name VARCHAR(300),
quantity INTEGER NOT NULL DEFAULT 1,
received_qty INTEGER DEFAULT 0,
unit_price NUMERIC(12,2) NOT NULL DEFAULT 0,
subtotal NUMERIC(12,2) NOT NULL DEFAULT 0,
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_poi_po ON purchase_order_items(po_id);
CREATE INDEX IF NOT EXISTS idx_poi_inventory ON purchase_order_items(inventory_id);