9.7 KiB
id: CASE-2026-05-30-incoherencia-canal-fuente-lead-digital fecha: 2026-05-30 categoria: custom_field, cascada_n8n, descuadre location_ids: [GbKkBpCmKu2QmloKFHy3 (Monte Providencia/Marca), KEZ7dAhgwzK4uZfMvZuj (Puebla), uJEn2iuUficuml9zxAnt (Cancún), uZnMH5bO6MXTHcgHeyq9 (Pilares), nF1uEaYB3mCK5em9bPn2 (Eugenia), jE41bVhhnb5T505BFm4F (Morelia 1), WCHyow6KpjLFYriQWdbJ (Tampico)] run_ids: [origen-fuente-3c50e43ec6a9, "n8n ddUEORBEtZLzsQF2 versionId 11c7184b→e8f6f33f (Patrón A)→37f780e1 (Patrón B)"] snapshots: [n8n/backup_fuente_reconcile_ddUEORBEtZLzsQF2_20260530_190108.json, n8n/backup_redes_sociales_ddUEORBEtZLzsQF2_20260530_192343.json] status: resuelto memorias: incoherencia_canal_fuente_lead_digital, super_script_fix_branch_user_origin, n8n_2004_canal_origen_complemento, createdby_only_in_individual_get playbooks: []
TRIGGERS
LEAD DIGITAL no cuadra con FORMULARIO + FACEBOOK,Recuento de Contactos por CANAL DE ORIGEN vs Fuente de prospecto929 vs 901,descuadre 28 LEAD DIGITAL,Canal=SUCURSAL pero Fuente=LEAD DIGITAL, viola AGENTS Cap.3REDES SOCIALESfuente de prospecto (valor no canónico), Tampico 7 contactosfix_branch_user_origin.py NO toca Fuente de Prospecto, n8n[2004]pone Canal=SUCURSAL a WEB_USER sin reconciliar FuentecreatedBy.source WEB_USER MOBILE_USER= captura manual sucursal;sourcede Marca (Formulario/Sucursal) lo estampa la replicación n8n y NO es fiable- scripts:
audit_origen_fuente_incoherencia.py,fix_origen_fuente_incoherencia.py
SÍNTOMA
Panel de Marca (GbKkBpCmKu2QmloKFHy3, 1328 contactos). Dos widgets descuadran:
Recuento por CANAL DE ORIGEN: FORMULARIO 612, SUCURSAL 426, FACEBOOK 289 (digital = 901).Fuente de prospecto: LEAD DIGITAL 929, SUCURSAL 374, PROSPECCIÓN 11, REDES SOCIALES 7, CLIENTE CONOCIDO 3, ALIANZA 2, GALLARDETES 1. Regla: LEAD DIGITAL debería = FORMULARIO + FACEBOOK. 929 ≠ 901 → descuadre +28. Además REDES SOCIALES sospechoso (canal digital mal clasificado).
DIAGNÓSTICO
Cross-tab Canal × Fuente desde la cache (generated/data/mp_manager.sqlite, contacts.custom_fields_json + object_schemas):
- FUENTE=LEAD DIGITAL (929) → 612 FORMULARIO + 289 FACEBOOK + 28 SUCURSAL ← el descuadre.
- FUENTE=REDES SOCIALES (7) → 7 canal=SUCURSAL.
- PROSPECCIÓN/CLIENTE CONOCIDO/ALIANZA/GALLARDETES → todos canal=SUCURSAL (OK, fuentes de prospección manual válidas).
Global (todas las cuentas): los 28 incoherentes de Marca = 16 Puebla + 4 Cancún + 3 Pilares + 3 Eugenia + 2 Morelia (suma 28 → replica 1:1 a Marca). Los 7 REDES SOCIALES = todos Tampico. La incoherencia nace en sucursal, no es artefacto de replicación.
Callejón descartado: el source de los contactos en Marca decía 19 "Sucursal" / 9 "Formulario", sugiriendo partir los 28 en dos. PERO el source del lado sucursal era 26 null + 2 Formulario (no coincide). Verificación en vivo con createdBy.source (GET individual, audit_origen_fuente_incoherencia.py --all): los 28 son WEB_USER/MOBILE_USER (captura manual sucursal), incluidos los 9 que en Marca figuraban "Formulario". El source de Marca lo estampa la replicación n8n y NO es fiable; createdBy.source es el gold standard.
CAUSA RAÍZ
- Workflow n8n
[2004](ddUEORBEtZLzsQF2) y su gemelo Pythonfix_branch_user_origin.pyponenCanal de Origen=SUCURSALa los contactos creados por empleado (WEB_USER/MOBILE_USER) pero deliberadamente NO tocan "Fuente de Prospecto" (fix_branch_user_origin.py:27-29,234) para no pisar ALIANZA/PROSPECCIÓN. Efecto colateral: cuando voltean canal a SUCURSAL dejan elLEAD DIGITALviejo huérfano → los 28 (Patrón A). - En Tampico se usó el valor no canónico
REDES SOCIALEScomo Fuente de Prospecto en 7 contactos digitales (social), que quedaron con canal=SUCURSAL (Patrón B).
ACCIÓN
Decisión del usuario (2 rondas AskUserQuestion): Patrón A → todos a SUCURSAL (createdBy manda sobre source de Marca); Patrón B → FACEBOOK + LEAD DIGITAL.
Script nuevo scripts/fix_origen_fuente_incoherencia.py (idempotente, detecta por estado actual, dry-run default, script_audit reversible, resuelve field ids por FIELD_ALIASES). Plan por patrón:
- A: contacto Fuente→SUCURSAL; opps: Canal de Origen de la Oportunidad→Sucursal, Fuente→SUCURSAL.
- B: contacto Canal→FACEBOOK + Fuente→LEAD DIGITAL; opps: Canal Opp→Facebook, Tipo de Lead→Lead digital, Fuente→LEAD DIGITAL.
Secuencia (RUN_ID origen-fuente-3c50e43ec6a9):
# dry-run (las 7 cuentas) -> A=28, B=7, 35 opps
# piloto Tampico (Patrón B)
python scripts/fix_origen_fuente_incoherencia.py --location WCHyow6KpjLFYriQWdbJ --apply --run-id origen-fuente-3c50e43ec6a9
# verificación live 7/7 OK -> batch resto:
for LOC in GbKkBpCmKu2QmloKFHy3 KEZ7dAhgwzK4uZfMvZuj uJEn2iuUficuml9zxAnt uZnMH5bO6MXTHcgHeyq9 nF1uEaYB3mCK5em9bPn2 jE41bVhhnb5T505BFm4F; do
python scripts/fix_origen_fuente_incoherencia.py --location $LOC --apply --run-id origen-fuente-3c50e43ec6a9; done
Marca en el batch reportó B=1 (no 7): los otros 6 REDES SOCIALES de Marca ya se habían corregido solos por replicación n8n desde Tampico. 0 errores en todas.
VERIFICACIÓN
Cross-tab en vivo en Marca (re-GET de 1328+ contactos vía API, no cache):
- LEAD DIGITAL = 908 = FORMULARIO 612 + FACEBOOK 296 → COHERENTE (antes 929 vs 901).
- canal=SUCURSAL & fuente=LEAD DIGITAL: 0 (antes 28).
- REDES SOCIALES: 0 (antes 7).
- Canal SUCURSAL 419 (antes 426; −7 que pasaron a FACEBOOK). Piloto Tampico verificado 7/7 (canal=FACEBOOK, fuente=LEAD DIGITAL sostenido; n8n [2004] no revirtió en el momento).
EDGE-CASES / TRAMPAS
- NO confiar en
contact.sourcede Marca para clasificar origen: la replicación n8n lo estampa (Formulario/Sucursal) y diverge del lado sucursal. UsarcreatedBy.sourceen vivo (GET individual). - Tampico tiene los campos con capitalización distinta (
CANAL DE ORIGEN): resolver field ids case-insensitive / por FIELD_ALIASES, nunca por nombre exacto. - Falso positivo en verificación: match difuso de nombre ("edgar") atrapó "edgar alejandro pozos" y "edgard radiadores" (PROSPECCIÓN/SUCURSAL, no eran target). Filtrar por id, no por substring de nombre.
- Riesgo de reversión del Patrón B: los 7 de Tampico son WEB_USER y n8n [2004] re-pone canal=SUCURSAL a WEB_USER. Si se re-editan, podrían revertir. Pendiente endurecer [2004].
REUTILIZABLE
python scripts/audit_origen_fuente_incoherencia.py --all→ clasifica incoherentes con createdBy en vivo (read-only).python scripts/fix_origen_fuente_incoherencia.py --all(dry-run) /--apply --run-id <uuid>→ corrige + propaga a opps. Idempotente: re-correr no re-toca.- Rollback: dashboard → run
origen-fuente-3c50e43ec6a9.
CAUSA RAÍZ — CIERRE EN TIEMPO REAL (2026-05-30)
Endurecido el workflow n8n [2004] (ddUEORBEtZLzsQF2) con n8n/_add_fuente_reconcile.py (backup→dry-run→apply→verify→E2E). En la rama "Creado por usuario" (esUsuario=true), tras Tag- facebook-ads se añadió: IF fuenteEsLeadDigital → PUT Fuente de Prospecto=SUCURSAL. El Code node se extendió para resolver el field contact.fuente_de_prospecto y leer su VALOR ACTUAL del GET del contacto (contact.customFields[].value), exponiendo fuente/fuenteActual/fuenteEsLeadDigital. Solo reconcilia si vale exactamente LEAD DIGITAL → preserva ALIANZA/PROSPECCIÓN/etc. versionId 11c7184b→e8f6f33f, 19→21 nodos. Backup n8n/backup_fuente_reconcile_ddUEORBEtZLzsQF2_20260530_190108.json.
E2E en vivo (Cancún, esau sotelo do3ClHt57hfHj0hw4tKk, WEB_USER): (1) ensuciado FORMULARIO/LEAD DIGITAL → webhook 8d574598 → SUCURSAL/SUCURSAL ✓. (2) ensuciado FORMULARIO/PROSPECCIÓN → webhook → SUCURSAL/PROSPECCIÓN intacta ✓ (preservación). Contacto restaurado a su estado real SUCURSAL/SUCURSAL.
CAUSA RAÍZ PATRÓN B — CIERRE EN TIEMPO REAL (2026-05-30)
Cerrado con n8n/_add_redes_sociales_branch.py. En la rama esUsuario, ANTES del PUT canal=SUCURSAL, se intercaló IF "Fuente es REDES SOCIALES": [true]→PUT Canal=FACEBOOK→PUT Fuente=LEAD DIGITAL (camino digital, igual que el fix de datos Patrón B); [false]→PUT Canal=SUCURSAL (camino existente intacto). Code node expone fuenteEsRedesSociales. versionId e8f6f33f→37f780e1, 21→24 nodos. Backup n8n/backup_redes_sociales_ddUEORBEtZLzsQF2_20260530_192343.json.
E2E vivo (Cancún, esau): T1 SUCURSAL/REDES SOCIALES→FACEBOOK/LEAD DIGITAL ✓; T2 regresión FORMULARIO/LEAD DIGITAL→SUCURSAL/SUCURSAL ✓ (la rama Patrón A sigue intacta por el false branch). Restaurado.
PENDIENTES
- [HECHO 2026-05-30] Causa raíz Patrón A cerrada en [2004] (
_add_fuente_reconcile.py). Nota:fix_branch_user_origin.py(batch) sigue sin reconciliar Fuente, perofix_origen_fuente_incoherencia.pycubre el backlog y [2004] cubre lo nuevo en tiempo real. - [HECHO 2026-05-30] Causa raíz Patrón B (REDES SOCIALES) cerrada en [2004] (
_add_redes_sociales_branch.py, ver sección arriba). - Re-sync de la cache local de las 7 cuentas (el panel GHL ya está bien; la cache SQLite quedó stale para los 35 contactos).
ENLACES
- Memorias: incoherencia_canal_fuente_lead_digital, super_script_fix_branch_user_origin, n8n_2004_canal_origen_complemento, createdby_only_in_individual_get, sucursal_tag_on_digital_leads
- Scripts:
scripts/fix_origen_fuente_incoherencia.py,scripts/audit_origen_fuente_incoherencia.py,n8n/_add_fuente_reconcile.py(cierre raíz Patrón A [2004]),n8n/_add_redes_sociales_branch.py(cierre raíz Patrón B [2004]),n8n/_add_canal_origen_branch.py(rama previa),scripts/fix_branch_user_origin.py,scripts/fuente_prospecto_workflow.py,scripts/tag_canal_origen_workflow.py,scripts/canal_origen_resolver.py