Files
calendario-glm-way/README.md
T
2026-08-27 19:56:51 +00:00

327 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# #TheGLMWay — Workflow de Automatización Onboarding (n8n)
> **Sistema automatizado que detecta nuevos ingresos en BambooHR, los inscribe en la sesión de GLM Way correspondiente a su idioma, gestiona toda la comunicación pre-sesión y recolecta la evaluación post-sesión.**
>
> **ID Workflow:** `F1iPg8hktNAebZua` · **Nombre:** `GLMWay` · **Timezone:** `America/Santo_Domingo` · **Estado:** Activo
---
## 1. Propósito
Centralizar y automatizar el ciclo de vida completo de la capacitación **#TheGLMWay** para nuevos colaboradores:
1. Detectar el ingreso (hireDate) en **BambooHR** el día 1.
2. Asignar al colaborador a la **sesión más cercana** según su idioma (país → ES/EN).
3. Notificarle por email con fecha, hora localizada y enlace de Meet.
4. Avisar al **coordinador RRHH** del país y al **facilitador** 48h / 24h antes.
5. Enviar **recordatorio** al colaborador 24h antes.
6. Verificar **asistencia real** vía Google Meet API y enviar **evaluación** (Google Forms) post-sesión.
7. Medir métricas de evaluaciones enviadas/respondidas.
Todo persiste en **Supabase** (`inscritos_glmway`, `sesiones_glmway`, `coordinadores_rrhh_glmway`) y se sincroniza con **Google Calendar / Sheets / Chat**.
---
## 2. Arquitectura General
```
BambooHR (Webhook + Polling) ─┬─► Supabase (inscritos_glmway)
Google Calendar (ethen@) ─────┼─► Supabase (sesiones_glmway)
Google Sheets (Calendario) ───┼─► Supabase (coordinadores_rrhh_glmway)
├─► Gmail (notificación + recordatorio + evaluación)
├─► Google Chat (facilitador + coordinadores RRHH)
└─► WhatsApp Evolution API (alerta sin coordinador)
Meet API ─────────────────────► Asistencia ─► Evaluación
Gitea (git.digitalcompass.agency) ─► Backup JSON del workflow
```
**Credenciales utilizadas:**
| Servicio | Credencial n8n | Uso |
|---|---|---|
| BambooHR | `Eidan - BambooHR account` + `GLM Credential` (Basic Auth) | `Get an employee`, `HTTP /employees/{id}` |
| Supabase | `Sofia test - Supabase account` | Tablas `inscritos_glmway`, `sesiones_glmway`, `coordinadores_rrhh_glmway`, RPCs |
| Gmail | `Eidan - Gmail account` (OAuth2) | 10 nodos `Send a message` |
| Google Calendar | `Eidan - Google Calendar account` | Lectura `ethen@gomezleemarketing.com` query `#TheGLMWay` |
| Google Sheets | `Eidan - Google Sheets account` | Sheet `1Xs-MkeXiZvWeJuwm-IblcI-ilr7lyhMtED0R7wczmRg` |
| Google Chat | `Eidan - Chat account` (OAuth) | Spaces `AAQAgX1mjaY` (GLMWay), `AAQAQj1t9AA` (RRHH) |
| Google Contacts | `Eidan - Google Contacts account` | Resolución `contactId` para menciones `<users/...>` en Chat |
| Google Meet | `Eidan - Google account` (OAuth2) | `meet.googleapis.com/v2/conferenceRecords` |
| Gitea | `Eidan - gitea` (headerAuth) | Backup `mgomez/calendario-glm-way:glmway-workflow.json` |
| n8n API | `Eidan - n8n account` | `Get a workflow` para backup |
| WhatsApp | Evolution API `wsp.gomezleemarketing.com` `botsoporte` | Alerta a `18494105855@s.whatsapp.net` |
---
## 3. Modelo de Datos (Supabase)
### `inscritos_glmway`
| Campo | Tipo | Descripción |
|---|---|---|
| `empleado_id` | text PK | ID BambooHR |
| `nombre` | text | displayName |
| `email` | text | workEmail |
| `departamento` | text | department |
| `division` | text | division |
| `puesto` | text | jobTitle |
| `pais` | text | location (BambooHR) |
| `fecha_ingreso` | date | hireDate `YYYY-MM-DD` |
| `fecha_sesion_inscrito` | text | `"14 January"` (sesión asignada) |
| `sesion_id` | text | FK `sesiones_glmway.id` |
| `estado` | text | `inactivo``notificado``recordado``evaluacion_enviada` |
| `coordinador_rrhh` | text | nombre coordinador del país |
| `instructor` | text | actualizado post-sesión |
| `asistio_evento_glmway` | bool | asistencia detectada Meet |
| `fecha_sesion_inscrito` | text | reutilizado para cálculo 24h |
**Estados:** `inactivo` (registrado sin email), `notificado` (email inicial enviado), `recordado` (recordatorio 24h), `evaluacion_enviada` (evaluación enviada).
### `sesiones_glmway`
| Campo | Descripción |
|---|---|
| `id` / `meet_id` | ID evento Calendar / Meet |
| `titulo` | `"Entrenamiento #TheGLMWay"` o `"#TheGLMWay Training"` |
| `fecha_sesion` | `"14 January"` |
| `hora_inicio` / `hora_fin` | `HH:mm` (hora Santo Domingo) |
| `instructor` | Nombre del facilitador |
| `correo_instructor` | Email |
| `enlace_reunion` / `url` | Meet link |
| `idioma` | `ES` / `EN` |
| `participantes` | JSONB `{"RD": [{nombre, correo, departamento, division}], "Mexico": [...]}` |
RPCs: `insertar_participantes_sesion_glmway(p_sesion_id, p_pais, p_empleados)`, `sync_sesiones_glmway`, `insertar_coordinadores_rrhh_glmway`, `incrementar_evaluaciones_enviadas_glmway`, `incrementar_evaluaciones_resueltas_glmway`.
### `coordinadores_rrhh_glmway`
| Campo | Descripción |
|---|---|
| `pais` | País asignado |
| `nombre` | Nombre completo |
| `correo` | Email |
| `estado` | `no_notificado` (filtro) |
---
## 4. Lógica de Idioma por País
```js
// ES = país NO está en lista Caribe anglófono
// EN = país SÍ está en lista
["Jamaica","Trinidad and Tobago","Barbados","Saint Thomas","St. Croix","Tobago","Guyana","Aruba"]
```
Todos los nodos `Pais?`, `Pais?1`, `Pais?2`, `Pais?3`, `Pais?4` usan esta lista. Si falta coordinador para el país, se dispara alerta WhatsApp a Matos (`18494105855`).
Conversión de hora: todos los `Cambiar zona horaria*` (6 nodos) toman `hora_inicio` en `America/Santo_Domingo` como base y la convierten con `Intl.DateTimeFormat` al timezone del país del colaborador (mapa `TIMEZONES` con 25 países).
---
## 5. Flujos Detallados (10 sub-workflows en 1 workflow)
### Flujo A — Notificación Inicial (Webhook BambooHR)
**Trigger:** `Webhook POST /glmway` (BambooHR webhook nuevos ingresos)
```
Webhook → Get an employee → Info extra (hireDate, supervisorEmail, jobTitle)
→ Calcular fecha hireDate (isValid = hireDate <= today)
→ Tiene correo y pais? ─┬─ NO → Insertar inscritos (inactivo)
└─ SI → Entra hoy? ─┬─ NO → Insertar inscritos (inactivo)
└─ SI → Obtener coordinador3
→ Hay coordinador? ─ NO → Alerta WhatsApp + Pais?
SI → Pais? → [ES/EN]
→ Crear variable idioma
→ Obtener sesiones (filtro idioma)
→ Calcular sesion mas cercana
→ Guardar datos empleado+sesion (estado=notificado)
→ Convertir fecha a es/en
→ Cambiar zona horaria
→ Send Gmail (ES/EN)
→ Registrar empleado a sesión (RPC)
```
*Templates Gmail:* `Entrenamiento #TheGLMWay` / `#TheGLMWay Training` con `fechaFormateada`, `hora_convertida`, `enlace_reunion`.
### Flujo B — Reintento Inactivos (Polling Horario)
**Trigger:** `Schedule Trigger1` — cada hora
```
Obtener inactivos (estado=inactivo) → Loop Over Items (SplitInBatches)
→ Get an employee1 → Info extra 1 → Calcular fecha hireDate1 → Tiene correo y pais?1
→ Entra hoy?1 ─ SI → Obtener coordinador4 → Hay coordinador?1 → Pais?1 → (mismo camino ES/EN)
→ Actualizar datos del empleado y sesion asignada → ... → Send Gmail 1
→ NO → Wait2 2s → Loop
```
### Flujo C — Recordatorio 24h al Colaborador
**Trigger:** `Schedule Trigger2` — martes y miércoles 17:00
```
Obtener notificados (estado=notificado) → Loop Over Items1
→ Calcular falta 24h o es el mismo dia (parsea "14 December", diferenciaDias 0-1)
→ Falta 24h para el evento? ─ SI → Obtener sesion → Pais?2 → Convertir fecha 2
→ Cambiar zona horaria 4/5 → Send Gmail recordatorio
→ Actualizar estado a recordado → Wait1 2s → Loop
─ NO → Wait1 → Loop
```
*Asunto recordatorio ES:* `Entrenamiento #TheGLMWay`*Texto:* “Te recordamos que la sesión será el {{fecha}} a las {{hora}}...”
### Flujo D — Notificación a Facilitador (48h / día del evento)
**Trigger:** `Schedule Trigger` — miércoles 16:00
```
Obtener sesion1 (todas) → Calcular sesion mas cercana4 (match hoy)
→ Obtener miembros (Chat Space AAQAgX1mjaY) → Limpiar id → Get a contact → Unir datos2
→ Hay inscritos? ─ SI → Es espanol o ingles1 (switch por idioma sesión)
→ Formatear datos ingles/espanol (card con totalParticipantes, enlace)
→ Enviar notificación a glmway notificaciones
─ NO → Es espanol o ingles (switch)
→ Formatear datos ingles1/espanol1 (card “No hay participantes”)
→ Enviar notificación
```
*Si hay inscritos:* `🔔 *Recordatorio de #TheGLMWay para hoy* \n Instructor: <users/...>` con `totalParticipantes` y `Entrar a reunión`. *Si no:* `🔔 *Aviso de sesión sin participantes*`.
### Flujo E — Notificación a Coordinador RRHH (48h + 24h)
**Trigger:** `Schedule Trigger3` — martes 16:00 + miércoles 16:00
```
Obtener sesiones (all) → Calcular falta 48h o 24h1 (sesión más cercana futura)
→ Hay inscritos en la sesion? ─ SI → Obtener coordinadores1 (estado=no_notificado filtro)
→ Matchear coordinador y pais inscrito (normaliza acentos)
→ Obtener miembros RRHH (AAQAQj1t9AA) → Limpiar id1 → Get a contact1
→ Unir datos3 (match nombre coordinador ↔ Google Contact)
→ Pais?3 → Formatear datos ingles2/espanol2
→ Enviar notificación a rrhh notificaciones
```
*Card RRHH:* `🔔 *Aviso para Coordinadores RRHH*` con `Total inscritos: X` + listado por país `📍 RD (2): • Nombre (Depto | División)`.
### Flujo F — Post-Sesión: Asistencia + Evaluación
**Trigger:** `Schedule Trigger7` — miércoles 19:50 (post-sesión)
```
Hora de la reunion (timestamp → startTime T22:50:24Z, endTime T24:00:00Z, fechaCorta "11 August")
→ Obtener conferenceRecord (Meet API v2) → Buscar sesion con hora UTC (filtra por fecha, distanciaMs mínima)
→ Obtener participantes (conferenceRecords/{name}/participants)
→ Detectar participantes en la sesion (extrae signedinUser.displayName)
→ Obtener sesion2 (por fecha_sesion) ─┬─→ Buscar participantes (match normalizado displayName ↔ nombre registrado)
│ └─→ Actualizar instructor de inscritos
→ Split Out (participantes) → Actualizar asistencia (supabase asistio_evento_glmway bool, wfts por nombre)
→ Asistio? (solo si asistio=false se envía evaluación — filtra asistentes? verificar lógica)
→ Cortar nombre (primerNombre) → Pais?4 → Send Gmail evaluación (HTML con branding GLM + botón Google Forms)
→ Actualizar estado a evaluacion_enviada → Incrementar conteo evaluaciones enviadas (RPC)
```
*Evaluación ES:* `Evaluación #TheGLMWay``https://docs.google.com/forms/d/e/1FAIpQLSf8QUfmXulvw4e9BwZjTFG4mEUWybdFSAes_dK-OFnWN-L43Q/viewform` con header `#2d3748` + logo GLM. *EN:* `#TheGLMWay Quiz``...Sf61KpkDr...`.
### Flujo G — Webhook Evaluación Respondida
**Trigger:** `Webhook1 POST /evaluacion-glmway`
```
Webhook1 → Incrementar conteo de evaluaciones respondidas (RPC incrementar_evaluaciones_resueltas_glmway)
```
### Flujo H — Sincronización Anual Calendario → Sheets
**Trigger:** `Schedule Trigger4``0 0 0 1 1 *` (1 de enero)
```
Obtener entrenamientos ingles y espanol (Calendar año completo query #TheGLMWay)
→ Formatear fecha (DD Month) → Borrar fechas (Sheets rango A2:D80) → Wait4 → Insertar fechas (appendOrUpdate por Fecha)
```
### Flujo I — Sincronización Diaria Coordinadores
**Trigger:** `Schedule Trigger5` — diario 11:00
```
Obtener coordinadores (Sheets “Coordinadores RRHH”) → Aggregate (input) → Insertar/actualizar coordinadores a la bd (RPC)
```
### Flujo J — Sincronización Diaria Sesiones
**Trigger:** `Schedule Trigger6` — miércoles 15:00
```
Obtener fechas e instructores (Sheets Calendario)
+ Obtener entrenamientos ingles y espanol1 (Calendar)
→ Formatear fecha y detectar idioma (detecta "#TheGLMWay Training"→EN, "Entrenamiento #TheGLMWay"→ES, extrae hora_inicio/fin, url)
→ Unir datos (merge por Fecha) → Edit Fields (meet_id, titulo, fecha_sesion, hora_inicio/fin, instructor, correo_instructor, enlace_reunion, idioma)
→ Aggregate1 → Insertar fechas1 (RPC sync_sesiones_glmway)
```
### Flujo K — Backup Automático del Workflow
**Trigger:** `When clicking Execute workflow` (manual)
```
Valores (mgomez / calendario-glm-way / glmway-workflow)
→ Obtener archivo (Gitea GET contents/*.json) ─┬─ éxito (onError continue) → Get a workflow1 → Code JS2 (base64 JSON + sha) → Actualizar archivo (PUT)
└─ error (no existe) → Get a workflow2 → Code JS4 (base64) → Subir archivo (POST)
```
---
## 6. Diagrama de Estados del Inscrito
```
[BambooHR hireDate futuro] ──► inactivo (insertado sin email/sin fecha)
▼ (hireDate == today, con coordinador)
notificado ──► (24h antes) ──► recordado
▼ (post-sesión Meet)
evaluacion_enviada ──► (webhook) conteo respondida++
```
---
## 7. Notificaciones
| Destinatario | Canal | Cuándo | Formato |
|---|---|---|---|
| Nuevo ingreso | Gmail | Día 1 (Flujo A/B) | Texto plano ES/EN con fecha, hora local, Meet link |
| Nuevo ingreso | Gmail | 24h antes (Flujo C) | Recordatorio ES/EN |
| Facilitador | Google Chat `AAQAgX1mjaY` | Miércoles 16:00 (Flujo D) | CardV2 con mención `<users/...>` + total participantes |
| Coordinador RRHH | Google Chat `AAQAQj1t9AA` | Mar/Mié 16:00 (Flujo E) | CardV2 por país con listado detallado |
| Coordinador RRHH | WhatsApp | Sin coordinador (Flujo A/B) | `⚠️ No hay coordinador RRHH en el país...` |
| Inscrito | Gmail | Post-sesión 19:50 (Flujo F) | HTML evaluación con botón a Google Forms |
---
## 8. Instalación y Configuración
### Requisitos
- n8n ≥ 1.x con timezone `America/Santo_Domingo`
- Credenciales: BambooHR API Key, Supabase service_role, Google OAuth (Calendar, Sheets, Gmail, Meet, Chat, Contacts), Gitea token, Evolution WhatsApp API key
- Supabase: crear tablas + RPCs (`insertar_participantes_sesion_glmway`, `sync_sesiones_glmway`, etc.)
- Google Sheet `Calendario #TheGLMWay` con hojas `Calendario` (col Fecha) y `Coordinadores RRHH`
### Importar
1. n8n → `Import from File``GLMWay.json`
2. Reasignar credenciales (IDs en JSON son de instancia origen `Eidan`)
3. Activar workflow
4. Configurar webhook BambooHR → `https://<n8n>/webhook/glmway` (POST)
5. Verificar `Schedule Triggers` — respetar horario Santo Domingo
### Variables Clave a Revisar
- `WEBHOOK_URL` base, `wsp.gomezleemarketing.com` apikey, `git.digitalcompass.agency` repo, `ethen@gomezleemarketing.com` calendar, Chat Space keys/tokens (rotar — están hardcodeados en nodos HTTP), Supabase URL `dbit.digitalcompass.agency`.
---
## 9. Operación y Monitoreo
- **Error Workflow:** `lX5ITf9s4TSQglsM` (configurado en `settings.errorWorkflow`) — revisar ejecuciones fallidas.
- **Reintentos:** `Informacion extra del empleado` con retry 2s, otros con `retryOnFail: true`.
- **Loops:** `Loop Over Items` / `Loop Over Items1` con `Wait` 2s para evitar rate-limit BambooHR/Gmail.
- **Logs:** `console.log` en nodos Code (`Sesiones procesadas`, eventos ignorados).
- **PinData:** Triggers con datos de prueba agosto 2026 — remover en prod.
---
## 10. Riesgos y Recomendaciones
| Riesgo | Mitigación sugerida |
|---|---|
| Tokens Chat hardcodeados (`key=AIza...&token=...`) en URLs | Migrar a credencial `googleChatOAuth2Api` o variable de entorno, rotar tokens |
| `service_role` JWT expuesto en nodos HTTP | Mover a credencial Header Auth, no en claro |
| `TIMEZONES` incompleto (faltan países GLM) | Centralizar en tabla `paises_timezones` |
| Parseo fecha `"14 January"` frágil (locale/año) | Usar `date-fns` con año explícito o ISO en `sesiones_glmway` |
| Duplicados por webhook + polling horario | Añadir constraint único `empleado_id` + upsert |
| `Wait` fijo 2s puede exceder límites Gmail (100/día) | Batch con `SplitInBatches` size controlado |
---
## 11. Archivos Relacionados
- `GLMWay.json` — definición completa del workflow (importable en n8n)
- `README_GLMWay.md` — este documento
---
*Documentado a partir de `GLMWay.json` v`f657df3d-e5b6-445e-ad75-df66da81ebf3` — última edición inspeccionada 27/08/2026.*
*GomezLee Marketing — Automatización #TheGLMWay.*