Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos - F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado - F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment - F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings) - F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant - Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas - Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant) - Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant - Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
This commit is contained in:
@@ -378,17 +378,32 @@ def handle_quote_search(text, state_data, ctx):
|
||||
search_text = search_text.strip()
|
||||
state_data['search_description'] = search_text
|
||||
|
||||
# Memoria conversacional + historial de compras + persona por tenant
|
||||
conv_history = []
|
||||
try:
|
||||
from blueprints.whatsapp_bp import _get_conversation_history
|
||||
conv_history = _get_conversation_history(ctx.phone, ctx.tenant_conn, limit=4)
|
||||
except Exception:
|
||||
conv_history = []
|
||||
purchase_ctx = None
|
||||
try:
|
||||
from services.part_kits import get_purchase_history
|
||||
purchase_ctx = get_purchase_history(ctx.phone, ctx.tenant_conn) or None
|
||||
except Exception:
|
||||
purchase_ctx = None
|
||||
persona = (ctx.wa_config or {}).get('ai_persona') or None
|
||||
|
||||
try:
|
||||
from services.ai_chat import chat, chat_with_image
|
||||
if ctx.media_kind == 'image' and ctx.media_base64:
|
||||
ai_resp = chat_with_image(
|
||||
user_message=search_text if search_text != '(imagen)' else 'Identifica esta parte automotriz y sugiere terminos de busqueda.',
|
||||
image_base64=ctx.media_base64,
|
||||
conversation_history=[],
|
||||
inventory_context=None,
|
||||
conversation_history=conv_history,
|
||||
inventory_context=purchase_ctx,
|
||||
)
|
||||
else:
|
||||
ai_resp = chat(search_text, conversation_history=[], inventory_context=None)
|
||||
ai_resp = chat(search_text, conversation_history=conv_history, inventory_context=purchase_ctx, persona=persona)
|
||||
search_query = ai_resp.get('search_query')
|
||||
vehicle = ai_resp.get('vehicle')
|
||||
|
||||
@@ -669,7 +684,7 @@ def handle_quote_command_created(text, state_data, ctx):
|
||||
try:
|
||||
from services.wa_quotation import get_open_quotation, get_quotation_detail
|
||||
from services.quote_image import generate_quote_image
|
||||
from services.whatsapp_service import send_image
|
||||
from services.whatsapp_service import wa_send_image
|
||||
|
||||
qid = get_open_quotation(ctx.tenant_conn, ctx.phone)
|
||||
if qid:
|
||||
@@ -688,11 +703,11 @@ def handle_quote_command_created(text, state_data, ctx):
|
||||
}
|
||||
tenant_name = _get_tenant_business_name(ctx.tenant_conn)
|
||||
b64_img = generate_quote_image(quote_items, totals, tenant_name=tenant_name)
|
||||
img_result = send_image(
|
||||
img_result = wa_send_image(
|
||||
ctx.wa_config,
|
||||
ctx.phone,
|
||||
caption="Aquí está tu cotización 👇",
|
||||
base64_image=b64_img,
|
||||
bridge_url=ctx.wa_config.get('bridge_url')
|
||||
)
|
||||
if img_result.get('success'):
|
||||
return (
|
||||
@@ -715,19 +730,45 @@ def handle_quote_command_created(text, state_data, ctx):
|
||||
|
||||
if t in ('confirmar', 'confirmo', 'acepto', 'si', 'sí', 'va', 'ordenar', 'pedir'):
|
||||
from services.wa_quotation import confirm_quotation
|
||||
qid = confirm_quotation(ctx.tenant_conn, ctx.phone)
|
||||
if qid:
|
||||
reply = (
|
||||
"✅ *¡Pedido confirmado!*\n\n"
|
||||
"Tu cotización fue registrada.\n"
|
||||
"Nos pondremos en contacto contigo para coordinar la entrega.\n\n"
|
||||
"¡Gracias por tu compra! 🙏"
|
||||
)
|
||||
clean_data = {
|
||||
'customer_id': state_data.get('customer_id'),
|
||||
'branch_name': state_data.get('branch_name'),
|
||||
}
|
||||
return reply, 'support_done', clean_data
|
||||
result = confirm_quotation(ctx.tenant_conn, ctx.phone,
|
||||
customer_id=ctx.customer_id,
|
||||
tenant_id=ctx.tenant_id,
|
||||
branch_id=ctx.branch_id)
|
||||
clean_data = {
|
||||
'customer_id': state_data.get('customer_id'),
|
||||
'branch_name': state_data.get('branch_name'),
|
||||
}
|
||||
if result and result.get('sale_id'):
|
||||
lines = []
|
||||
lines.append("✅ *¡Pedido confirmado!*")
|
||||
lines.append("")
|
||||
lines.append(f"Tu pedido *#{result['sale_id']}* quedó registrado y separado.")
|
||||
lines.append("Pasa por tu refaccionaria oco coordina la entrega al confirmar el pago.\n")
|
||||
lines.append("*Detalle:*")
|
||||
for it in result.get('items', []):
|
||||
lines.append(f"• {it['quantity']} x {it['part_number']}")
|
||||
lines.append("")
|
||||
lines.append(f"*Total:* ${result['total']:,.2f}")
|
||||
lines.append("")
|
||||
lines.append("¡Gracias por tu compra! 🙏")
|
||||
return "\n".join(lines), 'support_done', clean_data
|
||||
if result and result.get('error') == 'insufficient_stock':
|
||||
return (
|
||||
f"⚠️ No hay stock suficiente de *{result['part_number']}*\n"
|
||||
f"Disponible: {result['available']} — Solicitado: {result['requested']}\n\n"
|
||||
"Tu cotización sigue activa. ¿Quieres *confirmar* el resto, "
|
||||
"*agregar* otra pieza o *limpiar* el pedido?",
|
||||
'quote_command_created', state_data)
|
||||
if result and result.get('error') == 'item_not_found':
|
||||
return (
|
||||
f"⚠️ La pieza *{result['part_number']}* ya no está disponible.\n\n"
|
||||
"¿Quieres *agregar* otra pieza o *limpiar* el pedido?",
|
||||
'quote_command_created', state_data)
|
||||
if result:
|
||||
return (
|
||||
"⚠️ No pude registrar tu pedido en este momento. "
|
||||
"Intenta de nuevo en unos minutos o contáctanos directamente.",
|
||||
'quote_command_created', state_data)
|
||||
|
||||
if t in ('limpiar', 'borrar', 'nueva', 'cancelar', 'reset'):
|
||||
from services.wa_quotation import clear_quotation
|
||||
|
||||
Reference in New Issue
Block a user