Files
Autoparts-DB/pos/blueprints/catalog_bp.py

172 lines
5.8 KiB
Python

# /home/Autopartes/pos/blueprints/catalog_bp.py
"""Catalog blueprint: TecDoc vehicle navigation with local stock enrichment.
Endpoints (all under /pos/api/catalog):
GET /brands — vehicle brands with parts
GET /models?brand_id= — models for a brand
GET /years?model_id= — years for a model
GET /engines?model_id=&year_id= — engines for model+year
GET /categories?mye_id= — part categories for vehicle
GET /groups?mye_id=&category_id= — part subcategories for vehicle+category
GET /parts?mye_id=&group_id= — parts with local stock enrichment
GET /part/<part_id> — full part detail (stock + bodegas + alternatives)
GET /search?q= — smart search (part number or text)
"""
from flask import Blueprint, request, jsonify, g
from middleware import require_auth
from tenant_db import get_master_conn, get_tenant_conn
from services import catalog_service
catalog_bp = Blueprint('catalog', __name__, url_prefix='/pos/api/catalog')
def _with_conns(fn):
"""Helper: open master + tenant connections, call fn, close both.
fn receives (master_conn, tenant_conn, branch_id).
"""
master = None
tenant = None
try:
master = get_master_conn()
tenant = get_tenant_conn(g.tenant_id)
branch_id = request.args.get('branch_id', g.branch_id)
return fn(master, tenant, branch_id)
except Exception as e:
return jsonify({'error': str(e)}), 500
finally:
if master:
try: master.close()
except: pass
if tenant:
try: tenant.close()
except: pass
def _master_only(fn):
"""Helper: open only master connection for hierarchy endpoints."""
master = None
try:
master = get_master_conn()
return fn(master)
except Exception as e:
return jsonify({'error': str(e)}), 500
finally:
if master:
try: master.close()
except: pass
# ─── Hierarchy navigation (master DB only) ───
@catalog_bp.route('/brands', methods=['GET'])
@require_auth('catalog.view')
def brands():
def _do(master):
data = catalog_service.get_brands(master)
return jsonify({'data': data})
return _master_only(_do)
@catalog_bp.route('/models', methods=['GET'])
@require_auth('catalog.view')
def models():
brand_id = request.args.get('brand_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)
return jsonify({'data': data})
return _master_only(_do)
@catalog_bp.route('/years', methods=['GET'])
@require_auth('catalog.view')
def years():
model_id = request.args.get('model_id', type=int)
if not model_id:
return jsonify({'error': 'model_id required'}), 400
def _do(master):
data = catalog_service.get_years(master, model_id)
return jsonify({'data': data})
return _master_only(_do)
@catalog_bp.route('/engines', methods=['GET'])
@require_auth('catalog.view')
def engines():
model_id = request.args.get('model_id', type=int)
year_id = request.args.get('year_id', type=int)
if not model_id or not year_id:
return jsonify({'error': 'model_id and year_id required'}), 400
def _do(master):
data = catalog_service.get_engines(master, model_id, year_id)
return jsonify({'data': data})
return _master_only(_do)
@catalog_bp.route('/categories', methods=['GET'])
@require_auth('catalog.view')
def categories():
mye_id = request.args.get('mye_id', type=int)
if not mye_id:
return jsonify({'error': 'mye_id required'}), 400
def _do(master):
data = catalog_service.get_categories(master, mye_id)
return jsonify({'data': data})
return _master_only(_do)
@catalog_bp.route('/groups', methods=['GET'])
@require_auth('catalog.view')
def groups():
mye_id = request.args.get('mye_id', type=int)
category_id = request.args.get('category_id', type=int)
if not mye_id or not category_id:
return jsonify({'error': 'mye_id and category_id required'}), 400
def _do(master):
data = catalog_service.get_groups(master, mye_id, category_id)
return jsonify({'data': data})
return _master_only(_do)
# ─── Parts with stock enrichment (master + tenant) ───
@catalog_bp.route('/parts', methods=['GET'])
@require_auth('catalog.view')
def parts():
mye_id = request.args.get('mye_id', type=int)
group_id = request.args.get('group_id', type=int)
page = request.args.get('page', 1, type=int)
per_page = request.args.get('per_page', 30, type=int)
if not mye_id or not group_id:
return jsonify({'error': 'mye_id and group_id required'}), 400
def _do(master, tenant, branch_id):
result = catalog_service.get_parts(master, mye_id, group_id, tenant, branch_id, page, per_page)
return jsonify(result)
return _with_conns(_do)
@catalog_bp.route('/part/<int:part_id>', methods=['GET'])
@require_auth('catalog.view')
def part_detail(part_id):
def _do(master, tenant, branch_id):
result = catalog_service.get_part_detail(master, part_id, tenant, branch_id)
if not result:
return jsonify({'error': 'Part not found'}), 404
return jsonify(result)
return _with_conns(_do)
@catalog_bp.route('/search', methods=['GET'])
@require_auth('catalog.view')
def search():
q = request.args.get('q', '').strip()
if not q or len(q) < 2:
return jsonify({'data': []})
limit = request.args.get('limit', 50, type=int)
def _do(master, tenant, branch_id):
data = catalog_service.smart_search(master, q, tenant, branch_id, limit)
return jsonify({'data': data})
return _with_conns(_do)