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 = '';