Files
MP-Manager/docs/casos/2026-05-29-backfill-cf-vehiculo-temixco-marca.md
2026-05-30 14:31:19 -06:00

5.6 KiB


id: CASE-2026-05-29-backfill-cf-vehiculo-temixco-marca fecha: 2026-05-29 categoria: custom_field | cascada_n8n location_ids: ["GbKkBpCmKu2QmloKFHy3 (Monte Providencia / Marca)", "yjqKxoO02rsdwdJZSPmD (85950 - MP - Temixco)"] run_ids: ["4b26f163-f87c-48c5-be53-462b2e6f53da"] snapshots: [] status: parcial memorias: sucursal_to_marca_cf_drop_on_create, positive_descuadre_stale_cache, n8n_realtime_replication playbooks: [docs/PLAYBOOK_DESCUADRE.md]

TRIGGERS

  • contacto en Marca sin datos de vehículo, customFields count: 0 en Marca, réplica Sucursal→Marca sin custom fields
  • Sincronización Sucursal (source) + tag sincronizado-sucursal con CFs vacíos
  • Cristhian laura ramirez juarez, Hugo Lopez, Temixco, +527772582548, +527772162523
  • nombres de custom field idénticos entre sucursal y Marca (descarta name-mismatch)
  • Export_Contacts_leads CSV con columnas de vehículo vacías

SÍNTOMA

CSV export de Marca: 2 leads de Temixco (Cristhian U9DWipeW9XhQTZZMzFl9, Hugo bUNqMZaLI1QCn4yQM6qT) llegaron a Marca sin Versión/Marca/Año de vehículo, Modalidad, Fuente, Sucursal. Edgar (Tampico) sí llegó completo.

DIAGNÓSTICO (read-only)

  1. mcp get_contact (cache) Marca → custom_fields: []. Cache SQLite con synced_at ANTERIOR a la creación → stale, no concluir aún (positive_descuadre_stale_cache).
  2. search_contacts por fragmento de teléfono en sucursales de Morelos (777 = Cuernavaca) → ambos en Temixco (yjqKxoO02rsdwdJZSPmD) con 6-8 CFs completos.
  3. Comparación de nombres de contact CFs Temixco vs Marca vs Tampico (tabla object_schemas): idénticos. → DESCARTA la hipótesis de name-mismatch del workflow [1604] (el análisis estático del jsCode sugería divergencia de nombres; es FALSO).
  4. GET en VIVO (no cache) de los 4 contactos vía ghl_client._request('GET', f'/contacts/{id}', token):
    • Marca (ambos): customFields=0, dateAdded == dateUpdated (nunca actualizados).
    • Temixco (ambos): 8 CFs; dateUpdated ~4h después de creación (= workflow [2004] que añadió CANAL DE ORIGEN + TIENDA).
  5. Ejecuciones n8n del workflow Sucursal→Marca (x4DqZ5FtSc43tdzB) consultadas, pero el set reciente no cubre la ventana de creación real (2026-05-29 ~22:40/22:48 UTC); no se obtuvo el log del momento exacto.

CAUSA RAÍZ

  1. Confirmada: la réplica Sucursal→Marca creó el contacto en Marca (39-40 s después del de sucursal) sin copiar ningún custom field, y no hubo UPDATE posterior.
  2. NO es divergencia de nombres de esquema (verificado idénticos).
  3. Probable (confianza media): carrera de tiempo — el flujo leyó el contacto origen antes de que los CFs del formulario estuvieran disponibles. Es intermitente (Edgar/Tampico sí funcionó por el mismo path). Confirmación pendiente requiere log de ejecución n8n del momento de creación.

ACCIÓN

Backfill manual de 8 CFs Temixco→Marca (mapeo por nombre → object_schemas de Marca), confirmado por el owner.

  • run_id=4b26f163-f87c-48c5-be53-462b2e6f53da (script_audit, reversible). Script inline (no archivo).
  • Patrón: gc._request('PUT', f'/contacts/{cid}', mtok, json={'customFields':[{'id':marca_field_id,'field_value':val}, ...]}).
  • Orden: piloto Cristhian → verificación live (8 CFs) → Hugo.

VERIFICACIÓN

Antes: ambos Marca customFields=0. Después (GET live): ambos customFields=8 con valores correctos (Año 2026/2011, Marca ITALIKA/mercedes benz, Versión DM 200/c280, Modalidad Tradicional (Resguardo), Fuente SUCURSAL/PROSPECCIÓN, Sucursal "Temixco, Morelos", CANAL DE ORIGEN SUCURSAL, TIENDA TEMIXCO).

EDGE-CASES / TRAMPAS

  • No confiar en la cache SQLite para concluir CFs vacíos: synced_at puede ser anterior a la creación del contacto. Verificar SIEMPRE en vivo antes de backfillear.
  • El análisis estático del jsCode del workflow puede mentir: dio "name-mismatch" como causa, falso. Verificar nombres reales en object_schemas.
  • PUT contacto usa clave field_value (no value) y mergea CFs (no reemplaza).
  • Edgar (Tampico) se omitió a pedido del owner: ya tenía datos completos (control de que el path SÍ funciona a veces).

REUTILIZABLE

# Nombre de CF -> field_id de una location (tabla object_schemas)
import sqlite3, paths
conn=sqlite3.connect(paths.DB_PATH); conn.row_factory=sqlite3.Row
nameid={}
for r in conn.execute("SELECT field_id,field_name FROM object_schemas WHERE location_id=? AND object_key='contact'",(loc,)):
    nameid.setdefault(r['field_name'], r['field_id'])
# GET live + PUT CFs
import sync_engine; from sync_engine import ghl_client as gc
tok={a['location_id']:a for a in sync_engine.parse_accounts_csv()}[loc]['token']
gc._request('PUT', f'/contacts/{cid}', tok, json={'customFields':[{'id':fid,'field_value':v}]})

PENDIENTES

  • Causa raíz n8n al 100%: revisar ejecuciones de x4DqZ5FtSc43tdzB en la ventana 2026-05-29 ~22:40/22:48 UTC (paginar más atrás con /api/v1/executions?workflowId=...&includeData=true) para confirmar carrera vs gap de lógica.
  • Sistémico: si es carrera, hay riesgo de más contactos Temixco (u otras sucursales) con CFs vacíos en Marca. Considerar un audit que liste contactos Marca con source='Sincronización Sucursal' y customFields=0 para barrido masivo.

ENLACES