Files
Autoparts-DB/pos/migrations/v1.0_initial.sql
consultoria-as ea29cc31c0 feat(catalog): supplier catalog cleanup, fuzzy matching, and navigation fixes
- Cleaned 137+ fake engine-displacement models from supplier imports
  (v3/v4 scripts: Chevrolet, Ford, Chrysler, Dodge, Jeep, Nissan, etc.)
- Removed 1,251+ corrupted models (INT. prefixes, year-suffix, torque specs,
  empty names, trailing-year variants)
- Migrated supplier tables to master DB (supplier_catalog,
  supplier_catalog_compat, supplier_catalog_interchange)
- Fixed _get_mye_ids_with_parts() to query supplier_catalog_compat from
  master DB so supplier-only vehicles appear for all tenants
- Added fuzzy model matcher with parenthesis stripping, noise suffix removal,
  compact matching, prefix/substring fallback, model aliases, and ±3 year
  proximity
- Matched compat rows: KEEP GREEN +14,152, KNADIAN +3,021, VAZLO +127,500,
  LUK +477, RAYBESTOS +1,743
- Added KNADIAN catalog importer with year-range expansion and future-year
  filtering
- Added VAZLO catalog importer with position parsing and SKU-in-model cleanup
- Added Keep Green, LUK, Yokomitsu, Raybestos catalog importers
- Cache clearing after cleanups (_classify_cache_*, nexus:mye_ids:*,
  nexus:brand_mye_counts:*)

Final match rates:
- KEEP GREEN: 90.3%
- VAZLO: 93.6%
- YOKOMITSU: 100.0%
- KNADIAN: 57.4%
- LUK: 51.0%
- RAYBESTOS: 55.9%
2026-06-09 07:47:42 +00:00

390 lines
15 KiB
SQL

