diff --git a/pos/blueprints/chat_bp.py b/pos/blueprints/chat_bp.py index 654d558..393ad25 100644 --- a/pos/blueprints/chat_bp.py +++ b/pos/blueprints/chat_bp.py @@ -59,14 +59,30 @@ def chat(): if len(part_words) >= 3: effective_query = part_words - if effective_query and master and tenant: + if effective_query and tenant: + # First: search local inventory try: - results = catalog_service.smart_search( - master, effective_query, tenant, branch_id, limit=10 - ) - search_results = results if results else [] + local_results = _search_local_inventory(tenant, effective_query, search_query or '', branch_id) + if local_results: + search_results.extend(local_results) except Exception: - pass # search failure is non-fatal + pass + + # Then: search TecDoc catalog + if master: + try: + catalog_results = catalog_service.smart_search( + master, effective_query, tenant, branch_id, limit=10 + ) + if catalog_results: + # Mark as catalog results and avoid duplicates + local_parts = {r.get('part_number', '') for r in search_results} + for cr in catalog_results: + if cr.get('oem_part_number', '') not in local_parts: + cr['source'] = 'catalog' + search_results.append(cr) + except Exception: + pass # search failure is non-fatal except Exception: pass # DB failure is non-fatal for chat @@ -159,3 +175,61 @@ def _resolve_vehicle(master_conn, vehicle): cur.close() return result + + +def _search_local_inventory(tenant_conn, query_en, query_es, branch_id): + """Search tenant's local inventory by part name/number in both English and Spanish.""" + cur = tenant_conn.cursor() + results = [] + try: + # Search by part_number, name, or brand — try both English and Spanish terms + terms = set() + terms.add(query_en) + if query_es: + terms.add(query_es) + + where_parts = [] + params = [] + for term in terms: + if not term: + continue + where_parts.append("(i.part_number ILIKE %s OR i.name ILIKE %s OR i.brand ILIKE %s)") + params.extend([f'%{term}%', f'%{term}%', f'%{term}%']) + + if not where_parts: + return [] + + where = " OR ".join(where_parts) + if branch_id: + where = f"({where}) AND i.branch_id = %s" + params.append(branch_id) + + cur.execute(f""" + SELECT i.id, i.part_number, i.name, i.brand, i.price_1, i.price_2, i.price_3, i.cost, + COALESCE((SELECT SUM(quantity) FROM inventory_operations WHERE inventory_id = i.id), 0) as stock + FROM inventory i + WHERE i.is_active = true AND ({where}) + ORDER BY i.name + LIMIT 10 + """, params) + + for r in cur.fetchall(): + results.append({ + 'source': 'local', + 'inventory_id': r[0], + 'part_number': r[1], + 'oem_part_number': r[1], + 'name_part': r[2], + 'brand': r[3], + 'price_1': float(r[4]) if r[4] else 0, + 'price_2': float(r[5]) if r[5] else 0, + 'price_3': float(r[6]) if r[6] else 0, + 'cost': float(r[7]) if r[7] else 0, + 'local_stock': r[8], + }) + except Exception: + pass + finally: + cur.close() + + return results diff --git a/pos/static/js/chat.js b/pos/static/js/chat.js index 0a0eabd..875bc29 100644 --- a/pos/static/js/chat.js +++ b/pos/static/js/chat.js @@ -191,16 +191,21 @@ const card = document.createElement('div'); card.className = 'chat-part-card'; + const isLocal = p.source === 'local'; const stockQty = p.local_stock || 0; const stockClass = stockQty > 0 ? 'in-stock' : ''; const stockText = stockQty > 0 ? (stockQty + ' en stock') : 'Sin stock local'; const name = p.name_es || p.name_part || ''; - const partNum = p.oem_part_number || ''; + const partNum = p.oem_part_number || p.part_number || ''; + const brand = p.brand || ''; const priceText = p.price_1 ? ('$' + parseFloat(p.price_1).toFixed(2)) : ''; + const sourceTag = isLocal + ? 'MI INVENTARIO' + : 'CATÁLOGO'; card.innerHTML = - '