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:
129
pos/services/catalog_modes.py
Normal file
129
pos/services/catalog_modes.py
Normal file
@@ -0,0 +1,129 @@
|
||||
"""
|
||||
Catalog modes — OEM vs Local bodega filtering for brand lists.
|
||||
|
||||
Two catalog modes coexist:
|
||||
|
||||
- 'oem' : Full TecDoc catalog (36+ vehicle brands from Apify import).
|
||||
Use this for any customer-facing "find your exact OEM part" flow.
|
||||
|
||||
- 'local' : Curated list of vehicle brands that local bodegas in Mexico
|
||||
actually service. Used while the TecDoc/Apify import is paused
|
||||
or to simplify navigation for customers who only care about
|
||||
what's available locally.
|
||||
|
||||
Both modes use the SAME navigation hierarchy (brand > model > year > engine >
|
||||
category > parts). Only the initial brand list is filtered.
|
||||
|
||||
Edit LOCAL_BODEGA_BRANDS below to add/remove brands as the bodega network grows.
|
||||
Brand names must match the `brands.name_brand` column in nexus_autoparts
|
||||
(case-sensitive, uppercase).
|
||||
"""
|
||||
|
||||
# ─── OEM mode — full North America coverage (imported from TecDoc) ──────────
|
||||
OEM_BRANDS_NA = (
|
||||
'ACURA', 'AUDI', 'BMW', 'BUICK', 'CADILLAC', 'CHEVROLET', 'CHRYSLER',
|
||||
'DODGE', 'FIAT', 'FORD', 'GMC', 'HONDA', 'HYUNDAI', 'INFINITI',
|
||||
'JAGUAR', 'JEEP', 'KIA', 'LAND ROVER', 'LEXUS', 'LINCOLN', 'MAZDA',
|
||||
'MERCEDES-BENZ', 'MINI', 'MITSUBISHI', 'NISSAN', 'PEUGEOT', 'PORSCHE',
|
||||
'RAM', 'RENAULT', 'SEAT', 'SUBARU', 'SUZUKI', 'TESLA', 'TOYOTA',
|
||||
'VOLVO', 'VW',
|
||||
)
|
||||
|
||||
# ─── Local mode — brands actually stocked by Mexican bodegas ────────────────
|
||||
# Popular Mexican market passenger cars + light trucks. Edit as needed.
|
||||
LOCAL_BODEGA_BRANDS = (
|
||||
'NISSAN', # Tsuru, Sentra, Versa, March, Tiida, Navara
|
||||
'VW', # Jetta, Pointer, Vento, Gol, Polo, Beetle
|
||||
'CHEVROLET', # Aveo, Chevy, Spark, Beat, Sonic, Sail
|
||||
'FORD', # Fiesta, Focus, EcoSport, Ranger, Figo
|
||||
'TOYOTA', # Corolla, Yaris, Hilux, Avanza, Tacoma
|
||||
'HONDA', # Civic, City, CR-V, Fit, HR-V
|
||||
'DODGE', # Attitude, Neon, Journey
|
||||
'CHRYSLER',
|
||||
'RAM', # Pickups
|
||||
'HYUNDAI', # Accent, Grand i10, Tucson, Elantra
|
||||
'KIA', # Rio, Forte, Sportage, Sorento
|
||||
'MAZDA', # 2, 3, CX-5, CX-30
|
||||
'MITSUBISHI', # Lancer, L200, Outlander
|
||||
'RENAULT', # Logan, Sandero, Duster, Stepway
|
||||
'SEAT', # Ibiza, Leon, Arona
|
||||
'FIAT', # Uno, Palio, Mobi
|
||||
'SUZUKI', # Swift, Vitara, Ignis, Ertiga
|
||||
'JEEP', # Compass, Wrangler, Grand Cherokee, Renegade
|
||||
'GMC', # Sierra, Terrain
|
||||
'BUICK', # Encore, Enclave (GM)
|
||||
)
|
||||
|
||||
|
||||
def get_brands_for_mode(mode):
|
||||
"""Return the tuple of allowed brand names for a given catalog mode.
|
||||
|
||||
Args:
|
||||
mode: 'oem' or 'local'. Anything else defaults to 'oem'.
|
||||
|
||||
Returns:
|
||||
A tuple of uppercase brand name strings.
|
||||
"""
|
||||
if mode == 'local':
|
||||
return LOCAL_BODEGA_BRANDS
|
||||
return OEM_BRANDS_NA
|
||||
|
||||
|
||||
def normalize_mode(raw):
|
||||
"""Normalize a raw mode string from a query param or header."""
|
||||
if not raw:
|
||||
return 'oem'
|
||||
cleaned = str(raw).strip().lower()
|
||||
return 'local' if cleaned == 'local' else 'oem'
|
||||
|
||||
|
||||
# ─── Local mode — priority aftermarket manufacturer brands ─────────────────
|
||||
# Ordered list. Brands earlier in the list are shown first and get the top
|
||||
# "priority" badge in the UI. Match `manufacturers.name_manufacture` (uppercase).
|
||||
#
|
||||
# Tier 1 (most trusted / most stocked in Mexican bodegas) — shown first.
|
||||
# Tier 2 (also popular but not always on every shelf) — shown second.
|
||||
# Anything not in either list is "other" and shown last.
|
||||
LOCAL_PRIORITY_MANUFACTURERS_TIER1 = (
|
||||
'BOSCH', # Universal — ignition, sensors, filters, wipers
|
||||
'GATES', # Bandas / timing belts
|
||||
'MONROE', # Amortiguadores
|
||||
'DENSO', # Ignition, cooling, AC
|
||||
'MANN-FILTER', # Filtros
|
||||
'MAHLE', # Filtros, pistones, termostatos
|
||||
'NGK', # Bujias
|
||||
'BREMBO', # Frenos premium
|
||||
'KYB', # Amortiguadores
|
||||
'SKF', # Rodamientos
|
||||
)
|
||||
|
||||
LOCAL_PRIORITY_MANUFACTURERS_TIER2 = (
|
||||
'DELPHI',
|
||||
'VALEO',
|
||||
'HELLA',
|
||||
'DAYCO',
|
||||
'SACHS',
|
||||
'CHAMPION',
|
||||
'WAGNER',
|
||||
'FRAM',
|
||||
'NSK',
|
||||
)
|
||||
|
||||
# Combined flat tuple (Tier1 followed by Tier2) — used for SQL IN clauses
|
||||
# and for determining "any priority" status.
|
||||
LOCAL_PRIORITY_MANUFACTURERS = LOCAL_PRIORITY_MANUFACTURERS_TIER1 + LOCAL_PRIORITY_MANUFACTURERS_TIER2
|
||||
|
||||
|
||||
def get_priority_tier(manufacturer_name):
|
||||
"""Return 1 for tier 1, 2 for tier 2, 3 for everything else.
|
||||
|
||||
Used both by the sort order and by the UI to render a priority badge.
|
||||
"""
|
||||
if not manufacturer_name:
|
||||
return 3
|
||||
name = manufacturer_name.upper()
|
||||
if name in LOCAL_PRIORITY_MANUFACTURERS_TIER1:
|
||||
return 1
|
||||
if name in LOCAL_PRIORITY_MANUFACTURERS_TIER2:
|
||||
return 2
|
||||
return 3
|
||||
Reference in New Issue
Block a user