feat(catalog): full vehicle selector flow in brand catalog
Brand catalog now follows the same navigation as the regular catalog: 1. Brands -> 2. Models -> 3. Years -> 4. Engines -> 5. Categories -> 6. Parts Backend: - Add /mye-parts endpoint for MYE-specific parts with category filter - Uses existing /models, /years, /engines, /categories endpoints Frontend: - Complete rewrite of brand-catalog.js with breadcrumb navigation - State machine: brands -> models -> years -> engines -> categories -> parts - Search and pagination preserved at parts level - Breadcrumb allows jumping back to any previous step
This commit is contained in:
@@ -763,3 +763,107 @@ def brand_parts():
|
||||
finally:
|
||||
cur.close()
|
||||
return _with_conns(_query)
|
||||
|
||||
|
||||
@catalog_bp.route('/mye-parts', methods=['GET'])
|
||||
@require_auth('catalog.view')
|
||||
def mye_parts():
|
||||
"""Return parts for a specific MYE + category (brand-catalog flow).
|
||||
|
||||
Skips the group/subgroup level and goes directly from category to parts.
|
||||
"""
|
||||
mye_id = request.args.get('mye_id', type=int)
|
||||
category_id = request.args.get('category_id', type=int)
|
||||
search = request.args.get('search', '').strip()
|
||||
limit = request.args.get('limit', 50, type=int)
|
||||
offset = request.args.get('offset', 0, type=int)
|
||||
|
||||
if not mye_id:
|
||||
return jsonify({'error': 'mye_id required'}), 400
|
||||
|
||||
def _query(master, tenant, branch_id):
|
||||
cur = master.cursor()
|
||||
try:
|
||||
# Build dynamic filters
|
||||
cat_filter = ""
|
||||
search_filter = ""
|
||||
params = [mye_id]
|
||||
|
||||
if category_id:
|
||||
cat_filter = "AND pc.id_part_category = %s"
|
||||
params.append(category_id)
|
||||
|
||||
if search:
|
||||
search_filter = "AND (p.oem_part_number ILIKE %s OR COALESCE(NULLIF(p.name_es, ''), p.name_part) ILIKE %s)"
|
||||
like_term = f"%{search}%"
|
||||
params.extend([like_term, like_term])
|
||||
|
||||
# Get parts
|
||||
query_params = list(params)
|
||||
cur.execute(f"""
|
||||
SELECT DISTINCT p.id_part, p.oem_part_number,
|
||||
COALESCE(NULLIF(p.name_es, ''), p.name_part) as name,
|
||||
pg.id_part_group, pg.name_part_group,
|
||||
pc.id_part_category, pc.name_part_category
|
||||
FROM vehicle_parts vp
|
||||
JOIN parts p ON p.id_part = vp.part_id
|
||||
JOIN part_groups pg ON pg.id_part_group = p.group_id
|
||||
JOIN part_categories pc ON pc.id_part_category = pg.category_id
|
||||
WHERE vp.model_year_engine_id = %s
|
||||
{cat_filter}
|
||||
{search_filter}
|
||||
ORDER BY p.id_part
|
||||
LIMIT %s OFFSET %s
|
||||
""", query_params + [limit, offset])
|
||||
|
||||
part_rows = cur.fetchall()
|
||||
part_ids = [r[0] for r in part_rows]
|
||||
|
||||
# Count total
|
||||
cur.execute(f"""
|
||||
SELECT COUNT(DISTINCT p.id_part)
|
||||
FROM vehicle_parts vp
|
||||
JOIN parts p ON p.id_part = vp.part_id
|
||||
JOIN part_groups pg ON pg.id_part_group = p.group_id
|
||||
JOIN part_categories pc ON pc.id_part_category = pg.category_id
|
||||
WHERE vp.model_year_engine_id = %s
|
||||
{cat_filter}
|
||||
{search_filter}
|
||||
""", params)
|
||||
total = cur.fetchone()[0]
|
||||
|
||||
# Enrich with local stock if available
|
||||
local_stock = {}
|
||||
if tenant and part_ids:
|
||||
try:
|
||||
from services.catalog_service import _get_local_stock_bulk
|
||||
local_stock = _get_local_stock_bulk(tenant, part_ids)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
items = []
|
||||
for r in part_rows:
|
||||
part_id = r[0]
|
||||
stock_info = local_stock.get(part_id, {})
|
||||
items.append({
|
||||
'id': part_id,
|
||||
'oem_part_number': r[1],
|
||||
'name': r[2],
|
||||
'group': {'id': r[3], 'name': r[4]},
|
||||
'category': {'id': r[5], 'name': r[6]},
|
||||
'local_stock': stock_info.get('stock', 0),
|
||||
'local_price': stock_info.get('price', None),
|
||||
})
|
||||
|
||||
return jsonify({
|
||||
'mye_id': mye_id,
|
||||
'category_id': category_id,
|
||||
'search': search,
|
||||
'items': items,
|
||||
'total': total,
|
||||
'limit': limit,
|
||||
'offset': offset,
|
||||
})
|
||||
finally:
|
||||
cur.close()
|
||||
return _with_conns(_query)
|
||||
|
||||
Reference in New Issue
Block a user