descripción

This commit is contained in:
2026-05-30 20:16:12 -06:00
parent a35d26fac0
commit fb20cf8bd5
23 changed files with 32388 additions and 2 deletions
@@ -89,9 +89,15 @@ python -c "import sync_engine as se; a=next(x for x in se.parse_accounts_csv() i
- Descomponer cualquier bucket grande por (expected_branch → actual_branch) con Counter antes de concluir "error de datos": muchas veces es mapeo del Verificador, no datos.
- `cleanup_brand_duplicate_replica_opps.py --only-link <link>` es el camino seguro para el descuadre positivo de opps por doble réplica (verifica en vivo + snapshot + script_audit).
## ACCIÓN (3ª tanda — reasignación opp por homónimos)
Al ir a "crear la opp de miguel en Temixco" se descubrió que **NO procedía**: el lead digital $80,200 es de OTRO "Miguel Angel" (Eugenia `+525530454950`, contacto Marca `hE9U9Q`), distinto del Miguel-Temixco (`+527775114949`, contacto `RwxMQr0`). Ese lead ya estaba replicado (Eugenia `kGda02`↔Marca `1A3P5b`), pero `1A3P5b` colgaba del contacto EQUIVOCADO (`RwxMQr0`) porque la réplica n8n matcheó por nombre. `hE9U9Q` (su dueño real) tenía 0 opps.
- **Fix:** `PUT /opportunities/1A3P5b` con `contactId=hE9U9Q` (+ name/pipeline/stage/mv). **GHL v2 SÍ acepta cambiar contactId vía PUT** (status 200) — el gotcha de `build_brand_opp_payload` no aplicó. run_id `reassign-miguel-opp-20260530`, snapshot `reassign_miguel_opp_20260530_155526.json`. Verificado: RwxMQr0 solo $56,671, hE9U9Q solo $80,200.
## PENDIENTES
- **test21** (fantasma de índice): GET 400 / search 200. No accionable; se auto-corrige cuando GHL reconcilie el índice. Sigue en `without_tienda` + `not_in_any_branch`.
- **Lead digital de miguel** (opp Marca 1A3P5b $80,200, sin link): es un lead digital creado directo en Marca que nunca bajó a su sucursal (Temixco). Idealmente lo baja la cascada n8n Marca→Sucursal. Queda solo-Marca; el conteo cuadra igual. Revisar si ese lead digital es válido y debe cascar.
- **test21** (fantasma de índice): GET 400 / search 200. No accionable; se auto-corrige cuando GHL reconcilie el índice.
- **Causa raíz n8n homónimos**: la réplica Sucursal→Marca debe matchear por teléfono/`id_contacto_sucursal` antes que por nombre (si no, vuelve a colgar opps de homónimos en el contacto equivocado). Ver [[matching_rules]], [[n8n_workflows_v2_hardened]].
- ~~Leads digitales nuevos~~ **RESUELTO**: `David Arturo Vega` (Satélite) y `PER,LA MARIA VILLA` (La Viga) aparecieron ~15:56 (actividad viva) como descuadre +2. NO eran leads sin cascar: sus contactos+opps YA estaban en sucursal; el +2 eran **réplicas duplicadas en Marca sin link** (mismo patrón isai/Patricia, pero sin link compartido → caían en `missing_id_field`, no `duplicate_link`). Fix: (1) TIENDA llenada (`leads-tienda-20260530`); (2) borradas las 2 duplicadas sin link `LJPKIsd`+`OyRtrlGb`, conservadas las enlazadas `Mt9Dafz3`+`aZf3pzuc` (run_id `delete-dup-leads-opps-20260530`, snapshot `delete_dup_leads_opps_20260530_160744.json`). → opps 1342=1342, diff 0.
- Patrón a vigilar: el n8n de opps sigue generando réplicas duplicadas en Marca (Patricia, isai, PER,LA, David — 4 en un día). Unas con link compartido, otras sin link. La causa raíz (idempotencia) está en [[n8n_opp_idempotency_baserow_mapping]]; revisar por qué siguen apareciendo.
## ENLACES
- Memorias: [[positive_opp_descuadre_double_replica]], [[verificador_tipo_de_tienda_colapso]], [[positive_descuadre_stale_cache]], [[name_account_with_location_id]], [[audit_hub_map_metepec_pilares]]
@@ -0,0 +1,94 @@
---
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`
@@ -0,0 +1,63 @@
---
id: CASE-2026-05-30-opp-marca-cf-vacios-mapeo-contacto
fecha: 2026-05-30
categoria: cascada_n8n
location_ids: ["GbKkBpCmKu2QmloKFHy3 (Monte Providencia / Marca)", "yjqKxoO02rsdwdJZSPmD (85950 - MP - Temixco)"]
run_ids: ["backfill-opp-cf-20260530-164821"]
snapshots: ["generated/migrations/backfill_opp_cf_8HITkGkOn3gN23Tl8LBr_backfill-opp-cf-20260530-164821.json"]
status: resuelto
memorias: ["[[n8n_opp_sync_match]]", "[[opp_multiplicity_replication_gap]]", "[[sucursal_to_marca_cf_drop_on_create]]", "[[sucursal_datatype_divergence_intentional]]"]
playbooks: ["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
- [[n8n_opp_sync_match]] · [[opp_multiplicity_replication_gap]] · [[sucursal_to_marca_cf_drop_on_create]] · [[sucursal_datatype_divergence_intentional]] · [[custom_fields_picklist_alignment]]
- `n8n/_add_contact_to_opp_mapping.py` · `scripts/backfill_brand_opp_cf_from_source.py`
- docs/PLAYBOOK_ENLACE_OPORTUNIDADES.md
+2
View File
@@ -34,6 +34,8 @@ No registres: lecturas triviales, consultas de un solo dato, o trabajo puramente
| Fecha | ID | Categoría | Triggers (keywords para grep) | Status | Enlace |
|---|---|---|---|---|---|
| 2026-05-30 | CASE-2026-05-30-incoherencia-canal-fuente-lead-digital | custom_field, cascada_n8n, descuadre | `LEAD DIGITAL no cuadra con FORMULARIO + FACEBOOK`, `929 vs 901 descuadre 28`, `Canal=SUCURSAL pero Fuente=LEAD DIGITAL`, `REDES SOCIALES fuente no canónica Tampico`, `fix_branch_user_origin NO toca Fuente`, `[2004] pone Canal=SUCURSAL a WEB_USER sin reconciliar Fuente`, `source de Marca lo estampa n8n no es fiable usar createdBy.source`, `fix_origen_fuente_incoherencia.py`, `audit_origen_fuente_incoherencia.py`, `run origen-fuente-3c50e43ec6a9` | resuelto | [caso](2026-05-30-incoherencia-canal-fuente-lead-digital.md) |
| 2026-05-30 | CASE-2026-05-30-opp-marca-cf-vacios-mapeo-contacto | cascada_n8n, custom_field | `opp Marca Sucursal TIENDA Canal de Origen vacíos`, `desde sucursal no se traen datos de oportunidades`, `opp solo-link solo id_oportunidad_sucursal`, `Cfgwp0bOtDW8zuKW mapear campos del contacto a la opp`, `Obtener Contacto - SUCURSAL nodo nuevo`, `Mapeo completo oportunidad origen - SUCURSAL`, `8HITkGkOn3gN23Tl8LBr OQBrOQN9mNlybjlzB8Jk Miguel mahernandez2282`, `fuente_de_posible_cliente CANAL DE ORIGEN del contacto`, `backfill_brand_opp_cf_from_source.py` | resuelto | [caso](2026-05-30-opp-marca-cf-vacios-mapeo-contacto.md) |
| 2026-05-30 | CASE-2026-05-30-comparativa-auditoria-completa-buckets | descuadre, cascada_n8n, config_location | `Comparativa Marca vs Sucursales auditar todos los buckets`, `opps +1 opportunities_in_brand_duplicate_link`, `present_in_other_branch_not_assigned 84 falsos positivos`, `TIENDA=METEPEC vive en Pilares 85935`, `DIGITAL_HUB_BY_SHELL hub-map en código`, `audit lee Verificador CSV no Baserow`, `PATRICIA PARRA zzBzWC4adBrdTA8WhQph réplica abandoned $0`, `redirect Windows cp1252 no utf-8`, `re-sync Marca antes de re-auditar` | resuelto | [caso](2026-05-30-comparativa-auditoria-completa-buckets.md) |
| 2026-05-30 | CASE-2026-05-30-descuadre-opp-deadlink | descuadre, cascada_n8n | `DIFERENCIA OPORTUNIDADES +2`, `descuadre positivo todos los buckets de opps en 0`, `opp Marca link muerto GET 400 Opportunity doesn't exist or is deleted`, `réplica obsoleta n8n no borró id rotado`, `contacto 2 opps Marca 1 sola viva sucursal`, `cleanup scripts no atrapan dead-link único`, `re-enlazar vs borrar opp 1:1` | resuelto | [caso](2026-05-30-descuadre-opp-deadlink.md) |
| 2026-05-30 | CASE-2026-05-30-descuadre-opp-replica-duplicada-marca | descuadre, cascada_n8n, duplicado | `DIFERENCIA OPORTUNIDADES +10`, `descuadre positivo Marca > Sucursales`, `réplicas duplicadas mismo ID Oportunidad Sucursal`, `dos opps Marca misma opp sucursal`, `opportunities_in_brand_duplicate_link`, `Cfgwp0bOtDW8zuKW CREATE en vez de UPDATE`, `Decidir Match Create vs Update`, `cleanup_brand_orphan cero huérfanas pero descuadre`, `antelmo anselmo mismo teléfono distinto contacto`, `cleanup_brand_duplicate_replica_opps.py` | parcial | [caso](2026-05-30-descuadre-opp-replica-duplicada-marca.md) |