From 10e318bfd7e7abb5a9f91fd4186817a881839d44 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Thu, 2 Apr 2026 08:06:34 +0000 Subject: [PATCH] feat: dropdown filtra marcas y modelos por ano seleccionado Al seleccionar ano, solo muestra marcas/modelos disponibles para ese ano. Toyota 2020: 92 modelos vs 625 sin filtro. Co-Authored-By: Claude Opus 4.6 (1M context) --- dashboard/server.py | 25 +++++++++++--- pos/blueprints/catalog_bp.py | 6 ++-- pos/services/catalog_service.py | 59 ++++++++++++++++++++++----------- pos/static/js/catalog.js | 8 +++-- 4 files changed, 69 insertions(+), 29 deletions(-) diff --git a/dashboard/server.py b/dashboard/server.py index ebacd95..7c67e98 100644 --- a/dashboard/server.py +++ b/dashboard/server.py @@ -248,26 +248,35 @@ NORTH_AMERICA_BRANDS = REGION_BRANDS['north-america'] @app.route('/api/catalog/brands') def api_catalog_brands(): region = request.args.get('region', 'north-america') + year_id = request.args.get('year_id', type=int) session = Session() try: + params = {} + year_filter = "" + if year_id: + year_filter = " AND mye.year_id = :year_id" + params['year_id'] = year_id + if region == 'all': rows = session.execute(text(""" SELECT DISTINCT b.id_brand, b.name_brand FROM brands b JOIN models m ON m.brand_id = b.id_brand JOIN model_year_engine mye ON mye.model_id = m.id_model + WHERE 1=1""" + year_filter + """ ORDER BY b.name_brand - """)).mappings().all() + """), params).mappings().all() else: brand_list = list(REGION_BRANDS.get(region, NORTH_AMERICA_BRANDS)) + params['brands'] = brand_list rows = session.execute(text(""" SELECT DISTINCT b.id_brand, b.name_brand FROM brands b JOIN models m ON m.brand_id = b.id_brand JOIN model_year_engine mye ON mye.model_id = m.id_model - WHERE b.name_brand = ANY(:brands) + WHERE b.name_brand = ANY(:brands)""" + year_filter + """ ORDER BY b.name_brand - """), {'brands': brand_list}).mappings().all() + """), params).mappings().all() return jsonify([{'id_brand': r['id_brand'], 'name_brand': r['name_brand']} for r in rows]) finally: session.close() @@ -276,17 +285,23 @@ def api_catalog_brands(): @app.route('/api/catalog/models') def api_catalog_models(): brand_id = request.args.get('brand_id', type=int) + year_id = request.args.get('year_id', type=int) if not brand_id: return jsonify({'error': 'brand_id required'}), 400 session = Session() try: + params = {'brand_id': brand_id} + year_filter = "" + if year_id: + year_filter = " AND mye.year_id = :year_id" + params['year_id'] = year_id rows = session.execute(text(""" SELECT DISTINCT m.id_model, m.name_model FROM models m JOIN model_year_engine mye ON mye.model_id = m.id_model - WHERE m.brand_id = :brand_id + WHERE m.brand_id = :brand_id""" + year_filter + """ ORDER BY m.name_model - """), {'brand_id': brand_id}).mappings().all() + """), params).mappings().all() return jsonify([{'id_model': r['id_model'], 'name_model': r['name_model']} for r in rows]) finally: session.close() diff --git a/pos/blueprints/catalog_bp.py b/pos/blueprints/catalog_bp.py index fad36cc..bab0e1f 100644 --- a/pos/blueprints/catalog_bp.py +++ b/pos/blueprints/catalog_bp.py @@ -62,8 +62,9 @@ def _master_only(fn): @catalog_bp.route('/brands', methods=['GET']) @require_auth('catalog.view') def brands(): + year_id = request.args.get('year_id', type=int) def _do(master): - data = catalog_service.get_brands(master) + data = catalog_service.get_brands(master, year_id=year_id) return jsonify({'data': data}) return _master_only(_do) @@ -72,10 +73,11 @@ def brands(): @require_auth('catalog.view') def models(): brand_id = request.args.get('brand_id', type=int) + year_id = request.args.get('year_id', type=int) if not brand_id: return jsonify({'error': 'brand_id required'}), 400 def _do(master): - data = catalog_service.get_models(master, brand_id) + data = catalog_service.get_models(master, brand_id, year_id=year_id) return jsonify({'data': data}) return _master_only(_do) diff --git a/pos/services/catalog_service.py b/pos/services/catalog_service.py index 529ab02..545a526 100644 --- a/pos/services/catalog_service.py +++ b/pos/services/catalog_service.py @@ -27,32 +27,53 @@ NORTH_AMERICA_BRANDS = ( ) -def get_brands(master_conn): - """Get vehicle brands available in Mexico/USA/Canada that have MYE entries.""" +def get_brands(master_conn, year_id=None): + """Get vehicle brands available in Mexico/USA/Canada that have MYE entries. + If year_id is provided, only brands that have models for that year.""" cur = master_conn.cursor() - cur.execute(""" - SELECT DISTINCT b.id_brand, b.name_brand - FROM brands b - JOIN models m ON m.brand_id = b.id_brand - JOIN model_year_engine mye ON mye.model_id = m.id_model - WHERE b.name_brand = ANY(%s) - ORDER BY b.name_brand - """, (list(NORTH_AMERICA_BRANDS),)) + if year_id: + cur.execute(""" + SELECT DISTINCT b.id_brand, b.name_brand + FROM brands b + JOIN models m ON m.brand_id = b.id_brand + JOIN model_year_engine mye ON mye.model_id = m.id_model + WHERE b.name_brand = ANY(%s) AND mye.year_id = %s + ORDER BY b.name_brand + """, (list(NORTH_AMERICA_BRANDS), year_id)) + else: + cur.execute(""" + SELECT DISTINCT b.id_brand, b.name_brand + FROM brands b + JOIN models m ON m.brand_id = b.id_brand + JOIN model_year_engine mye ON mye.model_id = m.id_model + WHERE b.name_brand = ANY(%s) + ORDER BY b.name_brand + """, (list(NORTH_AMERICA_BRANDS),)) rows = cur.fetchall() cur.close() return [{'id_brand': r[0], 'name_brand': r[1]} for r in rows] -def get_models(master_conn, brand_id): - """Get models for a brand that have MYE entries (fast, no vehicle_parts scan).""" +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.""" cur = master_conn.cursor() - cur.execute(""" - SELECT DISTINCT m.id_model, m.name_model - FROM models m - JOIN model_year_engine mye ON mye.model_id = m.id_model - WHERE m.brand_id = %s - ORDER BY m.name_model - """, (brand_id,)) + if year_id: + cur.execute(""" + SELECT DISTINCT m.id_model, m.name_model + FROM models m + JOIN model_year_engine mye ON mye.model_id = m.id_model + WHERE m.brand_id = %s AND mye.year_id = %s + ORDER BY m.name_model + """, (brand_id, year_id)) + else: + cur.execute(""" + SELECT DISTINCT m.id_model, m.name_model + FROM models m + JOIN model_year_engine mye ON mye.model_id = m.id_model + WHERE m.brand_id = %s + ORDER BY m.name_model + """, (brand_id,)) rows = cur.fetchall() cur.close() return [{'id_model': r[0], 'name_model': r[1]} for r in rows] diff --git a/pos/static/js/catalog.js b/pos/static/js/catalog.js index 4b6e54f..1a6c02b 100644 --- a/pos/static/js/catalog.js +++ b/pos/static/js/catalog.js @@ -858,9 +858,9 @@ if (!yearId) return; - // Load brands (reuse existing endpoint) + // Load brands filtered by year vsBrand.disabled = false; - apiFetch(API + '/brands').then(function (data) { + apiFetch(API + '/brands?year_id=' + yearId).then(function (data) { var brands = data.data || data; if (!brands) return; vsBrand.innerHTML = ''; @@ -872,6 +872,7 @@ function vsBrandChanged() { var brandId = vsBrand.value; + var yearId = vsYear.value; vsModel.innerHTML = ''; vsEngine.innerHTML = ''; vsModel.disabled = true; @@ -879,8 +880,9 @@ if (!brandId) return; + // Load models filtered by brand AND year vsModel.disabled = false; - apiFetch(API + '/models?brand_id=' + brandId).then(function (data) { + apiFetch(API + '/models?brand_id=' + brandId + (yearId ? '&year_id=' + yearId : '')).then(function (data) { var models = data.data || data; if (!models) return; vsModel.innerHTML = '';