-- /home/Autopartes/pos/migrations/v1.0_initial.sql
-- Tenant DB schema v1.0 — 21 tables
-- Source: design spec section 10 (tenant_{id} DB)
-- =====================
-- SUCURSALES
-- =====================
CREATE TABLE branches (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
address TEXT,
phone VARCHAR(20),
is_active BOOLEAN DEFAULT TRUE
);
-- =====================
-- EMPLEADOS Y PERMISOS
-- =====================
CREATE TABLE employees (
id SERIAL PRIMARY KEY,
name VARCHAR(200) NOT NULL,
email VARCHAR(200),
phone VARCHAR(20),
pin VARCHAR(100), -- hashed, 4 digitos
password_hash VARCHAR(200),
role VARCHAR(20) NOT NULL, -- owner, admin, cashier, warehouse, accountant
branch_id INTEGER REFERENCES branches(id),
max_discount_pct NUMERIC(5,2) DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE employee_permissions (
employee_id INTEGER REFERENCES employees(id),
permission VARCHAR(100) NOT NULL, -- 'pos.sell', 'inventory.adjust', etc.
PRIMARY KEY (employee_id, permission)
);
CREATE TABLE employee_sessions (
id SERIAL PRIMARY KEY,
employee_id INTEGER REFERENCES employees(id),
device_id VARCHAR(200),
token VARCHAR(500) NOT NULL,
expires_at TIMESTAMPTZ NOT NULL
);
-- =====================
-- CLIENTES
-- =====================
CREATE TABLE customers (
id SERIAL PRIMARY KEY,
branch_id INTEGER REFERENCES branches(id),
name VARCHAR(300) NOT NULL,
rfc VARCHAR(13),
razon_social VARCHAR(300),
regimen_fiscal VARCHAR(10), -- codigo SAT regimen
uso_cfdi VARCHAR(10) DEFAULT 'G03', -- codigo SAT uso CFDI
cp VARCHAR(5),
email VARCHAR(200),
phone VARCHAR(20),
address TEXT,
price_tier SMALLINT DEFAULT 1 CHECK (price_tier IN (1,2,3)), -- 1=mostrador, 2=taller, 3=mayoreo
credit_limit NUMERIC(12,2) DEFAULT 0,
credit_balance NUMERIC(12,2) DEFAULT 0, -- saldo actual de credito
is_active BOOLEAN DEFAULT TRUE,
vehicle_info JSONB, -- [{make, model, year, vin, plates}]
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================
-- INVENTARIO
-- =====================
CREATE TABLE inventory (
id SERIAL PRIMARY KEY,
branch_id INTEGER REFERENCES branches(id),
part_number VARCHAR(100) NOT NULL,
barcode VARCHAR(100),
name VARCHAR(300) NOT NULL,
description TEXT,
category_id INTEGER,
brand VARCHAR(100),
vehicle_compatibility JSONB,
unit VARCHAR(20) DEFAULT 'PZA',
cost NUMERIC(12,2) DEFAULT 0,
price_1 NUMERIC(12,2) DEFAULT 0, -- mostrador
price_2 NUMERIC(12,2) DEFAULT 0, -- taller
price_3 NUMERIC(12,2) DEFAULT 0, -- mayoreo
tax_rate NUMERIC(5,4) DEFAULT 0.16,
min_stock INTEGER DEFAULT 0,
max_stock INTEGER DEFAULT 0,
location VARCHAR(50), -- ubicacion en almacen
image_url VARCHAR(500),
is_active BOOLEAN DEFAULT TRUE,
catalog_part_id INTEGER, -- referencia a catalogo Nexus (via API, no FK)
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE inventory_operations (
id SERIAL PRIMARY KEY,
inventory_id INTEGER REFERENCES inventory(id),
branch_id INTEGER REFERENCES branches(id),
operation_type VARCHAR(20) NOT NULL, -- SALE, PURCHASE, RETURN, ADJUST, TRANSFER, INITIAL
quantity INTEGER NOT NULL, -- positivo o negativo
reference_id INTEGER,
reference_type VARCHAR(50), -- 'sale', 'purchase', 'return', etc.
cost_at_time NUMERIC(12,2),
employee_id INTEGER REFERENCES employees(id),
device_id VARCHAR(200),
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- Stock actual = SUM(inventory_operations.quantity) WHERE inventory_id=X AND branch_id=Y
-- =====================
-- VENTAS
-- =====================
CREATE TABLE sales (
id SERIAL PRIMARY KEY,
branch_id INTEGER REFERENCES branches(id),
customer_id INTEGER REFERENCES customers(id), -- NULL = publico general
employee_id INTEGER REFERENCES employees(id),
register_id INTEGER, -- FK cash_registers (deferred, table below)
sale_type VARCHAR(20) NOT NULL, -- cash, credit, mixed
payment_method VARCHAR(20), -- efectivo, transferencia, tarjeta, mixto
subtotal NUMERIC(12,2) NOT NULL,
discount_total NUMERIC(12,2) DEFAULT 0,
tax_total NUMERIC(12,2) NOT NULL,
total NUMERIC(12,2) NOT NULL,
amount_paid NUMERIC(12,2) DEFAULT 0,
change_given NUMERIC(12,2) DEFAULT 0,
metodo_pago_sat VARCHAR(3), -- PUE o PPD
forma_pago_sat VARCHAR(2), -- 01, 03, 04, 99
status VARCHAR(20) DEFAULT 'completed', -- completed, cancelled, returned
device_id VARCHAR(200),
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE sale_items (
id SERIAL PRIMARY KEY,
sale_id INTEGER REFERENCES sales(id),
inventory_id INTEGER REFERENCES inventory(id),
part_number VARCHAR(100),
name VARCHAR(300),
quantity INTEGER NOT NULL,
unit_price NUMERIC(12,2) NOT NULL, -- precio al momento de la venta
unit_cost NUMERIC(12,2), -- costo al momento de la venta
discount_pct NUMERIC(5,2) DEFAULT 0,
discount_amount NUMERIC(12,2) DEFAULT 0,
tax_rate NUMERIC(5,4) DEFAULT 0.16,
tax_amount NUMERIC(12,2) DEFAULT 0,
subtotal NUMERIC(12,2) NOT NULL,
clave_prod_serv VARCHAR(10), -- clave SAT
clave_unidad VARCHAR(10) -- clave unidad SAT
);
-- =====================
-- COTIZACIONES
-- =====================
CREATE TABLE quotations (
id SERIAL PRIMARY KEY,
branch_id INTEGER REFERENCES branches(id),
customer_id INTEGER REFERENCES customers(id),
employee_id INTEGER REFERENCES employees(id),
subtotal NUMERIC(12,2) NOT NULL,
tax_total NUMERIC(12,2) NOT NULL,
total NUMERIC(12,2) NOT NULL,
status VARCHAR(20) DEFAULT 'active', -- active, converted, expired, cancelled
valid_until DATE,
converted_sale_id INTEGER REFERENCES sales(id),
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE quotation_items (
id SERIAL PRIMARY KEY,
quotation_id INTEGER REFERENCES quotations(id),
inventory_id INTEGER REFERENCES inventory(id),
part_number VARCHAR(100),
name VARCHAR(300),
quantity INTEGER NOT NULL,
unit_price NUMERIC(12,2) NOT NULL,
discount_pct NUMERIC(5,2) DEFAULT 0,
tax_rate NUMERIC(5,4) DEFAULT 0.16,
subtotal NUMERIC(12,2) NOT NULL
);
-- =====================
-- APARTADOS (LAYAWAYS)
-- =====================
CREATE TABLE layaways (
id SERIAL PRIMARY KEY,
branch_id INTEGER REFERENCES branches(id),
customer_id INTEGER REFERENCES customers(id) NOT NULL,
employee_id INTEGER REFERENCES employees(id),
total NUMERIC(12,2) NOT NULL,
amount_paid NUMERIC(12,2) DEFAULT 0,
status VARCHAR(20) DEFAULT 'active', -- active, completed, cancelled
expires_at DATE,
converted_sale_id INTEGER REFERENCES sales(id),
notes TEXT,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE layaway_payments (
id SERIAL PRIMARY KEY,
layaway_id INTEGER REFERENCES layaways(id),
amount NUMERIC(12,2) NOT NULL,
payment_method VARCHAR(20),
reference VARCHAR(100),
employee_id INTEGER REFERENCES employees(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================
-- CAJA REGISTRADORA
-- =====================
CREATE TABLE cash_registers (
id SERIAL PRIMARY KEY,
branch_id INTEGER REFERENCES branches(id),
employee_id INTEGER REFERENCES employees(id),
register_number SMALLINT NOT NULL, -- numero de caja (1, 2, 3...)
opening_amount NUMERIC(12,2) NOT NULL, -- fondo inicial
closing_amount NUMERIC(12,2), -- monto contado al cerrar
expected_amount NUMERIC(12,2), -- monto esperado calculado
difference NUMERIC(12,2), -- closing - expected
status VARCHAR(10) DEFAULT 'open', -- open, closed
opened_at TIMESTAMPTZ DEFAULT NOW(),
closed_at TIMESTAMPTZ
);
CREATE TABLE cash_movements (
id SERIAL PRIMARY KEY,
register_id INTEGER REFERENCES cash_registers(id),
type VARCHAR(5) NOT NULL, -- 'in' o 'out'
amount NUMERIC(12,2) NOT NULL,
reason VARCHAR(300) NOT NULL,
employee_id INTEGER REFERENCES employees(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- =====================
-- FACTURACION (CFDI QUEUE)
-- =====================
CREATE TABLE cfdi_queue (
id SERIAL PRIMARY KEY,
sale_id INTEGER REFERENCES sales(id),
type VARCHAR(10) NOT NULL, -- ingreso, egreso, pago
xml_unsigned TEXT, -- XML generado por POS backend
xml_signed TEXT, -- XML firmado+timbrado por Horux
uuid_fiscal VARCHAR(36), -- UUID del SAT
status VARCHAR(20) DEFAULT 'pending', -- pending, sending, stamped, failed, cancelled
retry_count SMALLINT DEFAULT 0,
provisional_folio VARCHAR(20), -- PRE-XXXXX
error_message TEXT,
cancel_motive VARCHAR(2), -- 01, 02, 03, 04
cancel_replacement_uuid VARCHAR(36), -- UUID del CFDI sustituto (motivo 01)
created_at TIMESTAMPTZ DEFAULT NOW(),
stamped_at TIMESTAMPTZ
);
-- =====================
-- CONTABILIDAD
-- =====================
CREATE TABLE accounts (
id SERIAL PRIMARY KEY,
code VARCHAR(20) NOT NULL UNIQUE,
name VARCHAR(200) NOT NULL,
parent_id INTEGER REFERENCES accounts(id),
type VARCHAR(20) NOT NULL, -- activo, pasivo, capital, ingreso, costo, gasto
sat_code VARCHAR(20),
is_system BOOLEAN DEFAULT FALSE, -- cuentas predeterminadas no editables
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE journal_entries (
id SERIAL PRIMARY KEY,
entry_number INTEGER NOT NULL,
date DATE NOT NULL,
type VARCHAR(20), -- ingreso, egreso, diario, poliza
description TEXT,
reference_type VARCHAR(50), -- sale, purchase, cash_register, etc.
reference_id INTEGER,
status VARCHAR(20) DEFAULT 'posted', -- draft, posted, cancelled
created_by INTEGER REFERENCES employees(id),
is_auto BOOLEAN DEFAULT TRUE, -- generada automaticamente
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE journal_entry_lines (
id SERIAL PRIMARY KEY,
journal_entry_id INTEGER REFERENCES journal_entries(id),
account_id INTEGER REFERENCES accounts(id),
debit NUMERIC(14,2) DEFAULT 0,
credit NUMERIC(14,2) DEFAULT 0,
description TEXT
);
CREATE TABLE fiscal_periods (
id SERIAL PRIMARY KEY,
year SMALLINT NOT NULL,
month SMALLINT NOT NULL,
status VARCHAR(10) DEFAULT 'open', -- open, closed
closed_by INTEGER REFERENCES employees(id),
closed_at TIMESTAMPTZ,
UNIQUE (year, month)
);
-- =====================
-- AUDITORIA
-- =====================
CREATE TABLE audit_log (
id SERIAL PRIMARY KEY,
employee_id INTEGER REFERENCES employees(id),
action VARCHAR(50) NOT NULL,
entity_type VARCHAR(50),
entity_id INTEGER,
old_value JSONB,
new_value JSONB,
device_id VARCHAR(200),
ip_address VARCHAR(45),
branch_id INTEGER REFERENCES branches(id),
created_at TIMESTAMPTZ DEFAULT NOW()
);
-- INSERT-only: nunca UPDATE, nunca DELETE
-- =====================
-- INDEXES
-- =====================
CREATE INDEX idx_inv_ops_inventory ON inventory_operations(inventory_id);
CREATE INDEX idx_inv_ops_branch ON inventory_operations(branch_id);
CREATE INDEX idx_inv_ops_type ON inventory_operations(operation_type);
CREATE INDEX idx_inv_ops_created ON inventory_operations(created_at);
CREATE INDEX idx_sales_branch ON sales(branch_id);
CREATE INDEX idx_sales_customer ON sales(customer_id);
CREATE INDEX idx_sales_created ON sales(created_at);
CREATE INDEX idx_sales_status ON sales(status);
CREATE INDEX idx_sale_items_sale ON sale_items(sale_id);
CREATE INDEX idx_inventory_part ON inventory(part_number);
CREATE INDEX idx_inventory_barcode ON inventory(barcode);
CREATE INDEX idx_inventory_branch ON inventory(branch_id);
CREATE INDEX idx_customers_rfc ON customers(rfc);
CREATE INDEX idx_customers_name ON customers(name);
CREATE INDEX idx_cfdi_queue_status ON cfdi_queue(status);
CREATE INDEX idx_cfdi_queue_sale ON cfdi_queue(sale_id);
CREATE INDEX idx_journal_entries_date ON journal_entries(date);
CREATE INDEX idx_journal_lines_entry ON journal_entry_lines(journal_entry_id);
CREATE INDEX idx_audit_log_action ON audit_log(action);
CREATE INDEX idx_audit_log_entity ON audit_log(entity_type, entity_id);
CREATE INDEX idx_audit_log_employee ON audit_log(employee_id);
CREATE INDEX idx_audit_log_created ON audit_log(created_at);
CREATE UNIQUE INDEX idx_inventory_branch_part ON inventory(branch_id, part_number);
CREATE INDEX idx_employee_sessions_token ON employee_sessions(token);
-- =====================
-- PHYSICAL COUNTS (two-phase inventory count)
-- =====================
CREATE TABLE IF NOT EXISTS physical_counts (
id SERIAL PRIMARY KEY,
branch_id INTEGER NOT NULL REFERENCES branches(id),
status VARCHAR(20) NOT NULL DEFAULT 'draft', -- draft, approved, cancelled
notes TEXT,
employee_id INTEGER REFERENCES employees(id),
approved_by INTEGER REFERENCES employees(id),
created_at TIMESTAMP DEFAULT NOW(),
approved_at TIMESTAMP
);
CREATE TABLE IF NOT EXISTS physical_count_lines (
id SERIAL PRIMARY KEY,
physical_count_id INTEGER NOT NULL REFERENCES physical_counts(id),
inventory_id INTEGER NOT NULL REFERENCES inventory(id),
expected_quantity INTEGER NOT NULL,
counted_quantity INTEGER NOT NULL,
difference INTEGER NOT NULL
);
-- =====================
-- TENANT CONFIGURATION (key-value store for CFDI, Horux, etc.)
-- =====================
CREATE TABLE IF NOT EXISTS tenant_config (
key VARCHAR(100) PRIMARY KEY,
value TEXT,
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- Barcode sequence
CREATE SEQUENCE IF NOT EXISTS barcode_seq START 1;