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>
130 lines
4.8 KiB
Python
130 lines
4.8 KiB
Python
"""
|
|
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
|