diff --git a/pos/services/marketplace_external_service.py b/pos/services/marketplace_external_service.py index e358230..52a92b6 100644 --- a/pos/services/marketplace_external_service.py +++ b/pos/services/marketplace_external_service.py @@ -120,6 +120,12 @@ def _extract_meli_error(err: MeliError) -> str: if cause_msgs: msg = (msg + " | " if msg else "") + "; ".join(cause_msgs) if msg: + # Translate common account-configuration errors to actionable messages + lowered = msg.lower() + if "me2 adoption is mandatory" in lowered: + return msg + " | Debes activar MercadoEnvíos (ME2) en tu cuenta de MercadoLibre. Ve a Configuración > Envíos en el panel de vendedor de ML." + if "user has not mode" in lowered: + return msg + " | Tu cuenta no tiene configurado este modo de envío. Configura tus métodos de envío en MercadoLibre." return msg except Exception: pass @@ -153,7 +159,7 @@ def build_item_payload( "listing_type_id": listing_type_id, "condition": "new", "pictures": [{"source": url} for url in images if url], - "shipping": {"mode": shipping_mode}, + "shipping": {"mode": shipping_mode, "local_pick_up": False, "free_shipping": False}, "attributes": [], } @@ -196,6 +202,34 @@ def build_item_payload( return payload +def check_meli_shipping_config(svc: MeliService, cfg: dict) -> dict: + """Check if the user's ML account has the required shipping modes configured. + + Returns {"ok": True} or {"ok": False, "error": "...", "available_modes": [...]}. + """ + user_id = cfg.get("meli_user_id") + if not user_id: + return {"ok": False, "error": "Usuario de ML no configurado"} + try: + prefs = svc.get_shipping_preferences(str(user_id)) + modes = prefs.get("modes", []) + mandatory = prefs.get("mandatory_mode_for_user", []) + if mandatory and not any(m in modes for m in mandatory): + return { + "ok": False, + "error": f"Tu cuenta requiere obligatoriamente los modos de envío: {', '.join(mandatory)}. Actualmente solo tienes: {', '.join(modes)}. Configúralos en el panel de vendedor de MercadoLibre.", + "available_modes": modes, + "mandatory_modes": mandatory, + } + return {"ok": True, "available_modes": modes, "mandatory_modes": mandatory} + except MeliError as e: + logger.warning("Failed to fetch shipping preferences: %s", e) + return {"ok": True} # Don't block on preference fetch failure + except Exception as e: + logger.warning("Failed to fetch shipping preferences: %s", e) + return {"ok": True} + + # ═══════════════════════════════════════════════════════════════════════════ # LISTINGS CRUD # ═══════════════════════════════════════════════════════════════════════════ @@ -279,6 +313,10 @@ def validate_items( if not svc: raise ValueError("MercadoLibre not configured") + shipping_check = check_meli_shipping_config(svc, cfg) + if not shipping_check["ok"]: + return {"valid": [], "invalid": [{"inventory_id": "config", "error": shipping_check["error"]}]} + cur = tenant_conn.cursor() cur.execute( """ @@ -373,6 +411,10 @@ def publish_items( if not svc: raise ValueError("MercadoLibre not configured") + shipping_check = check_meli_shipping_config(svc, cfg) + if not shipping_check["ok"]: + return {"success": [], "failed": [{"inventory_id": "config", "error": shipping_check["error"]}]} + cur = tenant_conn.cursor() # Batch fetch inventory rows diff --git a/pos/services/meli_service.py b/pos/services/meli_service.py index 3c88980..9773402 100644 --- a/pos/services/meli_service.py +++ b/pos/services/meli_service.py @@ -183,6 +183,9 @@ class MeliService: def get_category_attributes(self, category_id: str) -> list: return self._request("GET", f"/categories/{category_id}/attributes") + def get_shipping_preferences(self, user_id: str) -> dict: + return self._request("GET", f"/users/{user_id}/shipping_preferences") + # ─── Orders ────────────────────────────────────────────────────────── def get_orders(