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.4 CRM Enhanced: activities, tags, loyalty
-- Customer activity timeline (notes, calls, visits, emails, whatsapp)
CREATE TABLE IF NOT EXISTS customer_activities (
id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
activity_type VARCHAR(50) NOT NULL, -- 'note', 'call', 'visit', 'email', 'whatsapp', 'sale', 'payment', 'claim'
title VARCHAR(200),
description TEXT,
metadata JSONB, -- e.g. {"call_duration": 120, "email_subject": "..."}
employee_id INTEGER REFERENCES employees(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_cust_act_customer ON customer_activities(customer_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_cust_act_type ON customer_activities(activity_type);
-- Customer tags for segmentation
CREATE TABLE IF NOT EXISTS customer_tags (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL,
name VARCHAR(100) NOT NULL,
color VARCHAR(7) DEFAULT '#6B7280', -- hex color
description TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
UNIQUE(tenant_id, name)
);
CREATE INDEX IF NOT EXISTS idx_cust_tags_tenant ON customer_tags(tenant_id);
-- Many-to-many: customers <-> tags
CREATE TABLE IF NOT EXISTS customer_tag_assignments (
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
tag_id INTEGER NOT NULL REFERENCES customer_tags(id) ON DELETE CASCADE,
assigned_at TIMESTAMPTZ DEFAULT NOW(),
assigned_by INTEGER REFERENCES employees(id),
PRIMARY KEY (customer_id, tag_id)
);
-- Loyalty / rewards program
CREATE TABLE IF NOT EXISTS loyalty_points (
id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
points INTEGER NOT NULL DEFAULT 0,
points_type VARCHAR(20) NOT NULL DEFAULT 'earned', -- 'earned', 'redeemed', 'expired', 'bonus'
source_type VARCHAR(50), -- 'sale', 'referral', 'promotion', 'manual', 'redemption'
source_id INTEGER, -- sale_id or other reference
description TEXT,
expires_at TIMESTAMPTZ,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_loyalty_customer ON loyalty_points(customer_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_loyalty_expires ON loyalty_points(expires_at) WHERE expires_at IS NOT NULL;
-- Loyalty redemptions (rewards catalog)
CREATE TABLE IF NOT EXISTS loyalty_rewards (
id SERIAL PRIMARY KEY,
tenant_id INTEGER NOT NULL,
name VARCHAR(200) NOT NULL,
description TEXT,
points_cost INTEGER NOT NULL,
reward_type VARCHAR(50) DEFAULT 'discount', -- 'discount', 'free_product', 'service'
reward_value NUMERIC(12,2), -- discount amount or product ID
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Loyalty redemption history
CREATE TABLE IF NOT EXISTS loyalty_redemptions (
id SERIAL PRIMARY KEY,
customer_id INTEGER NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
reward_id INTEGER REFERENCES loyalty_rewards(id),
points_used INTEGER NOT NULL,
reward_value NUMERIC(12,2),
description TEXT,
employee_id INTEGER REFERENCES employees(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_loyalty_redem_customer ON loyalty_redemptions(customer_id);
-- Add loyalty balance to customers (denormalized for quick lookup)
ALTER TABLE customers ADD COLUMN IF NOT EXISTS loyalty_points_balance INTEGER DEFAULT 0;
ALTER TABLE customers ADD COLUMN IF NOT EXISTS loyalty_tier VARCHAR(50) DEFAULT 'bronze';