7.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-29-origen-sucursal-contactos-usuario | 2026-05-29 | custom_field |
|
|
resuelto |
|
TRIGGERS
createdBy.source,WEB_USER,MOBILE_USER,INTEGRATIONcontactos creados por usuario,canal de origen sucursal,origen sucursalcreatedBy no viene en el listado,GET /contacts/ omite createdByfix_web_user_branch_contacts roto,siempre detecta 0fix_branch_user_origin.py,super script origen sucursalFuente de Prospecto ALIANZA,PROSPECCIÓN,no sobrescribir Fuente de Prospectotag formulario -> sucursal,etiqueta de origen únicaCanal de Origen de la Oportunidad = Sucursal
SÍNTOMA
Los contactos creados a mano en una sucursal (por un empleado) no traen canal de origen confiable: el campo nativo source llega vacío/None y no indica "sucursal". Esto ensucia el CF Canal de Origen (contacto y opp) y las etiquetas de origen. Objetivo: identificar los contactos creados 100% por usuario en sucursal y dejarlos con origen = Sucursal (CF + tag), propagando a sus oportunidades. Solo sucursales (no Marca, no demos).
DIAGNÓSTICO
Pasos read-only (todos con el helper tag_canal_origen_workflow):
- Primer dry-run del super script leyendo
createdBydel listado →WEB_USER a corregir: 0. Distribución:(vacío): 129. Sospecha: el listado no traecreatedBy. - Comparación listado vs GET individual de un contacto:
- Listado
GET /contacts/: keys incluyensource(=None),attributions, pero NOcreatedBy. - Individual
GET /contacts/{id}: traecreatedBy = {source: 'WEB_USER', sourceName: 'EUGENIA- 85974 MP', channel: 'APP', ...}yattributionSource = {medium: 'manual', sessionSource: 'CRM UI'}.
- Listado
- Muestra de 20 GETs individuales en Eugenia: 3 WEB_USER + 17 INTEGRATION → el criterio discrimina perfecto. Proxy del listado: WEB_USER ≈
attributions[0].medium == 'manual'. - Audit log oficial del CRM para
maMw3C8QmhGVChRqL36y(JUAN CARLOS RAMIREZ): "Action: Created, Modified by: Web user" → coincide concreatedBy.source == WEB_USER. - Schema de contacto Eugenia (resolución de campos por alias, correcta):
Canal de Origen→KLEZyRNR0jrldccerErV(name real "CANAL DE ORIGEN")Fuente de Prospecto→QN1BNTKgCzcSOHa2wSZcSucursal→pmrGTW3tIa7oz7rQJMVx,TIENDA→H3g8J4NbgbcM4glyW9GZ
- Distribución de valores en Eugenia (SQLite):
Canal de Origen{SUCURSAL 84, FORMULARIO 28, FACEBOOK 14, vacío 3};Fuente de Prospecto{SUCURSAL 84, LEAD DIGITAL 42, ALIANZA 2, PROSPECCIÓN 1}. →Fuente de Prospectocontiene valores de negocio que NO deben pisarse.
CAUSA RAÍZ
createdBysolo viene en el GET individual del contacto; el listado paginado lo omite (ver createdby_only_in_individual_get). El script previoscripts/fix_web_user_branch_contacts.pylo leía del listado → roto silenciosamente (siempre 0).- Los contactos creados por empleado quedan con
createdBy.source∈ {WEB_USER(UI web),MOBILE_USER(app móvil)}; los replicados desde Marca por n8n quedanINTEGRATION.
ACCIÓN
Super script nuevo scripts/fix_branch_user_origin.py (registrado en SCRIPTS_METADATA como "Origen Sucursal (contactos creados por usuario)"). Ver super_script_fix_branch_user_origin. Orden contacto→opp:
- Contacto: tag único
sucursal(quitaformulario/facebook-ads),Canal de Origen= SUCURSAL. Si faltaSucursal/TIENDA, se completan desde el Verificador CSV (load_verifier_map). - TODAS las opps del contacto:
Canal de Origen de la Oportunidad= Sucursal + propagaSucursal/TIENDA. - NO toca
Fuente de Prospecto(decisión del owner: preserva ALIANZA/PROSPECCIÓN). No sincroniza a Marca.
Protocolo dry-run → piloto → batch (feedback_dry_run_protocol):
# Dry-run (Fase 1):
python scripts/fix_branch_user_origin.py --location nF1uEaYB3mCK5em9bPn2
# Piloto:
python scripts/fix_branch_user_origin.py --location nF1uEaYB3mCK5em9bPn2 --apply --run-id fbuo-cc20241b7a6f
# Batch:
python scripts/fix_branch_user_origin.py --all --apply --run-id fbuo-batch-8c31110b2d
VERIFICACIÓN
- Piloto Eugenia (run
fbuo-cc20241b7a6f, success): 89 creados por usuario, 6 contactos, 98 opps. JUAN CARLOS post-apply en vivo: tags=['sucursal'], Canal de Origen='SUCURSAL', Sucursal='Narvarte Oriente, Ciudad de México', TIENDA='EUGENIA', Fuente de Prospecto='ALIANZA' intacto; su opp Tpd964ztTwgNf1ipL5NC con Canal de Origen de la Oportunidad='Sucursal' + Sucursal propagado. - Consistencia Sucursal: 126/129 contactos ya tenían 'Narvarte Oriente, Ciudad de México'; los 3 vacíos quedaron con el MISMO valor del Verificador. Sin divergencia.
- Batch (run
fbuo-batch-8c31110b2d, success): 359 creados por usuario detectados en 47 sucursales, 48 contactos + 273 opps corregidos, 0 errores. Auditoría: 110 cambios contact + 284 opp, todosapplied.
EDGE-CASES / TRAMPAS
- No leer
createdBydel listado → siempre 0. Hay que GET individual por contacto (costoso pero fiel; el dashboard paraleliza por sucursal). - No sobrescribir
Fuente de Prospecto: contiene ALIANZA/PROSPECCIÓN (valores de negocio), no solo SUCURSAL/LEAD DIGITAL. - Incluir MOBILE_USER además de WEB_USER (ambos = creación manual por empleado).
- 8 sucursales tenían 0 contactos (no tocadas) y 5 tienen Verificador con Sucursal vacía + 2 no están en el Verificador: si más adelante reciben contactos creados por usuario sin Sucursal, no se autocompletará hasta corregir el Verificador.
REUTILIZABLE
# createdBy SOLO en GET individual:
full = ghl_request("GET", f"/contacts/{cid}", token); inner = full.get("contact") or full
src = (inner.get("createdBy") or {}).get("source") # WEB_USER | MOBILE_USER | INTEGRATION
PENDIENTES
- Corregir el Verificador para las sucursales con Sucursal vacía / ausentes (Isidro Fabela, SENDERO, Grand Plaza, Independencia, Morelia 3, + las 2 ausentes) por si reciben contactos creados por usuario.
- Confirmar si las 8 sucursales con 0 contactos es esperado (sucursales nuevas) o falta sync/acceso.
- Identificación de origen Facebook Ads / formulario en sucursal (fuera de alcance de este caso).
ENLACES
- Memorias: createdby_only_in_individual_get, super_script_fix_branch_user_origin, feedback_dry_run_protocol, name_account_with_location_id
- Scripts:
scripts/fix_branch_user_origin.py, helpers descripts/tag_canal_origen_workflow.py,scripts/fill_sucursal_tienda_from_location.py(load_verifier_map) - Roto/superado:
scripts/fix_web_user_branch_contacts.py - Logs:
generated/logs/fbuo_batch_8c31110b2d.log