Files
MP-Manager/docs/casos/2026-05-30-incoherencia-canal-fuente-lead-digital.md
T
2026-05-30 20:16:12 -06:00

95 lines
9.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
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 prospecto`
- `929 vs 901`, `descuadre 28 LEAD DIGITAL`, `Canal=SUCURSAL pero Fuente=LEAD DIGITAL`, viola AGENTS Cap.3
- `REDES SOCIALES` fuente de prospecto (valor no canónico), Tampico 7 contactos
- `fix_branch_user_origin.py NO toca Fuente de Prospecto`, n8n `[2004]` pone Canal=SUCURSAL a WEB_USER sin reconciliar Fuente
- `createdBy.source WEB_USER MOBILE_USER` = captura manual sucursal; `source` de 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
1. Workflow n8n `[2004]` (ddUEORBEtZLzsQF2) y su gemelo Python `fix_branch_user_origin.py` ponen `Canal de Origen=SUCURSAL` a los contactos creados por empleado (WEB_USER/MOBILE_USER) **pero deliberadamente NO tocan "Fuente de Prospecto"** ([fix_branch_user_origin.py:27-29,234](../../scripts/fix_branch_user_origin.py#L234)) para no pisar ALIANZA/PROSPECCIÓN. Efecto colateral: cuando voltean canal a SUCURSAL dejan el `LEAD DIGITAL` viejo huérfano → los 28 (Patrón A).
2. En Tampico se usó el valor no canónico `REDES SOCIALES` como 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.source` de Marca** para clasificar origen: la replicación n8n lo estampa (Formulario/Sucursal) y diverge del lado sucursal. Usar `createdBy.source` en 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
1. [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, pero `fix_origen_fuente_incoherencia.py` cubre el backlog y [2004] cubre lo nuevo en tiempo real.
2. [HECHO 2026-05-30] Causa raíz Patrón B (REDES SOCIALES) cerrada en [2004] (`_add_redes_sociales_branch.py`, ver sección arriba).
3. 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`