feat: configurable vehicle compatibility source (TecDoc / QWEN / Both)
Backend: - Added GET/PUT /pos/api/config/vehicle-compat-source endpoints - Added get_compat_source() helper reading from tenant_config - create_item() now respects config: runs TecDoc and/or QWEN accordingly - auto_match_item_vehicles() respects config: runs only configured source Frontend: - Added 'Compatibilidad de Vehiculos' section in config.html - Added loadVehicleCompatSource() / saveVehicleCompatSource() in config.js - Regenerated config.min.js
This commit is contained in:
@@ -409,3 +409,45 @@ def upgrade_billing():
|
||||
if 'error' in result:
|
||||
return jsonify(result), 400
|
||||
return jsonify(result)
|
||||
|
||||
|
||||
# ─── Vehicle Compatibility Source ────────────────────
|
||||
|
||||
@config_bp.route('/vehicle-compat-source', methods=['GET'])
|
||||
@require_auth()
|
||||
def get_vehicle_compat_source():
|
||||
"""Get the configured vehicle compatibility source.
|
||||
|
||||
Returns: {'source': 'tecdoc' | 'qwen' | 'both'}
|
||||
"""
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT value FROM tenant_config WHERE key = 'vehicle_compat_source'")
|
||||
row = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
source = row[0] if row else 'both'
|
||||
if source not in ('tecdoc', 'qwen', 'both'):
|
||||
source = 'both'
|
||||
return jsonify({'source': source})
|
||||
|
||||
|
||||
@config_bp.route('/vehicle-compat-source', methods=['PUT'])
|
||||
@require_auth('config.edit')
|
||||
def update_vehicle_compat_source():
|
||||
"""Set the vehicle compatibility source."""
|
||||
data = request.get_json() or {}
|
||||
source = data.get('source', 'both')
|
||||
if source not in ('tecdoc', 'qwen', 'both'):
|
||||
return jsonify({'error': 'source must be tecdoc, qwen, or both'}), 400
|
||||
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
INSERT INTO tenant_config (key, value) VALUES ('vehicle_compat_source', %s)
|
||||
ON CONFLICT (key) DO UPDATE SET value = EXCLUDED.value
|
||||
""", (source,))
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
return jsonify({'message': 'Vehicle compatibility source updated', 'source': source})
|
||||
|
||||
@@ -18,7 +18,7 @@ from services.audit import log_action
|
||||
from tenant_db import get_master_conn
|
||||
from services.inventory_vehicle_compat import (
|
||||
auto_match_vehicle_compatibility, add_compatibility, remove_compatibility,
|
||||
remove_all_compatibility, get_compatibility, search_mye,
|
||||
remove_all_compatibility, get_compatibility, search_mye, get_compat_source,
|
||||
)
|
||||
|
||||
inventory_bp = Blueprint('inventory', __name__, url_prefix='/pos/api/inventory')
|
||||
@@ -283,38 +283,43 @@ def create_item():
|
||||
conn.commit()
|
||||
cur.close()
|
||||
|
||||
# Auto-match vehicle compatibility via TecDoc
|
||||
try:
|
||||
master = get_master_conn()
|
||||
auto_match_vehicle_compatibility(master, conn, item_id, data['part_number'],
|
||||
brand=data.get('brand'), name=data.get('name'))
|
||||
master.close()
|
||||
except Exception as am_err:
|
||||
print(f"[auto_match] Error for item {item_id}: {am_err}")
|
||||
|
||||
# QWEN AI fitment (complementa TecDoc mientras se termina)
|
||||
# ── Vehicle compatibility (respects tenant config) ────────────────
|
||||
compat_source = get_compat_source(g.tenant_id)
|
||||
qwen_added = 0
|
||||
try:
|
||||
from services.qwen_fitment import get_vehicle_fitment
|
||||
fitment = get_vehicle_fitment(
|
||||
data['part_number'],
|
||||
data['name'],
|
||||
data.get('brand', '')
|
||||
)
|
||||
for v in fitment.get('vehicles', []):
|
||||
if v.get('mye_id'):
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
INSERT INTO inventory_vehicle_compat
|
||||
(inventory_id, model_year_engine_id, source, confidence, created_at)
|
||||
VALUES (%s, %s, %s, %s, NOW())
|
||||
ON CONFLICT (inventory_id, model_year_engine_id) DO NOTHING
|
||||
""", (item_id, v['mye_id'], 'qwen_ai', fitment.get('confidence', 0)))
|
||||
cur.close()
|
||||
qwen_added += 1
|
||||
conn.commit()
|
||||
except Exception as qwen_err:
|
||||
print(f"[qwen_fitment] Error for item {item_id}: {qwen_err}")
|
||||
|
||||
# TecDoc auto-match
|
||||
if compat_source in ('tecdoc', 'both'):
|
||||
try:
|
||||
master = get_master_conn()
|
||||
auto_match_vehicle_compatibility(master, conn, item_id, data['part_number'],
|
||||
brand=data.get('brand'), name=data.get('name'))
|
||||
master.close()
|
||||
except Exception as am_err:
|
||||
print(f"[auto_match] Error for item {item_id}: {am_err}")
|
||||
|
||||
# QWEN AI fitment
|
||||
if compat_source in ('qwen', 'both'):
|
||||
try:
|
||||
from services.qwen_fitment import get_vehicle_fitment
|
||||
fitment = get_vehicle_fitment(
|
||||
data['part_number'],
|
||||
data['name'],
|
||||
data.get('brand', '')
|
||||
)
|
||||
for v in fitment.get('vehicles', []):
|
||||
if v.get('mye_id'):
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
INSERT INTO inventory_vehicle_compat
|
||||
(inventory_id, model_year_engine_id, source, confidence, created_at)
|
||||
VALUES (%s, %s, %s, %s, NOW())
|
||||
ON CONFLICT (inventory_id, model_year_engine_id) DO NOTHING
|
||||
""", (item_id, v['mye_id'], 'qwen_ai', fitment.get('confidence', 0)))
|
||||
cur.close()
|
||||
qwen_added += 1
|
||||
conn.commit()
|
||||
except Exception as qwen_err:
|
||||
print(f"[qwen_fitment] Error for item {item_id}: {qwen_err}")
|
||||
|
||||
conn.close()
|
||||
return jsonify({
|
||||
@@ -1363,14 +1368,51 @@ def auto_match_item_vehicles(item_id):
|
||||
return jsonify({'error': 'Item not found'}), 404
|
||||
|
||||
part_number, brand, name = row
|
||||
master = get_master_conn()
|
||||
try:
|
||||
result = auto_match_vehicle_compatibility(master, conn, item_id, part_number,
|
||||
brand=brand, name=name)
|
||||
return jsonify(result)
|
||||
finally:
|
||||
master.close()
|
||||
conn.close()
|
||||
compat_source = get_compat_source(g.tenant_id)
|
||||
|
||||
# TecDoc auto-match
|
||||
if compat_source in ('tecdoc', 'both'):
|
||||
master = get_master_conn()
|
||||
try:
|
||||
result = auto_match_vehicle_compatibility(master, conn, item_id, part_number,
|
||||
brand=brand, name=name)
|
||||
return jsonify(result)
|
||||
finally:
|
||||
master.close()
|
||||
conn.close()
|
||||
|
||||
# QWEN AI auto-match
|
||||
if compat_source == 'qwen':
|
||||
try:
|
||||
from services.qwen_fitment import get_vehicle_fitment
|
||||
fitment = get_vehicle_fitment(part_number, name, brand)
|
||||
# Insert results
|
||||
inserted = 0
|
||||
cur2 = conn.cursor()
|
||||
for v in fitment.get('vehicles', []):
|
||||
if v.get('mye_id'):
|
||||
cur2.execute("""
|
||||
INSERT INTO inventory_vehicle_compat
|
||||
(inventory_id, model_year_engine_id, source, confidence, created_at)
|
||||
VALUES (%s, %s, %s, %s, NOW())
|
||||
ON CONFLICT (inventory_id, model_year_engine_id) DO NOTHING
|
||||
""", (item_id, v['mye_id'], 'qwen_ai', fitment.get('confidence', 0)))
|
||||
if cur2.rowcount > 0:
|
||||
inserted += 1
|
||||
conn.commit()
|
||||
cur2.close()
|
||||
return jsonify({
|
||||
'matched': inserted > 0,
|
||||
'matches': [],
|
||||
'myes': [v['mye_id'] for v in fitment.get('vehicles', []) if v.get('mye_id')],
|
||||
'inserted': inserted,
|
||||
})
|
||||
except Exception as e:
|
||||
conn.close()
|
||||
return jsonify({'error': str(e)}), 500
|
||||
|
||||
conn.close()
|
||||
return jsonify({'error': 'No compatibility source configured'}), 400
|
||||
|
||||
|
||||
@inventory_bp.route('/mye/search', methods=['GET'])
|
||||
|
||||
Reference in New Issue
Block a user