feat: complete session — catalog, marketplace, WhatsApp, peer-to-peer, install scripts
Major features: - Pixel-Perfect glassmorphism design (landing + POS + public catalog) - OEM/Local catalog toggle with Nexpart taxonomy (14 groups, 108 subgroups, 558 part types) - Marketplace B2B Phase 1 (bodegas, POs, status machine, WA+email notifications) - Peer-to-peer inventory (multi-instance, LAN discovery) - WhatsApp: photo→Vision AI, voice→Whisper, conversational quotations - Smart unified search (VIN/plate/part_number/keyword auto-detect) - Shop Supplies tab (vehicle-independent parts) - Chatbot AI fallback chain (5 models) + response cache - CSV inventory import tool + setup_instance.sh installer - Tablet-responsive CSS + sidebar toggle - Filters, export CSV, employee edit, business data save - Quotation system (WA→POS) with auto-print on confirmation - Live stats on landing page Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
149
sql/marketplace_schema.sql
Normal file
149
sql/marketplace_schema.sql
Normal file
@@ -0,0 +1,149 @@
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- Nexus Marketplace B2B — Phase 1 schema
|
||||
-- Target: nexus_autoparts (master DB)
|
||||
-- Date: 2026-04-09
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
--
|
||||
-- This migration is idempotent. Running it multiple times has no effect.
|
||||
-- All new tables use IF NOT EXISTS and ALTERs use IF NOT EXISTS on columns.
|
||||
--
|
||||
-- Tables:
|
||||
-- 1. bodegas — warehouse registry in the Nexus network
|
||||
-- 2. purchase_orders — PO headers
|
||||
-- 3. purchase_order_items — PO line items
|
||||
-- 4. po_status_history — audit trail for PO state changes
|
||||
--
|
||||
-- Altered tables:
|
||||
-- - warehouse_inventory (add bodega_id, currency, updated_at)
|
||||
--
|
||||
-- NOTE: the `users` table alter for marketplace_role + bodega_id lives in
|
||||
-- the TENANT databases (each refaccionaria), not the master DB. That
|
||||
-- migration is applied separately per-tenant via tenant_migrations.
|
||||
|
||||
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- 1. BODEGAS — warehouse registry
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
CREATE TABLE IF NOT EXISTS bodegas (
|
||||
id_bodega SERIAL PRIMARY KEY,
|
||||
name VARCHAR(200) NOT NULL,
|
||||
owner_name VARCHAR(200),
|
||||
whatsapp_phone VARCHAR(20) NOT NULL,
|
||||
email VARCHAR(200),
|
||||
city VARCHAR(100),
|
||||
state VARCHAR(50),
|
||||
address TEXT,
|
||||
verified BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
verified_at TIMESTAMP,
|
||||
commission_pct NUMERIC(5, 2) NOT NULL DEFAULT 0, -- reserved for Phase 3
|
||||
notes TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_bodegas_verified ON bodegas (verified) WHERE verified = TRUE;
|
||||
CREATE INDEX IF NOT EXISTS idx_bodegas_city ON bodegas (city);
|
||||
|
||||
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- 2. WAREHOUSE_INVENTORY — extend existing table
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- Existing schema has: id_inventory, user_id, part_id, price, stock_quantity,
|
||||
-- min_order_quantity, warehouse_location, updated_at.
|
||||
-- We add bodega_id (FK to the new table) and currency, keeping user_id as
|
||||
-- the legacy owner pointer.
|
||||
|
||||
ALTER TABLE warehouse_inventory
|
||||
ADD COLUMN IF NOT EXISTS bodega_id INTEGER REFERENCES bodegas(id_bodega),
|
||||
ADD COLUMN IF NOT EXISTS currency VARCHAR(3) NOT NULL DEFAULT 'MXN';
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_wi_bodega ON warehouse_inventory (bodega_id);
|
||||
|
||||
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- 3. PURCHASE_ORDERS — PO headers
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- Status state machine:
|
||||
-- draft → submitted → confirmed → ready → delivered → closed
|
||||
-- ↘ rejected (terminal)
|
||||
|
||||
CREATE TABLE IF NOT EXISTS purchase_orders (
|
||||
id_po SERIAL PRIMARY KEY,
|
||||
buyer_tenant_id INTEGER NOT NULL,
|
||||
buyer_user_id INTEGER NOT NULL,
|
||||
buyer_phone VARCHAR(20),
|
||||
buyer_email VARCHAR(200),
|
||||
buyer_display_name VARCHAR(200),
|
||||
bodega_id INTEGER NOT NULL REFERENCES bodegas(id_bodega),
|
||||
status VARCHAR(20) NOT NULL DEFAULT 'draft',
|
||||
total_amount NUMERIC(12, 2),
|
||||
currency VARCHAR(3) NOT NULL DEFAULT 'MXN',
|
||||
buyer_notes TEXT,
|
||||
seller_notes TEXT,
|
||||
delivery_method VARCHAR(50), -- 'pickup' | 'delivery'
|
||||
delivery_address TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW(),
|
||||
submitted_at TIMESTAMP,
|
||||
confirmed_at TIMESTAMP,
|
||||
ready_at TIMESTAMP,
|
||||
delivered_at TIMESTAMP,
|
||||
closed_at TIMESTAMP,
|
||||
CONSTRAINT chk_po_status CHECK (
|
||||
status IN ('draft', 'submitted', 'confirmed', 'rejected', 'ready', 'delivered', 'closed')
|
||||
)
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_po_buyer ON purchase_orders (buyer_tenant_id, buyer_user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_po_bodega ON purchase_orders (bodega_id, status);
|
||||
CREATE INDEX IF NOT EXISTS idx_po_status ON purchase_orders (status);
|
||||
CREATE INDEX IF NOT EXISTS idx_po_created ON purchase_orders (created_at DESC);
|
||||
|
||||
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- 4. PURCHASE_ORDER_ITEMS — line items
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
CREATE TABLE IF NOT EXISTS purchase_order_items (
|
||||
id_po_item SERIAL PRIMARY KEY,
|
||||
po_id INTEGER NOT NULL REFERENCES purchase_orders(id_po) ON DELETE CASCADE,
|
||||
part_id INTEGER NOT NULL REFERENCES parts(id_part),
|
||||
oem_part_number VARCHAR(100),
|
||||
part_name VARCHAR(300),
|
||||
manufacturer VARCHAR(200),
|
||||
quantity INTEGER NOT NULL CHECK (quantity > 0),
|
||||
unit_price NUMERIC(12, 2),
|
||||
subtotal NUMERIC(12, 2),
|
||||
confirmed_qty INTEGER, -- bodega may adjust after confirmation
|
||||
notes TEXT
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_po_items_po ON purchase_order_items (po_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_po_items_part ON purchase_order_items (part_id);
|
||||
|
||||
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- 5. PO_STATUS_HISTORY — audit trail
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
CREATE TABLE IF NOT EXISTS po_status_history (
|
||||
id_history SERIAL PRIMARY KEY,
|
||||
po_id INTEGER NOT NULL REFERENCES purchase_orders(id_po) ON DELETE CASCADE,
|
||||
from_status VARCHAR(20),
|
||||
to_status VARCHAR(20) NOT NULL,
|
||||
actor_user_id INTEGER,
|
||||
actor_kind VARCHAR(20), -- 'buyer' | 'seller' | 'system' | 'admin'
|
||||
note TEXT,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_po_history_po ON po_status_history (po_id, created_at);
|
||||
|
||||
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- Verification queries
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
-- Run after applying to check everything landed:
|
||||
-- SELECT COUNT(*) FROM bodegas;
|
||||
-- SELECT COUNT(*) FROM purchase_orders;
|
||||
-- SELECT column_name, data_type FROM information_schema.columns WHERE table_name = 'warehouse_inventory' AND column_name IN ('bodega_id', 'currency');
|
||||
16
sql/marketplace_tenant_users.sql
Normal file
16
sql/marketplace_tenant_users.sql
Normal file
@@ -0,0 +1,16 @@
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
-- Marketplace — per-tenant employees table migration
|
||||
-- Apply to: tenant_template, tenant_refaccionaria_demo, tenant_acct_test,
|
||||
-- and any future tenant DB.
|
||||
-- ═══════════════════════════════════════════════════════════════════════
|
||||
|
||||
ALTER TABLE employees
|
||||
ADD COLUMN IF NOT EXISTS marketplace_role VARCHAR(20) NOT NULL DEFAULT 'buyer',
|
||||
ADD COLUMN IF NOT EXISTS bodega_id INTEGER;
|
||||
|
||||
-- Valid values: 'buyer' (refaccionaria/taller) | 'seller' (bodega) | 'admin'
|
||||
-- bodega_id references master.bodegas(id_bodega). No FK because that table
|
||||
-- lives in a different database — the app enforces referential integrity.
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_marketplace_role ON employees (marketplace_role);
|
||||
CREATE INDEX IF NOT EXISTS idx_employees_bodega ON employees (bodega_id) WHERE bodega_id IS NOT NULL;
|
||||
Reference in New Issue
Block a user