diff --git a/pos/services/catalog_service.py b/pos/services/catalog_service.py index 545a526..20311a5 100644 --- a/pos/services/catalog_service.py +++ b/pos/services/catalog_service.py @@ -12,6 +12,8 @@ PERFORMANCE: vehicle_parts has 14B+ rows. Every query MUST: import re +from services.na_models import is_na_model + # ───────────────────────────────────────────────────────────────────────────── # VEHICLE HIERARCHY NAVIGATION @@ -54,10 +56,18 @@ def get_brands(master_conn, year_id=None): return [{'id_brand': r[0], 'name_brand': r[1]} for r in rows] -def get_models(master_conn, brand_id, year_id=None): - """Get models for a brand that have MYE entries. - If year_id is provided, only models available for that year.""" +def get_models(master_conn, brand_id, year_id=None, brand_name=None): + """Get models for a brand that have MYE entries, filtered to North America. + If year_id is provided, only models available for that year. + brand_name is used for NA filtering; looked up from DB if not provided.""" cur = master_conn.cursor() + + # Resolve brand_name for NA filter if not provided + if not brand_name: + cur.execute("SELECT name_brand FROM brands WHERE id_brand = %s", (brand_id,)) + row = cur.fetchone() + brand_name = row[0] if row else '' + if year_id: cur.execute(""" SELECT DISTINCT m.id_model, m.name_model @@ -76,7 +86,13 @@ def get_models(master_conn, brand_id, year_id=None): """, (brand_id,)) rows = cur.fetchall() cur.close() - return [{'id_model': r[0], 'name_model': r[1]} for r in rows] + + # Filter to North America models only + return [ + {'id_model': r[0], 'name_model': r[1]} + for r in rows + if is_na_model(brand_name, r[1]) + ] def get_years(master_conn, model_id): diff --git a/pos/services/na_models.py b/pos/services/na_models.py new file mode 100644 index 0000000..33cc76a --- /dev/null +++ b/pos/services/na_models.py @@ -0,0 +1,502 @@ +# /home/Autopartes/pos/services/na_models.py +"""North America model filter -- models sold in Mexico, USA, and Canada. + +TecDoc includes models from all global markets (Europe, Asia, Middle East, etc.) +but our catalog only needs models actually sold in North America. + +Usage: + from services.na_models import is_na_model + if is_na_model('TOYOTA', 'AGYA (B1_)'): # False -- not sold in NA + if is_na_model('TOYOTA', 'Corolla (_E21_)'): # True -- sold in NA + if is_na_model('NISSAN', 'TSURU'): # True -- Mexico-specific + +Matching logic: + - Case-insensitive + - TecDoc model name must START WITH one of the listed base names + - e.g. base "COROLLA" matches "Corolla (_E21_)", "Corolla Hatchback", etc. + - Unknown brands (not in NA_MODELS) pass through unfiltered +""" + +# ──────────────────────────────────────────────────────────────────────────── +# Base model names sold in North America (case-insensitive prefix matching) +# Includes: current (2020-2026), recent discontinued (2010-2019), +# classic/legacy still serviced (2000-2009), +# and Mexico-specific models. +# ──────────────────────────────────────────────────────────────────────────── + +NA_MODELS = { + + # ── TOYOTA ────────────────────────────────────────────────────────────── + 'TOYOTA': [ + '4 RUNNER', '86', 'AVALON', 'BZ4X', + 'C-HR', 'CAMRY', 'CELICA', 'COROLLA', 'CORONA', 'CRESSIDA', + 'ECHO', 'FJ CRUISER', + 'GR86', 'GR SUPRA', 'GRAND HIGHLANDER', + 'HIGHLANDER', + 'LAND CRUISER', + 'MATRIX', 'MIRAI', 'MR2', + 'PASEO', 'PICKUP', 'PRIUS', + 'RAV4', 'RAV 4', + 'SEQUOIA', 'SIENNA', 'SOLARA', 'SUPRA', + 'TACOMA', 'TERCEL', 'T100', 'TUNDRA', + 'VENZA', + 'YARIS', + ], + + # ── NISSAN ────────────────────────────────────────────────────────────── + 'NISSAN': [ + '200SX', '240SX', '300ZX', '350Z', '370Z', + 'ALTIMA', 'APRIO', 'ARIYA', 'ARMADA', + 'FRONTIER', + 'GT-R', + 'JUKE', + 'KICKS', + 'LEAF', + 'MARCH', 'MAXIMA', 'MURANO', + 'NOTE', 'NP300', 'NV', + 'PATHFINDER', 'PLATINA', + 'QUEST', + 'ROGUE', 'ROGUE SPORT', + 'SENTRA', + 'TIIDA', 'TITAN', 'TSURU', + 'V-DRIVE', 'VERSA', + 'X-TRAIL', 'XTERRA', + 'Z', + ], + + # ── FORD ──────────────────────────────────────────────────────────────── + 'FORD': [ + 'BRONCO', 'BRONCO SPORT', + 'CONTOUR', 'CROWN VICTORIA', + 'ECOSPORT', 'EDGE', 'ESCAPE', 'ESCORT', 'EXCURSION', 'EXPEDITION', + 'EXPLORER', + 'F 150', 'F-150', 'F 250', 'F-250', 'F 350', 'F-350', 'F 450', 'F-450', + 'F 550', 'F-550', 'FIESTA', 'FIGO', 'FIVE HUNDRED', 'FLEX', 'FOCUS', + 'FREESTAR', 'FREESTYLE', 'FUSION', + 'IKON', + 'LOBO', + 'MAVERICK', 'MUSTANG', + 'PROBE', + 'RANGER', + 'TAURUS', 'THUNDERBIRD', 'TRANSIT', 'TRANSIT CONNECT', + 'WINDSTAR', + ], + + # ── CHEVROLET ─────────────────────────────────────────────────────────── + 'CHEVROLET': [ + 'ASTRA', 'ASTRO', 'AVALANCHE', 'AVEO', + 'BEAT', 'BLAZER', 'BOLT', + 'CAMARO', 'CAPTIVA', 'CAVALIER', 'CHEVY', 'COBALT', 'COLORADO', + 'CORSA', 'CORVETTE', 'CRUZE', + 'EQUINOX', 'EXPRESS', + 'GROVE', + 'HHR', + 'IMPALA', + 'LUMINA', + 'MALIBU', 'MERIVA', 'MONTE CARLO', + 'ONIX', 'ORLANDO', + 'PRIZM', + 'S-10', 'S10', 'SILVERADO', 'SONIC', 'SPARK', 'SPIN', 'SUBURBAN', + 'TAHOE', 'TORNADO', 'TRACKER', 'TRAIL', 'TRAILBLAZER', 'TRAVERSE', + 'TRAX', + 'VENTURE', + 'ZAFIRA', + ], + + # ── VW (VOLKSWAGEN) ──────────────────────────────────────────────────── + 'VW': [ + 'ATLAS', + 'BEETLE', 'BORA', + 'CABRIO', 'CC', 'CLASICO', 'CROSSFOX', + 'DERBY', + 'EOS', 'EUROVAN', + 'GLI', 'GOL', 'GOLF', 'GTI', + 'ID.4', 'ID.BUZZ', + 'JETTA', + 'LUPO', + 'NEW BEETLE', 'NIVUS', + 'PASSAT', 'POINTER', 'POLO', + 'RABBIT', 'ROUTAN', + 'SAVEIRO', 'SEDAN', 'SPORTVAN', 'SURAN', + 'T-CROSS', 'TAOS', 'TIGUAN', 'TOUAREG', + 'UP', + 'VENTO', 'VIRTUS', + ], + + # ── HONDA ─────────────────────────────────────────────────────────────── + 'HONDA': [ + 'ACCORD', + 'BR-V', + 'CITY', 'CIVIC', 'CR-V', 'CR-Z', 'CROSSTOUR', + 'DEL SOL', + 'ELEMENT', + 'FIT', + 'HR-V', + 'INSIGHT', + 'ODYSSEY', + 'PASSPORT', 'PILOT', 'PRELUDE', 'PROLOGUE', + 'RIDGELINE', + 'S2000', + ], + + # ── HYUNDAI ───────────────────────────────────────────────────────────── + 'HYUNDAI': [ + 'ACCENT', 'AZERA', + 'CRETA', + 'ELANTRA', 'ENTOURAGE', + 'GENESIS', + 'IONIQ', + 'KONA', + 'NEXO', + 'PALISADE', + 'SANTA CRUZ', 'SANTA FE', 'SONATA', 'STAREX', + 'TIBURON', 'TUCSON', + 'VELOSTER', 'VENUE', 'VERACRUZ', + 'XG', + ], + + # ── KIA ───────────────────────────────────────────────────────────────── + 'KIA': [ + 'AMANTI', + 'CARNIVAL', 'CERATO', + 'EV6', 'EV9', + 'FORTE', + 'K5', + 'NIRO', + 'OPTIMA', + 'RIO', + 'SEDONA', 'SELTOS', 'SORENTO', 'SOUL', 'SPECTRA', 'SPORTAGE', + 'STINGER', + 'TELLURIDE', + ], + + # ── MAZDA ─────────────────────────────────────────────────────────────── + 'MAZDA': [ + '2', '3', '5', '6', + '323', '626', '929', + 'B-SERIE', 'B2300', 'B2500', 'B3000', 'B4000', + 'CX-3', 'CX-30', 'CX-5', 'CX-50', 'CX-7', 'CX-70', 'CX-9', 'CX-90', + 'MIATA', 'MILLENIA', 'MPV', 'MX-3', 'MX-5', 'MX-30', + 'PROTEGE', + 'RX-7', 'RX-8', + 'TRIBUTE', + ], + + # ── BMW ───────────────────────────────────────────────────────────────── + 'BMW': [ + '1', '2', '3', '4', '5', '6', '7', '8', + 'I3', 'I4', 'I7', 'I8', 'IX', 'IX3', + 'M2', 'M3', 'M4', 'M5', 'M6', 'M8', + 'X1', 'X2', 'X3', 'X4', 'X5', 'X6', 'X7', 'XM', + 'Z3', 'Z4', 'Z8', + ], + + # ── MERCEDES-BENZ ────────────────────────────────────────────────────── + 'MERCEDES-BENZ': [ + 'A', 'AMG GT', + 'B', + 'C', 'CL', 'CLA', 'CLK', 'CLS', + 'E', 'EQB', 'EQE', 'EQS', + 'G', 'GL', 'GLA', 'GLB', 'GLC', 'GLE', 'GLK', 'GLS', + 'MAYBACH', 'METRIS', 'ML', + 'R', + 'S', 'SL', 'SLC', 'SLK', 'SPRINTER', + ], + + # ── AUDI ──────────────────────────────────────────────────────────────── + 'AUDI': [ + 'A1', 'A3', 'A4', 'A5', 'A6', 'A7', 'A8', + 'E-TRON', 'ETRON', + 'Q3', 'Q4', 'Q5', 'Q7', 'Q8', + 'R8', 'RS', 'RS3', 'RS4', 'RS5', 'RS6', 'RS7', + 'S3', 'S4', 'S5', 'S6', 'S7', 'S8', 'SQ5', 'SQ7', 'SQ8', + 'TT', + ], + + # ── ACURA ─────────────────────────────────────────────────────────────── + 'ACURA': [ + 'CL', 'CSX', + 'EL', + 'ILX', 'INTEGRA', 'INTEGRA (2023)', + 'MDX', + 'NSX', + 'RDX', 'RL', 'RLX', 'RSX', + 'TL', 'TLX', 'TSX', + 'ZDX', + ], + + # ── LEXUS ─────────────────────────────────────────────────────────────── + 'LEXUS': [ + 'CT', + 'ES', + 'GS', 'GX', + 'IS', + 'LC', 'LFA', 'LS', 'LX', + 'NX', + 'RC', 'RX', 'RZ', + 'SC', + 'TX', + 'UX', + ], + + # ── INFINITI ──────────────────────────────────────────────────────────── + 'INFINITI': [ + 'EX', 'FX', + 'G', 'I', + 'JX', + 'M', + 'Q40', 'Q50', 'Q60', 'Q70', 'QX30', 'QX50', 'QX55', 'QX56', + 'QX60', 'QX70', 'QX80', + ], + + # ── CHRYSLER ──────────────────────────────────────────────────────────── + 'CHRYSLER': [ + '200', '300', + 'ASPEN', + 'CIRRUS', 'CONCORDE', 'CROSSFIRE', + 'GRAND VOYAGER', + 'NEON', + 'PACIFICA', 'PT CRUISER', + 'SEBRING', + 'TOWN', 'TOWN & COUNTRY', + 'VOYAGER', + ], + + # ── DODGE ─────────────────────────────────────────────────────────────── + 'DODGE': [ + 'ATOS', 'ATTITUDE', 'AVENGER', + 'CALIBER', 'CARAVAN', 'CHALLENGER', 'CHARGER', + 'DAKOTA', 'DART', 'DURANGO', + 'GRAND CARAVAN', + 'HORNET', + 'I10', + 'JOURNEY', + 'MAGNUM', + 'NEON', 'NITRO', + 'RAM', 'STRATUS', + 'VIPER', 'VISION', + ], + + # ── JEEP ──────────────────────────────────────────────────────────────── + 'JEEP': [ + 'CHEROKEE', 'COMMANDER', 'COMPASS', + 'GLADIATOR', 'GRAND CHEROKEE', 'GRAND WAGONEER', + 'LIBERTY', + 'PATRIOT', + 'RENEGADE', + 'WAGONEER', 'WRANGLER', + ], + + # ── RAM ───────────────────────────────────────────────────────────────── + 'RAM': [ + '1500', '2500', '3500', '4500', '5500', + '700', + 'PROMASTER', + ], + + # ── GMC ───────────────────────────────────────────────────────────────── + 'GMC': [ + 'ACADIA', + 'CANYON', + 'ENVOY', + 'HUMMER EV', + 'JIMMY', + 'SAFARI', 'SAVANA', 'SIERRA', 'SONOMA', + 'TERRAIN', + 'YUKON', + ], + + # ── BUICK ─────────────────────────────────────────────────────────────── + 'BUICK': [ + 'CENTURY', + 'ENCLAVE', 'ENCORE', 'ENVISION', 'ENVISTA', + 'LACROSSE', 'LESABRE', 'LUCERNE', + 'PARK AVENUE', + 'RAINIER', 'REGAL', 'RENDEZVOUS', 'RIVIERA', + 'TERRAZA', + 'VERANO', + ], + + # ── CADILLAC ──────────────────────────────────────────────────────────── + 'CADILLAC': [ + 'ATS', + 'CT4', 'CT5', 'CT6', 'CTS', + 'DEVILLE', 'DTS', + 'ELDORADO', 'ESCALADE', + 'LYRIQ', + 'SEVILLE', 'SRX', 'STS', + 'XT4', 'XT5', 'XT6', 'XTS', + ], + + # ── LINCOLN ───────────────────────────────────────────────────────────── + 'LINCOLN': [ + 'AVIATOR', + 'CONTINENTAL', 'CORSAIR', + 'LS', + 'MKC', 'MKS', 'MKT', 'MKX', 'MKZ', + 'NAUTILUS', 'NAVIGATOR', + 'TOWN CAR', + 'ZEPHYR', + ], + + # ── SUBARU ────────────────────────────────────────────────────────────── + 'SUBARU': [ + 'ASCENT', + 'BAJA', 'BRZ', + 'CROSSTREK', + 'FORESTER', + 'IMPREZA', + 'LEGACY', + 'OUTBACK', + 'SOLTERRA', + 'TRIBECA', + 'WRX', + 'XV', + ], + + # ── MITSUBISHI ────────────────────────────────────────────────────────── + 'MITSUBISHI': [ + 'ECLIPSE', 'ECLIPSE CROSS', 'ENDEAVOR', + 'GALANT', + 'L200', 'LANCER', + 'MIRAGE', 'MONTERO', + 'OUTLANDER', + 'RAIDER', 'RVR', + ], + + # ── SUZUKI ────────────────────────────────────────────────────────────── + 'SUZUKI': [ + 'AERIO', + 'CIAZ', + 'EQUATOR', + 'FORENZA', + 'GRAND VITARA', + 'IGNIS', + 'KIZASHI', + 'RENO', + 'S-CROSS', 'SWIFT', 'SX4', + 'VITARA', + 'XL-7', 'XL7', + ], + + # ── RENAULT ───────────────────────────────────────────────────────────── + 'RENAULT': [ + 'CAPTUR', 'CLIO', 'COLEOS', + 'DUSTER', + 'FLUENCE', + 'KANGOO', 'KOLEOS', 'KWID', + 'LOGAN', + 'MEGANE', + 'OROCH', + 'SAFRANE', 'SANDERO', 'SCENIC', 'STEPWAY', + ], + + # ── PEUGEOT ───────────────────────────────────────────────────────────── + 'PEUGEOT': [ + '2008', '206', '207', '208', '3008', '301', '306', '307', '308', + '405', '5008', '508', + 'MANAGER', 'PARTNER', 'RCZ', 'RIFTER', + ], + + # ── SEAT ──────────────────────────────────────────────────────────────── + 'SEAT': [ + 'ARONA', 'ATECA', + 'CORDOBA', + 'IBIZA', + 'LEON', + 'TARRACO', 'TOLEDO', + ], + + # ── FIAT ──────────────────────────────────────────────────────────────── + 'FIAT': [ + '124', '500', '500L', '500X', + 'DUCATO', + 'FREEMONT', + 'LINEA', + 'MOBI', + 'PALIO', 'PUNTO', 'PULSE', + 'TIPO', 'TORO', + 'UNO', + ], + + # ── MINI ──────────────────────────────────────────────────────────────── + 'MINI': [ + 'CLUBMAN', 'CONVERTIBLE', 'COUNTRYMAN', 'COUPE', + 'HARDTOP', 'MINI', + 'PACEMAN', + ], + + # ── VOLVO ─────────────────────────────────────────────────────────────── + 'VOLVO': [ + 'C30', 'C40', 'C70', + 'EX30', 'EX90', + 'S40', 'S60', 'S70', 'S80', 'S90', + 'V40', 'V50', 'V60', 'V70', 'V90', + 'XC40', 'XC60', 'XC70', 'XC90', + ], + + # ── PORSCHE ───────────────────────────────────────────────────────────── + 'PORSCHE': [ + '718', '911', + 'BOXSTER', + 'CAYENNE', 'CAYMAN', + 'MACAN', + 'PANAMERA', + 'TAYCAN', + ], + + # ── JAGUAR ────────────────────────────────────────────────────────────── + 'JAGUAR': [ + 'E-PACE', + 'F-PACE', 'F-TYPE', + 'I-PACE', + 'S-TYPE', + 'X-TYPE', 'XE', 'XF', 'XJ', 'XK', + ], + + # ── LAND ROVER ────────────────────────────────────────────────────────── + 'LAND ROVER': [ + 'DEFENDER', 'DISCOVERY', 'DISCOVERY SPORT', + 'FREELANDER', + 'LR2', 'LR3', 'LR4', + 'RANGE ROVER', + ], + + # ── TESLA ─────────────────────────────────────────────────────────────── + 'TESLA': [ + 'CYBERTRUCK', + 'MODEL 3', 'MODEL S', 'MODEL X', 'MODEL Y', + ], +} + +# Pre-compute upper-cased sets for fast lookup +_NA_MODELS_UPPER = { + brand: [m.upper() for m in models] + for brand, models in NA_MODELS.items() +} + + +def is_na_model(brand_name, model_name): + """Check if a TecDoc model is sold in North America. + + Args: + brand_name: Brand name (e.g. 'TOYOTA', 'VW') + model_name: TecDoc model name (e.g. 'Corolla (_E21_)', 'AGYA (B1_)') + + Returns: + True if the model is sold in NA, or if the brand is not in our list + (unknown brands pass through unfiltered). + """ + brand_upper = brand_name.upper().strip() + model_upper = model_name.upper().strip() + + # If brand not in our list, allow all models (don't filter unknown brands) + if brand_upper not in _NA_MODELS_UPPER: + return True + + # Check if model name starts with any known NA model + for na_model in _NA_MODELS_UPPER[brand_upper]: + if model_upper.startswith(na_model): + return True + + return False