468 lines
20 KiB
JSON
468 lines
20 KiB
JSON
{
|
|
"name": "Seguimiento de Impuestos GLM - Recordatorios",
|
|
"nodes": [
|
|
{
|
|
"parameters": {
|
|
"rule": {
|
|
"interval": [
|
|
{
|
|
"field": "cronExpression",
|
|
"expression": "0 8 * * *"
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"type": "n8n-nodes-base.scheduleTrigger",
|
|
"typeVersion": 1.2,
|
|
"position": [
|
|
-1180,
|
|
-120
|
|
],
|
|
"id": "7beab0d6-ad06-4cb3-bcbe-e362296ed74e",
|
|
"name": "Diario 8:00 AM"
|
|
},
|
|
{
|
|
"parameters": {},
|
|
"type": "n8n-nodes-base.manualTrigger",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
-1180,
|
|
40
|
|
],
|
|
"id": "2d00fd7e-10ad-4b79-9b53-25d998e6dda0",
|
|
"name": "Ejecutar manualmente"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"httpMethod": "POST",
|
|
"path": "seguimiento-impuestos",
|
|
"responseMode": "onReceived",
|
|
"options": {}
|
|
},
|
|
"type": "n8n-nodes-base.webhook",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
-1180,
|
|
240
|
|
],
|
|
"id": "48598cee-8800-4d4f-9bb4-5b10cb93b9f4",
|
|
"name": "Webhook seguimiento-impuestos",
|
|
"webhookId": "seguimiento-impuestos-glm"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "function dateInTimeZone(timeZone) {\n const parts = new Intl.DateTimeFormat('en-US', {\n timeZone,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n }).formatToParts(new Date());\n const values = Object.fromEntries(parts.map((part) => [part.type, part.value]));\n return `${values.year}-${values.month}-${values.day}`;\n}\n\nconst input = $json ?? {};\nconst runDate = input.run_date || dateInTimeZone('America/Santo_Domingo');\n\nreturn [{\n json: {\n run_date: runDate,\n source: input.source || 'schedule',\n },\n}];"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-940,
|
|
-40
|
|
],
|
|
"id": "386d5276-17fa-410e-9750-887ebab51e67",
|
|
"name": "Preparar ejecución"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const body = $json.body ?? $json;\nconst action = String(body.action ?? '').trim().toLowerCase();\n\nif (!['run_now', 'run_reminders', 'ejecutar_recordatorios'].includes(action)) {\n return [];\n}\n\nreturn [{\n json: {\n run_date: body.run_date || body.date || '',\n source: 'webhook',\n },\n}];"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-940,
|
|
240
|
|
],
|
|
"id": "d1c460af-3103-4a11-ae9f-508525b34f6e",
|
|
"name": "Validar ejecución por Webhook"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "={{ ($env.SUPABASE_URL || 'https://dbit.digitalcompass.agency').replace(/\\/$/, '') + '/rest/v1/rpc/tax_due_reminders' }}",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "={{ $env.SUPABASE_SERVICE_ROLE_KEY }}"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "={{ 'Bearer ' + $env.SUPABASE_SERVICE_ROLE_KEY }}"
|
|
},
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ { p_run_date: $json.run_date } }}",
|
|
"options": {}
|
|
},
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.4,
|
|
"position": [
|
|
-680,
|
|
40
|
|
],
|
|
"id": "d8682d72-3681-43f1-83aa-521b54572a95",
|
|
"name": "Supabase - Recordatorios pendientes"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "const htmlEscape = (value) => String(value ?? '')\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n\nconst rows = [];\nfor (const item of $input.all()) {\n const value = item.json;\n if (Array.isArray(value)) rows.push(...value);\n else if (Array.isArray(value?.data)) rows.push(...value.data);\n else if (Array.isArray(value?.body)) rows.push(...value.body);\n else if (value?.obligation_id) rows.push(value);\n}\n\nconst logoUrl = $env.GLM_LOGO_URL || 'https://dbit.digitalcompass.agency/storage/v1/object/public/public-assets/GLM.png';\nconst appUrl = $env.GLM_TAX_APP_URL || 'https://digitalcompass.agency/calendario-impuestos/';\n\nconst formatDate = (iso) => {\n const [year, month, day] = String(iso).split('-').map(Number);\n return new Intl.DateTimeFormat('es-DO', {\n timeZone: 'UTC',\n day: '2-digit',\n month: 'long',\n year: 'numeric',\n }).format(new Date(Date.UTC(year, month - 1, day)));\n};\n\nconst normalizeWhatsApp = (value) => {\n let raw = String(value ?? '').trim();\n if (!raw) return '';\n raw = raw.replace('@c.us', '@s.whatsapp.net');\n if (raw.includes('@g.us')) return raw;\n if (raw.includes('@s.whatsapp.net')) {\n const number = raw.replace('@s.whatsapp.net', '').replace(/\\D/g, '');\n return number.startsWith('120363') ? `${number}@g.us` : `${number}@s.whatsapp.net`;\n }\n const number = raw.replace(/\\D/g, '');\n return number.startsWith('120363') ? `${number}@g.us` : `${number}@s.whatsapp.net`;\n};\n\nreturn rows.map((row) => {\n const dueDate = formatDate(row.due_date);\n const isSameDay = row.alert_type === 'same_day';\n const reminderLabel = isSameDay ? 'Vence hoy' : 'Recordatorio previo';\n const categoryLabel = row.category === 'nomina' ? 'Nómina' : 'Administración';\n const firstName = String(row.contact_name ?? '').trim().split(/\\s+/)[0] || 'equipo';\n const subject = `${isSameDay ? 'Vence hoy' : 'Recordatorio'} · ${row.country_name} · ${row.description}`;\n\n const emailHtml = `\n<!doctype html>\n<html lang=\"es\">\n <body style=\"margin:0;padding:0;background:#F5F5F5;font-family:Arial,sans-serif;color:#4A4A4A;\">\n <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"background:#F5F5F5;padding:28px 12px;\">\n <tr>\n <td align=\"center\">\n <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"max-width:640px;background:#FFFFFF;border:1px solid #D0D0D0;border-radius:14px;overflow:hidden;\">\n <tr>\n <td style=\"padding:26px 30px 18px;\">\n <img src=\"${htmlEscape(logoUrl)}\" alt=\"GomezLee Marketing\" width=\"150\" style=\"display:block;width:150px;max-width:100%;height:auto;border:0;\">\n </td>\n </tr>\n <tr><td style=\"height:5px;background:#6CC24A;font-size:0;line-height:0;\"> </td></tr>\n <tr>\n <td style=\"padding:30px;\">\n <div style=\"font-size:12px;font-weight:700;letter-spacing:1.4px;text-transform:uppercase;color:#6CC24A;\">${htmlEscape(reminderLabel)}</div>\n <h1 style=\"margin:8px 0 10px;font-size:27px;line-height:1.18;color:#4F758B;\">Seguimiento de Impuestos</h1>\n <p style=\"margin:0 0 24px;font-size:15px;line-height:1.6;\">Hola ${htmlEscape(firstName)}, este es el recordatorio correspondiente a la siguiente obligación tributaria:</p>\n\n <table role=\"presentation\" width=\"100%\" cellspacing=\"0\" cellpadding=\"0\" style=\"border-collapse:collapse;border:1px solid #D0D0D0;\">\n <tr>\n <td style=\"width:34%;padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B;\">País</td>\n <td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:13px;\">${htmlEscape(row.country_name)}</td>\n </tr>\n <tr>\n <td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B;\">Categoría</td>\n <td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:13px;\">${htmlEscape(categoryLabel)}</td>\n </tr>\n <tr>\n <td style=\"padding:12px 14px;background:#EEF6E8;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#4F758B;\">Fecha límite</td>\n <td style=\"padding:12px 14px;border-bottom:1px solid #D0D0D0;font-size:13px;font-weight:700;color:#FF6A13;\">${htmlEscape(dueDate)}</td>\n </tr>\n <tr>\n <td style=\"padding:12px 14px;background:#EEF6E8;font-size:13px;font-weight:700;color:#4F758B;\">Obligación</td>\n <td style=\"padding:12px 14px;font-size:13px;line-height:1.5;\">${htmlEscape(row.description)}</td>\n </tr>\n </table>\n\n <p style=\"margin:22px 0 0;font-size:12px;line-height:1.55;color:#6B8FA3;\">Regla de seguimiento: miércoles anterior y fecha límite, incluidos sábados y domingos.</p>\n <div style=\"margin-top:24px;\">\n <a href=\"${htmlEscape(appUrl)}\" style=\"display:inline-block;background:#6CC24A;color:#FFFFFF;text-decoration:none;font-size:13px;font-weight:700;padding:11px 17px;border-radius:8px;\">Abrir calendario</a>\n </div>\n </td>\n </tr>\n <tr>\n <td style=\"padding:16px 30px;border-top:1px solid #D0D0D0;font-size:11px;color:#6B8FA3;\">\n GOMEZLEE MARKETING | Seguimiento de Impuestos\n </td>\n </tr>\n </table>\n </td>\n </tr>\n </table>\n </body>\n</html>`.trim();\n\n const whatsappText = [\n `*${reminderLabel.toUpperCase()} · SEGUIMIENTO DE IMPUESTOS*`,\n '',\n `Hola ${firstName},`,\n '',\n `*País:* ${row.country_name}`,\n `*Categoría:* ${categoryLabel}`,\n `*Fecha límite:* ${dueDate}`,\n `*Obligación:* ${row.description}`,\n '',\n 'Regla: miércoles anterior y fecha límite, incluidos sábados y domingos.',\n '',\n `Calendario: ${appUrl}`,\n ].join('\\n');\n\n return {\n json: {\n ...row,\n email_subject: subject,\n email_html: emailHtml,\n whatsapp_to: normalizeWhatsApp(row.contact_whatsapp),\n whatsapp_text: whatsappText,\n },\n };\n});"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-420,
|
|
40
|
|
],
|
|
"id": "2c3419a7-55f0-4a5d-ac1d-cc22f5c18143",
|
|
"name": "Construir mensajes GLM"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "return $input.all().filter((item) => item.json.email_enabled === true && String(item.json.contact_email ?? '').trim());"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-150,
|
|
-100
|
|
],
|
|
"id": "96b06567-a67f-49cd-af02-917c04d28c34",
|
|
"name": "Preparar correos"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"sendTo": "={{ $json.contact_email }}",
|
|
"subject": "={{ $json.email_subject }}",
|
|
"emailType": "html",
|
|
"message": "={{ $json.email_html }}",
|
|
"options": {
|
|
"appendAttribution": false
|
|
}
|
|
},
|
|
"type": "n8n-nodes-base.gmail",
|
|
"typeVersion": 2.1,
|
|
"position": [
|
|
100,
|
|
-100
|
|
],
|
|
"id": "502b8c7c-1cf6-4072-a62e-698e08506667",
|
|
"name": "Gmail - Enviar recordatorio"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "={{ ($env.SUPABASE_URL || 'https://dbit.digitalcompass.agency').replace(/\\/$/, '') + '/rest/v1/rpc/tax_record_notification' }}",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "={{ $env.SUPABASE_SERVICE_ROLE_KEY }}"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "={{ 'Bearer ' + $env.SUPABASE_SERVICE_ROLE_KEY }}"
|
|
},
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ { p_obligation_id: $('Preparar correos').item.json.obligation_id, p_contact_id: $('Preparar correos').item.json.contact_id, p_alert_date: $('Preparar correos').item.json.alert_date, p_channel: 'email', p_status: 'sent', p_provider_response: $json } }}",
|
|
"options": {}
|
|
},
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.4,
|
|
"position": [
|
|
350,
|
|
-100
|
|
],
|
|
"id": "054a395c-1a8a-4313-b17a-8571dd693707",
|
|
"name": "Supabase - Registrar correo"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"jsCode": "return $input.all().filter((item) => item.json.whatsapp_enabled === true && String(item.json.whatsapp_to ?? '').trim());"
|
|
},
|
|
"type": "n8n-nodes-base.code",
|
|
"typeVersion": 2,
|
|
"position": [
|
|
-150,
|
|
180
|
|
],
|
|
"id": "bb7ad7d3-67a2-4bec-a31d-b723af8b7ed0",
|
|
"name": "Preparar WhatsApp"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "={{ ($env.WHATSAPP_BASE_URL || 'https://wsp.gomezleemarketing.com').replace(/\\/$/, '') + '/message/sendText/' + ($env.WHATSAPP_INSTANCE || 'botsoporte') }}",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "={{ $env.WHATSAPP_API_KEY }}"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"bodyParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "number",
|
|
"value": "={{ $json.whatsapp_to }}"
|
|
},
|
|
{
|
|
"name": "delay",
|
|
"value": "={{ 1000 }}"
|
|
},
|
|
{
|
|
"name": "text",
|
|
"value": "={{ $json.whatsapp_text }}"
|
|
}
|
|
]
|
|
},
|
|
"options": {}
|
|
},
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.4,
|
|
"position": [
|
|
100,
|
|
180
|
|
],
|
|
"id": "34bae65c-ae17-4bde-9caa-21218c5cc2d0",
|
|
"name": "WhatsApp - Enviar recordatorio"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"method": "POST",
|
|
"url": "={{ ($env.SUPABASE_URL || 'https://dbit.digitalcompass.agency').replace(/\\/$/, '') + '/rest/v1/rpc/tax_record_notification' }}",
|
|
"sendHeaders": true,
|
|
"headerParameters": {
|
|
"parameters": [
|
|
{
|
|
"name": "apikey",
|
|
"value": "={{ $env.SUPABASE_SERVICE_ROLE_KEY }}"
|
|
},
|
|
{
|
|
"name": "Authorization",
|
|
"value": "={{ 'Bearer ' + $env.SUPABASE_SERVICE_ROLE_KEY }}"
|
|
},
|
|
{
|
|
"name": "Content-Type",
|
|
"value": "application/json"
|
|
}
|
|
]
|
|
},
|
|
"sendBody": true,
|
|
"specifyBody": "json",
|
|
"jsonBody": "={{ { p_obligation_id: $('Preparar WhatsApp').item.json.obligation_id, p_contact_id: $('Preparar WhatsApp').item.json.contact_id, p_alert_date: $('Preparar WhatsApp').item.json.alert_date, p_channel: 'whatsapp', p_status: 'sent', p_provider_response: $json } }}",
|
|
"options": {}
|
|
},
|
|
"type": "n8n-nodes-base.httpRequest",
|
|
"typeVersion": 4.4,
|
|
"position": [
|
|
350,
|
|
180
|
|
],
|
|
"id": "c26d3b1b-e257-47c2-a865-055ba889f536",
|
|
"name": "Supabase - Registrar WhatsApp"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"content": "## CONFIGURACIÓN ANTES DE ACTIVAR\n\n1. Selecciona la credencial de **Gmail** en el nodo de envío.\n2. Crea estas variables en n8n:\n- `SUPABASE_URL`\n- `SUPABASE_SERVICE_ROLE_KEY`\n- `WHATSAPP_API_KEY`\n- `WHATSAPP_BASE_URL` (opcional)\n- `WHATSAPP_INSTANCE` (opcional)\n- `GLM_TAX_APP_URL` (opcional)\n- `GLM_LOGO_URL` (opcional)\n\nLa `service_role` sólo va en n8n, nunca en el frontend.",
|
|
"height": 360,
|
|
"width": 420
|
|
},
|
|
"type": "n8n-nodes-base.stickyNote",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
-1220,
|
|
-620
|
|
],
|
|
"id": "51a235ae-93c1-4554-b13f-9d6b142d9cef",
|
|
"name": "Configuración"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"content": "## REGLA DE ALERTAS\n\nEl flujo corre diariamente a las **8:00 AM** (America/Santo_Domingo).\n\nCada obligación se avisa:\n- el miércoles estrictamente anterior;\n- y el mismo día de vencimiento.\n\nTambién aplica si vence sábado o domingo. La base de datos evita duplicados por obligación, contacto, fecha y canal.",
|
|
"height": 260,
|
|
"width": 400
|
|
},
|
|
"type": "n8n-nodes-base.stickyNote",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
-720,
|
|
-470
|
|
],
|
|
"id": "f2c36c91-3d35-4684-9d68-da5bede36cfd",
|
|
"name": "Regla de alertas"
|
|
},
|
|
{
|
|
"parameters": {
|
|
"content": "## ENRUTAMIENTO\n\n- Contactos **Regional**: reciben todas las obligaciones.\n- Contactos de **Nómina**: sólo obligaciones de Nómina de su país.\n- Contactos de **Administración**: sólo obligaciones de Administración de su país.\n\nLos contactos se administran desde la app y el flujo los consulta en Supabase en cada ejecución. No hay que editar el workflow para agregar destinatarios.",
|
|
"height": 300,
|
|
"width": 420
|
|
},
|
|
"type": "n8n-nodes-base.stickyNote",
|
|
"typeVersion": 1,
|
|
"position": [
|
|
-180,
|
|
-470
|
|
],
|
|
"id": "c19ded8a-93b9-46df-b747-fa19ee271f72",
|
|
"name": "Enrutamiento"
|
|
}
|
|
],
|
|
"pinData": {},
|
|
"connections": {
|
|
"Diario 8:00 AM": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar ejecución",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Ejecutar manualmente": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar ejecución",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Webhook seguimiento-impuestos": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Validar ejecución por Webhook",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar ejecución": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Supabase - Recordatorios pendientes",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Validar ejecución por Webhook": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar ejecución",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Supabase - Recordatorios pendientes": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Construir mensajes GLM",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Construir mensajes GLM": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Preparar correos",
|
|
"type": "main",
|
|
"index": 0
|
|
},
|
|
{
|
|
"node": "Preparar WhatsApp",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar correos": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Gmail - Enviar recordatorio",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Gmail - Enviar recordatorio": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Supabase - Registrar correo",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"Preparar WhatsApp": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "WhatsApp - Enviar recordatorio",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
},
|
|
"WhatsApp - Enviar recordatorio": {
|
|
"main": [
|
|
[
|
|
{
|
|
"node": "Supabase - Registrar WhatsApp",
|
|
"type": "main",
|
|
"index": 0
|
|
}
|
|
]
|
|
]
|
|
}
|
|
},
|
|
"active": false,
|
|
"settings": {
|
|
"executionOrder": "v1",
|
|
"timezone": "America/Santo_Domingo",
|
|
"saveManualExecutions": true
|
|
},
|
|
"versionId": "87be43e3-7709-4f42-bfa6-03fd337cbfcd",
|
|
"meta": {
|
|
"templateCredsSetupCompleted": false
|
|
},
|
|
"tags": []
|
|
} |