Skip to content

TODO — Pendiente de aprobación

Recursos que NO se migran en la primera corrida y quedan a la espera de decisión del cliente / lead técnico.

1. Meetings / Appointments / Calendars / Calendar Events

Estado: ❌ Pausado.

Por qué se sacó del scope inicial:

  • El modelo meetings del destino está pensado alrededor de Google Calendar (campos google_event_id, google_meet_link, google_calendar_id, synced, synced_at). En GHL los appointments viven dentro de su propio sistema de booking — no hay un Google Calendar Event ID que mapear.
  • Hay que decidir si:
    1. Migrar appointments sin Google sync y dejar synced=false (los usuarios re-conectarían su Google después en imcrmdev).
    2. No migrar histórico y arrancar limpio.
    3. Migrar solo eventos futuros (a partir de hoy) y descartar el histórico.
  • También falta saber si Trebol usa el módulo de citas activamente (volumen real, importancia para retención de cliente).

Lo que queda en pausa:

Recurso GHLEndpointDestino propuesto
Calendarios (catálogo)GET /calendars/?locationId=…calendars (NUEVA)
Calendar Events (rango)GET /calendars/events?calendarId=…&startTime=…&endTime=…meetings
Appointments por contactoGET /contacts/{id}/appointmentsmeetings

Mapping propuesto (cuando se apruebe)

Calendar (calendars — NUEVA)

OrigenDestino
idexternal_id
namename
descriptiondescription
calendarTypecalendar_type
eventTypeevent_type
slotDurationslot_duration
teamMembers[]team_members[]
widgetSlugwidget_slug

Appointment / Calendar Event (meetings)

OrigenDestinoNota
idexternal_id
titletitle
notes o descriptiondescription
startTimestart_timeparse ISO
endTimeend_timeparse ISO
addresslocation
appointmentStatusstatusmapear: confirmedconfirmed, cancelledcancelled, noshowno_show, default → scheduled
assignedUserIdowner_user_idresolver a users._id destino
contactIdcontact_idresolver a contacts_new._id destino
calendarIdcustom_fields.calendar_idpreserva FK lógica
dateAddedcreated_at
dateUpdatedupdated_at
google_event_idvacío (no aplica)
google_meet_linkvacío
google_calendar_idvacío
syncedfalse (forzado)
attendees[]array vacío (GHL no expone igual)

Decisiones que necesitamos antes de arrancar

  • [ ] ¿Migra histórico, solo futuro, o nada?
  • [ ] Si migra histórico, ¿cuántos años atrás? (recomendación: 1 año)
  • [ ] ¿Los usuarios van a re-conectar Google Calendar manualmente en imcrmdev, o es deal-breaker tener sync inmediato?
  • [ ] ¿Hay que enviar notificaciones (reminders_sent[]) post-migración? (Recomendación: NO, son citas pasadas)

2. Custom Field Definitions (catálogo en colección propia)

Estado: ❌ Pausado (la persistencia del catálogo). El uso interno sí se mantiene — explicación abajo.

¿Qué es un Custom Field Definition?

En GHL, un Custom Field es un campo extra que se agrega a un contacto, oportunidad u otro objeto. Por ejemplo, para Trebol probablemente existen:

  • "Bank Name" (texto)
  • "Comments & Questions" (texto largo)
  • "B-018-GLB. SMS Opt-Out Date" (fecha)
  • "Gender" (single option: Male/Female)
  • etc.

Trebol tiene 227 custom field definitions.

Cada definición dice: "este campo se llama X, es de tipo TEXTO/FECHA/NÚMERO/RADIO, su key interna es contact.bank_name, va en la posición 50 del formulario, etc."

Cada contacto tiene un array customFields: [{id, value}] donde id es el id de la definición y value es el valor para ese contacto.

Por qué se sacó al TODO

La pregunta abierta es: ¿queremos que imcrmdev replique el concepto de "custom fields" como entidad de primera clase?

  • Si SÍ: necesitamos una colección custom_field_definitions (con tipo, opciones de picklist, posición en el form) para que el frontend pueda renderizar formularios dinámicos por tenant. Hoy imcrmdev no tiene este concepto — los custom_fields en contacts_new son un objeto libre tipo {key: value} sin metadatos.
  • Si NO: los valores quedan guardados en contacts_new.custom_fields como objeto libre (que es lo que el script ya hace), pero el frontend no sabe que "bank_name" debe renderizarse como texto y "gender" como select.

Esta decisión es de producto, no de migración.

Lo que el script SÍ hace en este momento

Aunque NO persiste el catálogo en Mongo, el script igual lee las 227 definiciones de GHL en memoria al arrancar (ctx.custom_field_map). Eso lo necesitamos para resolver:

js
// GHL devuelve esto en cada contacto:
{ "customFields": [{ "id": "0QstDUndlPhfmKoKoD3k", "value": "Bank of America" }] }

// El script lo guarda en Mongo así:
{ "custom_fields": { "bank_name": "Bank of America" } }
//                    ↑ resuelto desde la definición

Sin la lectura de definiciones, las keys quedarían como 0QstDUndlPhfmKoKoD3k — ilegibles.

Por eso el script:

  • ✅ Sigue llamando GET /locations/{id}/customFields al arrancar
  • ✅ Sigue resolviendo id → fieldKey para los contactos y oportunidades
  • ❌ NO escribe la colección custom_field_definitions en Mongo

Cuando producto decida, agregar la persistencia es un cambio de 5 líneas (re-activar el bulk_upsert en migrate_custom_fields).

Decisiones que necesitamos

  • [ ] ¿imcrmdev va a soportar custom fields como concepto de producto? (admin UI para crearlos, formularios dinámicos)
  • [ ] Si sí, ¿el modelo de datos es por-tenant (company_id + field_key) o global?
  • [ ] ¿Mantener el concepto de folders (parentId) para agrupar campos en la UI?

Mapping propuesto (cuando se apruebe)

custom_field_definitions (NUEVA)

Origen GHLDestino
idexternal_id
namename
fieldKeyfield_key
placeholderplaceholder
dataTypedata_type (TEXT, LARGE_TEXT, NUMERICAL, DATE, MONETORY, CHECKBOX, RADIO, SINGLE_OPTIONS, MULTIPLE_OPTIONS, FILE_UPLOAD, SIGNATURE)
positionposition
documentTypedocument_type
parentIdfolder_id
modelmodel (contact, opportunity, etc.)
picklistOptions[]options[]
standardstandard
dateAddedcreated_at