Files
MP-Manager/docs/casos/2026-05-29-origen-sucursal-contactos-usuario.md
T
2026-05-30 14:31:19 -06:00

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
nF1uEaYB3mCK5em9bPn2 (85974 - MP - Eugenia, piloto)
todas las sucursales productivas (47, batch); excluye Marca GbKkBpCmKu2QmloKFHy3 y demos Vf7qQl3L9vakJ8hDtQ8e / Z64WQKORPVwXb5mn68Ef
fbuo-cc20241b7a6f (piloto Eugenia)
fbuo-batch-8c31110b2d (batch 47 sucursales)
resuelto
createdby_only_in_individual_get
super_script_fix_branch_user_origin
feedback_dry_run_protocol
name_account_with_location_id

TRIGGERS

  • createdBy.source, WEB_USER, MOBILE_USER, INTEGRATION
  • contactos creados por usuario, canal de origen sucursal, origen sucursal
  • createdBy no viene en el listado, GET /contacts/ omite createdBy
  • fix_web_user_branch_contacts roto, siempre detecta 0
  • fix_branch_user_origin.py, super script origen sucursal
  • Fuente de Prospecto ALIANZA, PROSPECCIÓN, no sobrescribir Fuente de Prospecto
  • tag formulario -> sucursal, etiqueta de origen única
  • Canal 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):

  1. Primer dry-run del super script leyendo createdBy del listadoWEB_USER a corregir: 0. Distribución: (vacío): 129. Sospecha: el listado no trae createdBy.
  2. Comparación listado vs GET individual de un contacto:
    • Listado GET /contacts/: keys incluyen source (=None), attributions, pero NO createdBy.
    • Individual GET /contacts/{id}: trae createdBy = {source: 'WEB_USER', sourceName: 'EUGENIA- 85974 MP', channel: 'APP', ...} y attributionSource = {medium: 'manual', sessionSource: 'CRM UI'}.
  3. 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'.
  4. Audit log oficial del CRM para maMw3C8QmhGVChRqL36y (JUAN CARLOS RAMIREZ): "Action: Created, Modified by: Web user" → coincide con createdBy.source == WEB_USER.
  5. Schema de contacto Eugenia (resolución de campos por alias, correcta):
    • Canal de OrigenKLEZyRNR0jrldccerErV (name real "CANAL DE ORIGEN")
    • Fuente de ProspectoQN1BNTKgCzcSOHa2wSZc
    • SucursalpmrGTW3tIa7oz7rQJMVx, TIENDAH3g8J4NbgbcM4glyW9GZ
  6. 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 Prospecto contiene valores de negocio que NO deben pisarse.

CAUSA RAÍZ

  1. createdBy solo viene en el GET individual del contacto; el listado paginado lo omite (ver createdby_only_in_individual_get). El script previo scripts/fix_web_user_branch_contacts.py lo leía del listado → roto silenciosamente (siempre 0).
  2. Los contactos creados por empleado quedan con createdBy.source ∈ {WEB_USER (UI web), MOBILE_USER (app móvil)}; los replicados desde Marca por n8n quedan INTEGRATION.

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 (quita formulario/facebook-ads), Canal de Origen = SUCURSAL. Si falta Sucursal/TIENDA, se completan desde el Verificador CSV (load_verifier_map).
  • TODAS las opps del contacto: Canal de Origen de la Oportunidad = Sucursal + propaga Sucursal/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, todos applied.

EDGE-CASES / TRAMPAS

  • No leer createdBy del 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