feat: MercadoLibre integration + inventory bulk publish + WhatsApp bridge fixes

- Add MercadoLibre OAuth, listings, orders, webhooks and category search
- New marketplace_external_bp.py, meli_service.py, marketplace_external_service.py
- New marketplace_external.html/js with ML management UI
- Inventory: bulk publish to ML with category autocomplete, listing type and shipping selectors
- Inventory: new .btn--meli styles, select/label CSS fixes
- WhatsApp bridge: rate limiting, 440/515/408 error handling, stale watchdog
- DB migration v3.4_meli_integration.sql for marketplace_listings, orders, sync_queue
- Add Celery tasks for ML sync and webhook processing
- Sidebar: MercadoLibre navigation link
This commit is contained in:
2026-05-26 04:24:07 +00:00
parent 50c0dbe7d4
commit a236187f3a
66 changed files with 7335 additions and 498 deletions

View File

@@ -135,41 +135,34 @@ def _ensure_sessions_table(tenant_conn):
cur.close()
def set_last_shown_part(phone, part_info):
def set_last_shown_part(tenant_conn, phone, part_info):
"""Store the last part shown to this phone number.
part_info: dict with keys inventory_id, part_number, name, brand,
price, stock, unit
"""
# In-memory fallback for when tenant_conn is not available
from tenant_db import get_tenant_conn
try:
conn = get_tenant_conn(11)
_ensure_sessions_table(conn)
cur = conn.cursor()
_ensure_sessions_table(tenant_conn)
cur = tenant_conn.cursor()
import json
cur.execute("""
INSERT INTO whatsapp_sessions (phone, last_shown, updated_at)
VALUES (%s, %s, NOW())
ON CONFLICT (phone) DO UPDATE SET last_shown = EXCLUDED.last_shown, updated_at = NOW()
""", (phone, json.dumps(part_info)))
conn.commit()
tenant_conn.commit()
cur.close()
conn.close()
except Exception as e:
print(f"[WA-SESSION] Failed to persist last_shown for {phone}: {e}")
def get_last_shown_part(phone):
from tenant_db import get_tenant_conn
def get_last_shown_part(tenant_conn, phone):
try:
conn = get_tenant_conn(11)
_ensure_sessions_table(conn)
cur = conn.cursor()
_ensure_sessions_table(tenant_conn)
cur = tenant_conn.cursor()
cur.execute("SELECT last_shown FROM whatsapp_sessions WHERE phone = %s", (phone,))
row = cur.fetchone()
cur.close()
conn.close()
if row and row[0]:
return row[0]
except Exception as e:
@@ -177,54 +170,45 @@ def get_last_shown_part(phone):
return None
def clear_last_shown(phone):
from tenant_db import get_tenant_conn
def clear_last_shown(tenant_conn, phone):
try:
conn = get_tenant_conn(11)
_ensure_sessions_table(conn)
cur = conn.cursor()
_ensure_sessions_table(tenant_conn)
cur = tenant_conn.cursor()
cur.execute("DELETE FROM whatsapp_sessions WHERE phone = %s", (phone,))
conn.commit()
tenant_conn.commit()
cur.close()
conn.close()
except Exception as e:
print(f"[WA-SESSION] Failed to clear last_shown for {phone}: {e}")
def set_vehicle(phone, vehicle):
def set_vehicle(tenant_conn, phone, vehicle):
"""Store the detected vehicle for this phone number.
vehicle: dict with keys brand, model, year
"""
from tenant_db import get_tenant_conn
try:
conn = get_tenant_conn(11)
_ensure_sessions_table(conn)
cur = conn.cursor()
_ensure_sessions_table(tenant_conn)
cur = tenant_conn.cursor()
import json
cur.execute("""
INSERT INTO whatsapp_sessions (phone, vehicle, updated_at)
VALUES (%s, %s, NOW())
ON CONFLICT (phone) DO UPDATE SET vehicle = EXCLUDED.vehicle, updated_at = NOW()
""", (phone, json.dumps(vehicle)))
conn.commit()
tenant_conn.commit()
cur.close()
conn.close()
except Exception as e:
print(f"[WA-SESSION] Failed to persist vehicle for {phone}: {e}")
def get_vehicle(phone):
def get_vehicle(tenant_conn, phone):
"""Retrieve the stored vehicle for this phone number."""
from tenant_db import get_tenant_conn
try:
conn = get_tenant_conn(11)
_ensure_sessions_table(conn)
cur = conn.cursor()
_ensure_sessions_table(tenant_conn)
cur = tenant_conn.cursor()
cur.execute("SELECT vehicle FROM whatsapp_sessions WHERE phone = %s", (phone,))
row = cur.fetchone()
cur.close()
conn.close()
if row and row[0]:
return row[0]
except Exception as e:
@@ -232,17 +216,14 @@ def get_vehicle(phone):
return None
def clear_session(phone):
def clear_session(tenant_conn, phone):
"""Clear all session data (last_shown + vehicle) for this phone."""
from tenant_db import get_tenant_conn
try:
conn = get_tenant_conn(11)
_ensure_sessions_table(conn)
cur = conn.cursor()
_ensure_sessions_table(tenant_conn)
cur = tenant_conn.cursor()
cur.execute("DELETE FROM whatsapp_sessions WHERE phone = %s", (phone,))
conn.commit()
tenant_conn.commit()
cur.close()
conn.close()
except Exception as e:
print(f"[WA-SESSION] Failed to clear session for {phone}: {e}")