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
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.
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.
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.
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 confundiropportunity.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 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.