Files
MP-Manager/docs/casos/2026-05-30-opp-marca-cf-vacios-mapeo-contacto.md
2026-05-30 20:16:12 -06:00

6.0 KiB

id, fecha, categoria, location_ids, run_ids, snapshots, status, memorias, playbooks
id fecha categoria location_ids run_ids snapshots status memorias playbooks
CASE-2026-05-30-opp-marca-cf-vacios-mapeo-contacto 2026-05-30 cascada_n8n
GbKkBpCmKu2QmloKFHy3 (Monte Providencia / Marca)
yjqKxoO02rsdwdJZSPmD (85950 - MP - Temixco)
backfill-opp-cf-20260530-164821
generated/migrations/backfill_opp_cf_8HITkGkOn3gN23Tl8LBr_backfill-opp-cf-20260530-164821.json
resuelto
n8n_opp_sync_match
opp_multiplicity_replication_gap
sucursal_to_marca_cf_drop_on_create
sucursal_datatype_divergence_intentional
docs/PLAYBOOK_ENLACE_OPORTUNIDADES.md

TRIGGERS

  • opp de Marca con Sucursal / TIENDA / Canal de Origen (CANAL DE ORIGEN) vacíos
  • "desde sucursal no se están trayendo los datos de las oportunidades"
  • opp de Marca con solo opportunity.id_oportunidad_sucursal y ningún otro CF (opp "solo-link")
  • workflow Cfgwp0bOtDW8zuKW "Sincronizar Oportunidad - Nodos Nuevos (Create/Update)"
  • 8HITkGkOn3gN23Tl8LBr (opp Marca), OQBrOQN9mNlybjlzB8Jk (opp Temixco), Miguel Angel mahernandez2282@gmail.com
  • opportunity.fuente_de_posible_cliente == CANAL DE ORIGEN (opp y contacto comparten el sufijo de fieldKey)

SÍNTOMA

La opp de Marca 8HITkGkOn3gN23Tl8LBr (Miguel Angel, viene de Temixco) tenía vacíos Sucursal, TIENDA y Canal de Origen. Hipótesis del owner: la réplica n8n no mapea esos campos.

DIAGNÓSTICO (read-only)

  • get_opportunities(GbKkBpCmKu2QmloKFHy3) agregado por CF: de 1342 opps de Marca, opportunity.sucursal poblado en 1341, tienda 1295, fuente_de_posible_cliente 711. → el mapeo genérico SÍ funciona; 8HIT era la única opp "solo-link".
  • Los nodos Armar Body - CREATE y Armar Body - UPDATE (v2) ya copian todos los CF de la opp de sucursal → Marca por fieldKey (fallback name), enriquecidos por Mapeo completo oportunidad origen - SUCURSAL.
  • La opp de sucursal OQBrOQN9… SÍ tiene opportunity.sucursal="Temixco, Morelos", tienda="TEMIXCO", pero NO tiene fuente_de_posible_cliente (CANAL DE ORIGEN). El contacto de Temixco (eE6374FcwI7zlmQmTgGO) sí: contact.fuente_de_posible_cliente="SUCURSAL".
  • Schema en vivo: el fieldKey canónico (sufijo) es idéntico contact↔opportunity: *.sucursal, *.tienda, *.fuente_de_posible_cliente. contact.sucursal es SINGLE_OPTIONS pero su label = el texto-ciudad que opportunity.sucursal (TEXT) espera (sucursal_datatype_divergence_intentional).

CAUSA RAÍZ

  1. Caso puntual 8HIT: opp creada fuera del flujo normal (creación manual + backfill_opp_sucursal_link.py, que solo escribe el link) — por eso quedó solo-link. No es bug del mapeo genérico.
  2. Gap general: cuando la opp de sucursal NO trae un CF (p.ej. CANAL DE ORIGEN, que vive solo en el contacto, o por carrera de tiempo intermitente sucursal_to_marca_cf_drop_on_create), Marca queda sin ese campo. El contacto es la fuente estable que faltaba consultar.

ACCIÓN

  1. Workflow Cfgwp0bOtDW8zuKW (script n8n/_add_contact_to_opp_mapping.py, dry-run → confirmación → --apply):
    • Nodo nuevo Obtener Contacto - SUCURSAL (GET /contacts/{{ Datos de Lead.Cliente['Contact ID'] }}, token sucursal, onError=continue) insertado entre Obtener info de Oportunidad - SUCURSAL y Obtener Pipelines - SUCURSAL.
    • Code Mapeo completo oportunidad origen - SUCURSAL extendido: upsert de opportunity.sucursal / tienda / fuente_de_posible_cliente con prioridad (a) opp sucursal → (b) contacto → (c) webhook. El loop genérico de Armar Body lo propaga a Marca por fieldKey (sin race lectura/escritura).
    • Aplicado y verificado (verify_post), workflow reactivado. Backups en n8n/backup_contact_to_opp_mapping_Cfgwp0bOtDW8zuKW_*.json.
  2. Backfill 8HIT (scripts/backfill_brand_opp_cf_from_source.py --brand-opp-id 8HITkGkOn3gN23Tl8LBr --branch-location-id yjqKxoO02rsdwdJZSPmD --apply): rellenó 5 CF vacíos desde la opp de sucursal + contacto. run_id=backfill-opp-cf-20260530-164821 (reversible desde dashboard).

VERIFICACIÓN

GET /opportunities/8HITkGkOn3gN23Tl8LBr (Marca) — después:

  • Sucursal = Temixco, Morelos, TIENDA = TEMIXCO, CANAL DE ORIGEN = SUCURSAL (del contacto), Fuente de Prospecto = LEAD DIGITAL, Vehículo = March 2014, link intacto.

EDGE-CASES / TRAMPAS

  • No confundir opportunity.fuente_de_prospecto (LEAD DIGITAL / REFERIDO / REDES SOCIALES) con opportunity.fuente_de_posible_cliente (= CANAL DE ORIGEN: SUCURSAL / FACEBOOK / FORMULARIO / WHATSAPP). Mismo nombre "Fuente…" en GHL, fieldKeys distintos.
  • No sourcear 8HIT del contacto de Marca (RwxMQr0): por el lío de homónimos su contact.sucursal="Narvarte Oriente…" (incorrecto para Temixco). La opp de sucursal es la fuente limpia.
  • El valor de CANAL DE ORIGEN debe ser opción válida del picklist de Marca (SUCURSAL lo es). custom_fields_picklist_alignment
  • El backfill solo rellena campos vacíos (no sobreescribe).

REUTILIZABLE

  • Agregado por CF para detectar opps "solo-link" en Marca: cargar el dump de get_opportunities y contar ids de CF (1342 vs 1341 reveló el outlier).
  • scripts/backfill_brand_opp_cf_from_source.py: backfill genérico de CF descriptivos de una opp de Marca desde su opp de sucursal enlazada (+ contacto como respaldo), dry-run/snapshot/audit.

PENDIENTES

  • Validar E2E el cambio del workflow en la próxima sincronización real de una opp de Temixco (o construir un E2E para Cfgwp0bOtDW8zuKW, hoy el harness scripts/n8n_e2e_test.py solo cubre los workflows de contactos).
  • (Opcional 2ª iteración) Persistir también los 3 campos en la opp de sucursal vía el PUT de Mapear ID Oportunidad Sucursal - SUCURSAL.
  • Re-sync de Marca para refrescar el cache SQLite del dashboard.

ENLACES