Files
Autoparts-DB/pos/migrations/v4.27_yonke_m1.sql
consultoria-as a386ca5b4a
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled
Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos
- F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado
- F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment
- F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings)
- F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant
- Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas
- Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant)
- Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant
- Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
2026-10-12 00:57:31 +00:00

114 lines
5.9 KiB
SQL

-- ═══════════════════════════════════════════════════════════════════════════
-- v4.27 — Yonke M1: used-parts (parts-usadas) data model
-- Additive + idempotent. Runs against tenant DBs.
--
-- Enables businesses that sell ONLY used parts (yonkes/deshuesaderos):
-- * condition (nuevo/usado/reconstruido) + grade (A/B/C/D)
-- * unique 1-of-1 pieces (is_unique_piece) with a donor vehicle
-- * multiple photos per item (inventory_images)
-- * ownership (propio/consigna) + consignment tables
-- * warranty expressed in days (for used parts)
-- ═══════════════════════════════════════════════════════════════════════════
-- ─── 1. inventory: condition / donor / ownership / unique-piece columns ─────
ALTER TABLE inventory
ADD COLUMN IF NOT EXISTS condition VARCHAR(20) DEFAULT 'nuevo',
ADD COLUMN IF NOT EXISTS condition_grade VARCHAR(2),
ADD COLUMN IF NOT EXISTS is_unique_piece BOOLEAN DEFAULT FALSE,
ADD COLUMN IF NOT EXISTS serial_number VARCHAR(100),
ADD COLUMN IF NOT EXISTS donor_vin VARCHAR(20),
ADD COLUMN IF NOT EXISTS donor_make VARCHAR(60),
ADD COLUMN IF NOT EXISTS donor_model VARCHAR(60),
ADD COLUMN IF NOT EXISTS donor_year INTEGER,
ADD COLUMN IF NOT EXISTS donor_mileage_km INTEGER,
ADD COLUMN IF NOT EXISTS ownership VARCHAR(20) DEFAULT 'propio',
ADD COLUMN IF NOT EXISTS acquisition_cost NUMERIC(12,2);
-- CHECK constraints (guarded so re-runs are safe)
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'inventory_condition_chk') THEN
ALTER TABLE inventory ADD CONSTRAINT inventory_condition_chk
CHECK (condition IS NULL OR condition IN ('nuevo','usado','reconstruido'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'inventory_ownership_chk') THEN
ALTER TABLE inventory ADD CONSTRAINT inventory_ownership_chk
CHECK (ownership IS NULL OR ownership IN ('propio','consigna'));
END IF;
IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'inventory_grade_chk') THEN
ALTER TABLE inventory ADD CONSTRAINT inventory_grade_chk
CHECK (condition_grade IS NULL OR condition_grade IN ('A','B','C','D'));
END IF;
END $$;
CREATE INDEX IF NOT EXISTS idx_inventory_condition
ON inventory(condition);
CREATE INDEX IF NOT EXISTS idx_inventory_donor_vin
ON inventory(donor_vin) WHERE donor_vin IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_inventory_donor_vehicle
ON inventory(donor_make, donor_model, donor_year) WHERE is_unique_piece;
-- ─── 2. Relax part_number NOT NULL so codeless unique pieces can exist ──────
-- A standard UNIQUE index in PostgreSQL already treats NULLs as distinct, so
-- the existing full unique index (idx_inventory_part_unique) keeps enforcing
-- uniqueness on non-null codes while allowing many NULLs. We deliberately do
-- NOT turn it into a partial index: that would break `ON CONFLICT (part_number)`
-- used by the importers.
ALTER TABLE inventory ALTER COLUMN part_number DROP NOT NULL;
-- ─── 3. inventory_images: multiple photos per item ─────────────────────────
CREATE TABLE IF NOT EXISTS inventory_images (
id SERIAL PRIMARY KEY,
inventory_id INTEGER NOT NULL REFERENCES inventory(id) ON DELETE CASCADE,
url VARCHAR(500) NOT NULL,
sort_order INTEGER NOT NULL DEFAULT 0,
is_primary BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_inventory_images_inv
ON inventory_images(inventory_id, sort_order);
CREATE UNIQUE INDEX IF NOT EXISTS idx_inventory_images_primary
ON inventory_images(inventory_id) WHERE is_primary;
-- Migrate the existing single image_url into the new table (first photo).
INSERT INTO inventory_images (inventory_id, url, sort_order, is_primary)
SELECT i.id, i.image_url, 0, TRUE
FROM inventory i
WHERE i.image_url IS NOT NULL AND i.image_url <> ''
AND NOT EXISTS (SELECT 1 FROM inventory_images ii WHERE ii.inventory_id = i.id);
-- ─── 4. warranties: express warranty in days (used parts) ──────────────────
DO $$
BEGIN
IF to_regclass('public.warranties') IS NOT NULL THEN
EXECUTE 'ALTER TABLE warranties ADD COLUMN IF NOT EXISTS warranty_days INTEGER';
END IF;
END $$;
-- ─── 5. Consignments (third-party parts held for sale) ─────────────────────
CREATE TABLE IF NOT EXISTS consignments (
id SERIAL PRIMARY KEY,
consignor_name VARCHAR(200) NOT NULL,
consignor_rfc VARCHAR(13),
consignor_phone VARCHAR(30),
commission_pct NUMERIC(5,2) DEFAULT 0,
status VARCHAR(20) NOT NULL DEFAULT 'open', -- open | closed | settled
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE IF NOT EXISTS consignment_items (
id SERIAL PRIMARY KEY,
consignment_id INTEGER NOT NULL REFERENCES consignments(id) ON DELETE CASCADE,
inventory_id INTEGER REFERENCES inventory(id) ON DELETE SET NULL,
agreed_price NUMERIC(12,2),
commission_amount NUMERIC(12,2),
status VARCHAR(20) NOT NULL DEFAULT 'in_stock', -- in_stock | sold | returned | settled
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_consignment_items_cons
ON consignment_items(consignment_id);
CREATE INDEX IF NOT EXISTS idx_consignment_items_inv
ON consignment_items(inventory_id);