Files
wsp-cumpleanos/hbdwsp-documentacion.md
T
2026-06-12 13:37:03 +00:00

414 lines
16 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.
# 🎂 Workflow: `hbdwsp` — Bot de Cumpleaños GLM (WhatsApp)
**ID del Workflow:** `ZzH2eMPrsqlY7WPz`
**Autor:** Eidan Then (`ethen@gomezleemarketing.com`)
**Última actualización:** 12 de junio de 2026
**Versión activa:** `2d27b5f7-f5c4-41f9-b008-d8f3a50557a5` (v251)
**Estado:** ✅ Activo
**Zona horaria:** America/Santo_Domingo (UTC-04:00)
---
## Descripción General
Este workflow automatiza el envío de tarjetas de cumpleaños personalizadas al grupo de WhatsApp corporativo de GLM. Cada día a las 10:00 AM, el sistema revisa los empleados registrados en BambooHR, filtra los que cumplen años ese día, genera una tarjeta visual personalizada con su foto y la envía al grupo correspondiente.
El flujo excluye automáticamente a empleados de países no hispanos (Jamaica, Trinidad & Tobago, Barbados, etc.) y maneja casos donde el empleado no tiene fecha de cumpleaños o foto registrada.
---
## Arquitectura del Flujo
```
Schedule Trigger (10am)
└── BambooHR (Reporte CSV #1509)
└── Google Drive (Guardar CSV)
└── Google Sheets (Leer datos)
└── Pais? (Filtrar hispanos)
├── [TRUE] Obtener empleado (BambooHR)
│ └── Merge (unir datos Sheets + Bamboo)
│ └── Loop Over Items
│ └── Tiene fecha?
│ ├── [FALSE] → Wait3 → Loop (siguiente)
│ └── [TRUE] → Coincide con fecha de hoy?
│ ├── [FALSE] → Wait3 → Loop
│ └── [TRUE] → Tiene foto?
│ ├── [TRUE] → Foto Empleado URL
│ └── [FALSE] → URL si no tiene foto (bizcocho)
│ └── HTML (generar tarjeta)
│ └── HTTP Request (screenshot browserless)
│ └── Extract from File
│ └── Enviar imagen WhatsApp
│ └── Wait (3s)
│ └── Loop (siguiente)
└── [FALSE] Ignorado (país no hispano)
```
---
## Nodos del Workflow
### 1. Schedule Trigger
- **Tipo:** `scheduleTrigger`
- **Configuración:** Se ejecuta todos los días a las **10:00 AM** (hora Santo Domingo, UTC-4).
- **Notas:** El trigger está pinned con fecha `2026-05-29` para pruebas manuales.
---
### 2. BambooHR (Reporte CSV)
- **Tipo:** `bambooHr``companyReport`
- **Reporte ID:** `1509`
- **Formato:** CSV
- **Credencial:** `Eidan - BambooHR account`
- **Función:** Descarga el listado completo de empleados activos con sus datos (incluyendo Birthday, Location, EEID, Work Email).
---
### 3. Google Drive (Guardar CSV)
- **Tipo:** `googleDrive``update`
- **Archivo destino:** `HBD.csv` (`1Jw9yOyIKB1QLcr9m8ObGP9PK0tC9z3n6`)
- **Credencial:** `Eidan - Google Drive account`
- **Función:** Actualiza el archivo CSV en Google Drive con los datos más recientes de BambooHR. Sirve como respaldo y fuente de verdad para Google Sheets.
---
### 4. Google Sheets (Leer datos)
- **Tipo:** `googleSheets` — read
- **Spreadsheet:** `HBD` (`1p0bCvHAIHjobiPhDfmTshZWvRP6PWAmmTtSoX77lBmM`)
- **Hoja:** `HBD` (gid: `759771456`)
- **Credencial:** `Eidan - Google Sheets account`
- **Función:** Lee los datos procesados del spreadsheet. Aquí se encuentran campos adicionales como `Location` que se usa para el filtro de país.
---
### 5. Pais? (Filtro de país)
- **Tipo:** `if`
- **Lógica:** `AND` de condiciones `notEquals`
- **Países excluidos (FALSE):**
- Jamaica
- Trinidad and Tobago / Trinidad & Tobago / Tobago
- Barbados
- Saint Thomas
- St. Croix
- **TRUE:** El empleado es de un país hispano → el flujo continúa.
- **FALSE:** El empleado es de un país no hispano → se ignora silenciosamente.
---
### 6. Obtener empleado
- **Tipo:** `bambooHr``get`
- **Employee ID:** `{{ $json.EEID }}`
- **Credencial:** `Eidan - BambooHR account`
- **Configuración:** `retryOnFail: true`
- **Función:** Obtiene el perfil completo del empleado desde BambooHR, incluyendo `photoUrl` y `photoUploaded`.
---
### 7. Merge
- **Tipo:** `merge` — combine (enrich)
- **Join por:** `workEmail``Work Email`
- **Modo:** `enrichInput1` (enriquece los datos de Bamboo con los de Google Sheets)
- **Función:** Combina en un solo objeto los datos de BambooHR (foto, cargo, etc.) con los de Google Sheets (Location, Birthday, etc.).
---
### 8. Loop Over Items
- **Tipo:** `splitInBatches`
- **Función:** Procesa cada empleado de forma individual y secuencial. Al terminar con uno, vuelve al siguiente. Cuando no quedan más ítems, el flujo termina.
---
### 9. Tiene fecha?
- **Tipo:** `if`
- **Condición:** `!$json.Birthday` es `true`
- **TRUE (sin fecha):** → `Wait3` → vuelve al loop (siguiente empleado).
- **FALSE (tiene fecha):** → continúa hacia `Coincide con fecha de hoy?`.
> ⚠️ **Nota:** La nota sticky original decía que el sistema avisaría a RRHH si falta la fecha, pero actualmente el `TRUE` solo hace un `Wait3` y continúa. Considerar agregar una notificación a RRHH.
---
### 10. Coincide con fecha de hoy?
- **Tipo:** `if`
- **Condición:** `$json.Birthday` equals `$now.format('MMMM dd')` (con espacios)
- **Validación:** `loose` (tipos flexibles)
- **`onError`:** `continueRegularOutput`
- **TRUE:** La fecha coincide → continúa el flujo.
- **FALSE:** No es el cumpleaños hoy → `Wait3` → siguiente empleado.
> ⚠️ **Importante:** El `rightValue` tiene un espacio al final: `{{ $now.format('MMMM dd') }} `. Asegurarse de que los datos de Birthday en el CSV también tengan ese formato (`MMMM dd`) para evitar falsos negativos.
---
### 11. Tiene foto?
- **Tipo:** `if`
- **Condición:** `$json.photoUploaded == true` es `true`
- **TRUE:** → `Foto Empleado URL` (usa la foto real del empleado)
- **FALSE:** → `URL si no tiene foto` (usa imagen de bizcocho)
---
### 12. Foto Empleado URL / URL si no tiene foto
- **Tipo:** `set`
- **Foto real:** `{{ $json.photoUrl }}` (URL de BambooHR)
- **Foto fallback:** `https://gomezleemarketing.com/wp-content/uploads/2026/03/hbdcake.png`
- **Output:** Campo `photoUrl` que se pasa al nodo HTML.
---
### 13. HTML (Generación de tarjeta)
- **Tipo:** `html`
- **Dimensiones:** 1080 × 1920 px (formato Instagram Stories / WhatsApp)
- **Background:** `https://gomezleemarketing.com/wp-content/uploads/2026/03/Comunicacion-de-Cumpleanos-Background.jpg`
- **Contenido dinámico:**
- Foto del empleado (circular, 530px)
- Nombre del empleado (`firstName`)
- Fecha de cumpleaños formateada en español (ej: "12 de junio")
- **Nota:** Los divs `.hello`, `.message`, `.position` y `.team` están vacíos en la versión actual. Pueden ser rellenados con texto personalizado.
---
### 14. HTTP Request (Screenshot)
- **Tipo:** `httpRequest` POST
- **URL:** `https://pruebadetemplate-browserless.k11ytw.easypanel.host/chromium/screenshot`
- **Token:** `8d66338953f117a7c29c55364c8c9e6d`
- **Viewport:** `1080 × 1920`
- **Response format:** `file` (binario)
- **Función:** Convierte el HTML generado en una imagen PNG usando Browserless (Chromium headless) alojado en Easypanel.
---
### 15. Extract from File
- **Tipo:** `extractFromFile``binaryToPropery`
- **Función:** Convierte el binario de la imagen en base64 (`data`) para enviarlo a la API de WhatsApp.
---
### 16. Enviar mensaje al whatsapp imagen
- **Tipo:** `httpRequest` POST
- **URL:** `https://wsp.gomezleemarketing.com/message/sendMedia/botsoporte`
- **API Key:** `C267126ABB45-4C12-B626-6BAB1833F5D7`
- **Número destino:** `120363404268176806@g.us` (Grupo oficial Motorola)
- **Tipo de media:** `image`
- **Media:** `{{ $json.data }}` (base64)
- **Delay:** 2000ms
---
### 17. Wait (entre empleados)
- **Tipo:** `wait`
- **Duración:** 3 segundos
- **Función:** Pausa entre el envío y la vuelta al loop para no saturar la API de WhatsApp.
### Wait3 (skip al siguiente)
- **Tipo:** `wait`
- **Duración:** 1 segundo
- **Función:** Pequeña pausa antes de saltar al siguiente empleado cuando no cumple años o no tiene fecha.
---
## Subworkflow: Respaldo en Gitea
Este subworkflow es independiente (trigger manual) y guarda/actualiza el JSON del workflow en Gitea como respaldo de versión.
### Nodos:
| Nodo | Función |
|------|---------|
| `Valores` | Define `repo_name: wsp-cumpleanos` y `file_name: hbd-panama-motorola` |
| `Obtener archivo` | GET al repo Gitea para obtener el SHA actual del archivo |
| `Get a workflow1/2` | Obtiene el JSON del propio workflow (`ZzH2eMPrsqlY7WPz`) vía API de n8n |
| `Code in JavaScript` | Prepara payload con SHA para **actualizar** (PUT) |
| `Code in JavaScript1` | Prepara payload sin SHA para **crear** (POST) |
| `Actualizar archivo` | PUT al endpoint de Gitea (actualiza si ya existe) |
| `Subir archivo` | POST al endpoint de Gitea (crea si no existe) |
**Repositorio:** `https://git.digitalcompass.agency/EidanThen/wsp-cumpleanos`
---
## Requisitos de Instalación
### Servicios externos requeridos
| Servicio | Uso | Credencial |
|----------|-----|------------|
| **BambooHR** | Fuente de datos de empleados | `Eidan - BambooHR account` |
| **Google Drive** | Almacenamiento del CSV actualizado | `Eidan - Google Drive account` |
| **Google Sheets** | Lectura de datos procesados | `Eidan - Google Sheets account` |
| **Browserless** (Easypanel) | Renderizado HTML → PNG | Token en URL |
| **EvolutionAPI** (WhatsApp) | Envío de imágenes al grupo | API Key en header |
| **Gitea** (opcional) | Respaldo del workflow JSON | `Eidan - gitea` |
| **n8n API** (opcional) | Leer JSON del workflow para backup | `Eidan - n8n account` |
---
### Paso 1: Configurar credenciales en n8n
1. Ve a **Settings → Credentials** en n8n.
2. Crea las siguientes credenciales:
#### BambooHR
- **Tipo:** BambooHR API
- **API Key:** (tu API key de BambooHR)
- **Subdomain:** `gomezleemarketing`
#### Google Drive / Sheets
- **Tipo:** Google OAuth2
- Autoriza con la cuenta `ethen@gomezleemarketing.com`
- Aplica a ambos nodos (Drive y Sheets)
#### Gitea (Header Auth)
- **Tipo:** Header Auth
- **Name:** `Authorization`
- **Value:** `token TU_TOKEN_GITEA`
---
### Paso 2: Configurar Google Sheets
El spreadsheet `HBD` debe tener al menos las columnas:
| Columna | Descripción |
|---------|-------------|
| `EEID` | ID del empleado en BambooHR |
| `Work Email` | Email corporativo (clave de join con Bamboo) |
| `Birthday` | Fecha en formato `MMMM dd` (ej: `June 12`) |
| `Location` | País del empleado (ej: `Dominican Republic`) |
| `firstName` | Nombre del empleado |
---
### Paso 3: Configurar Browserless
El workflow usa una instancia de Browserless alojada en Easypanel:
```
URL: https://pruebadetemplate-browserless.k11ytw.easypanel.host
Token: 8d66338953f117a7c29c55364c8c9e6d
```
Si se despliega una nueva instancia, actualizar la URL y el token en el nodo **HTTP Request** (screenshot).
**Para desplegar Browserless en Docker:**
```yaml
services:
browserless:
image: ghcr.io/browserless/chromium:latest
ports:
- "3000:3000"
environment:
- TOKEN=tu_token_aqui
```
---
### Paso 4: Configurar EvolutionAPI (WhatsApp)
El workflow usa EvolutionAPI como intermediario para WhatsApp:
- **Base URL:** `https://wsp.gomezleemarketing.com`
- **Instancia:** `botsoporte`
- **API Key:** configurada en el header `apikey`
Asegúrate de que la instancia `botsoporte` esté conectada y activa antes de ejecutar el workflow.
---
### Paso 5: Importar el workflow en n8n
1. En n8n, ve a **Workflows → Import from file/URL**.
2. Pega el JSON del workflow o importa desde Gitea.
3. Asigna las credenciales correctas a cada nodo.
4. Activa el workflow con el toggle.
---
## Configuración del Grupo de WhatsApp
| Entorno | Número del grupo |
|---------|-----------------|
| **Producción (Motorola)** | `120363404268176806@g.us` |
| **Pruebas** | `120363426353723539@g.us` |
Para cambiar el grupo destino, editar el campo `number` en el nodo **Enviar mensaje al whatsapp imagen**.
---
## Personalización de la Tarjeta HTML
La tarjeta visual se genera en el nodo `HTML`. Los siguientes divs están disponibles para personalización:
```html
<div class="hello">¡Feliz Cumpleaños!</div>
<!-- Saludo principal. Actualmente vacío. -->
<div class="message">Tu mensaje aquí...</div>
<!-- Mensaje de felicitación. Actualmente vacío. -->
<div class="position">{{ cargo del empleado }}</div>
<!-- Cargo/posición. Actualmente vacío. -->
<div class="team">Equipo GLM</div>
<!-- Firma del equipo. Actualmente vacío. -->
```
Para agregar el cargo dinámico, incluir en el nodo `Obtener empleado` el campo `jobTitle` y referenciarlo con `{{ $json.jobTitle }}`.
---
## Solución de Problemas
### El workflow no envía tarjetas aunque es el cumpleaños del empleado
**Causa probable:** El formato de la fecha en Google Sheets no coincide con `$now.format('MMMM dd')`.
**Solución:** Asegúrate de que el campo `Birthday` en el CSV/Sheets tenga el formato `June 12` (no `06/12` ni `12-06-1990`). El nodo `Coincide con fecha de hoy?` también tiene un espacio trailing en el `rightValue`; revisar que no cause falsos negativos.
---
### La imagen no se genera (error en Browserless)
**Causa probable:** La instancia de Browserless está caída o el token expiró.
**Solución:**
1. Verificar que el contenedor de Browserless esté corriendo en Easypanel.
2. Probar manualmente con curl:
```bash
curl -X POST "https://pruebadetemplate-browserless.k11ytw.easypanel.host/chromium/screenshot?token=8d66338953f117a7c29c55364c8c9e6d" \
-H "Content-Type: application/json" \
-d '{"html": "<h1>Test</h1>", "viewport": {"width": 1080, "height": 1920}}'
```
---
### El mensaje no llega al grupo de WhatsApp
**Causa probable:** La instancia `botsoporte` de EvolutionAPI no está conectada.
**Solución:**
1. Ir al panel de EvolutionAPI en `https://wsp.gomezleemarketing.com`.
2. Verificar que la instancia `botsoporte` muestra estado `open`.
3. Si está desconectada, escanear el QR de WhatsApp nuevamente.
---
### Empleados de países no hispanos reciben tarjetas
**Causa probable:** El campo `Location` en Google Sheets tiene un valor diferente al esperado (ej: `Trinidad` en vez de `Trinidad and Tobago`).
**Solución:** Revisar el nodo `Pais?` y agregar las variantes del nombre del país en las condiciones `notEquals`.
---
## Flujo de Error
El workflow tiene configurado el workflow de error `lX5ITf9s4TSQglsM`. Ante un error crítico no manejado, se disparará ese workflow de manejo de errores.
---
## Notas de Mantenimiento
- El reporte de BambooHR (#1509) debe incluir los campos `Birthday`, `Location`, `EEID`, y `Work Email`.
- La imagen de fondo y el fallback de bizcocho están alojadas en el servidor de WordPress de GLM. Si cambian de dominio, actualizar las URLs en el nodo `HTML` y en el nodo `URL si no tiene foto`.
- El workflow respalda su propio JSON en Gitea bajo `EidanThen/wsp-cumpleanos/hbd-panama-motorola.json`. Ejecutar el subworkflow manual periódicamente o tras cambios importantes.