{
  "name": "02 - Recordatorio 24h antes (CON VERIFICACI\u00d3N) \u2705",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 10 * * *"
            }
          ]
        }
      },
      "id": "02b2470c-a797-4827-a2b9-6e1065a1784d",
      "name": "\u23f0 Cron Diario 10:00 AM",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.1,
      "position": [
        -1088,
        608
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "reservations",
        "returnAll": true
      },
      "id": "93d5bd94-8838-44c1-97f0-69d6ad539502",
      "name": "\ud83d\udcca Obtener TODAS las Reservas",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        -848,
        608
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const tomorrow = new Date();\ntomorrow.setDate(tomorrow.getDate() + 1);\nconst tomorrowStr = tomorrow.toISOString().split('T')[0];\n\nconst filtered = $input.all().filter(item => {\n  const data = item.json;\n  return data.reservation_date === tomorrowStr &&\n         data.status === 'pending' &&\n         data.customer_phone !== null &&\n         data.customer_phone !== '';\n});\n\nconsole.log(`\ud83d\udd0d Encontradas ${filtered.length} reservas para ma\u00f1ana (${tomorrowStr})`);\n\nreturn filtered;"
      },
      "id": "7dcf4283-5d93-416f-b7f5-ffd740aee264",
      "name": "\ud83d\udd0d Filtrar: Ma\u00f1ana + Pending",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -608,
        608
      ]
    },
    {
      "parameters": {
        "conditions": {
          "boolean": [
            {
              "value1": "={{ $input.all().length }}",
              "value2": 0,
              "operator": {
                "type": "number",
                "operation": "gt"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "bd9a6b86-4d10-4544-a3c0-aa3748c014f1",
      "name": "\u2753 \u00bfHay Reservas?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -416,
        608
      ]
    },
    {
      "parameters": {
        "options": {}
      },
      "id": "5d133977-c65b-42d8-bd19-bdd7f6ef05ef",
      "name": "\ud83d\udd01 Loop Cada Reserva",
      "type": "n8n-nodes-base.splitInBatches",
      "typeVersion": 3,
      "position": [
        -144,
        400
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://ktsqwvhqamedpmzkzjaz.supabase.co/rest/v1/rpc/check_confirmation_sent",
        "authentication": "predefinedCredentialType",
        "nodeCredentialType": "supabaseApi",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Content-Type",
              "value": "application/json"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"p_reservation_id\": \"{{ $json.id }}\",\n  \"p_message_type\": \"24h\"\n}",
        "options": {}
      },
      "id": "check-duplicate-24h",
      "name": "\ud83d\udd0d Verificar Si Ya Enviado",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        80,
        400
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "notes": "\u2705 Verifica duplicados v\u00eda RPC"
    },
    {
      "parameters": {
        "jsCode": "const reservation = $('\ud83d\udd01 Loop Cada Reserva').first().json;\nconst checkResult = $input.first().json;\n\nconst alreadySent = checkResult === true;\n\nconsole.log(`\ud83d\udd0d Reserva ${reservation.id}: ${alreadySent ? '\u26a0\ufe0f YA ENVIADO' : '\u2705 PENDIENTE DE ENVIAR'}`);\n\nreturn {\n  ...reservation,\n  already_sent: alreadySent,\n  should_send: !alreadySent\n};"
      },
      "id": "process-check-24h",
      "name": "\ud83d\udd04 Procesar Verificaci\u00f3n",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        304,
        400
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict"
          },
          "conditions": [
            {
              "id": "should-send",
              "leftValue": "={{ $json.should_send }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "id": "if-should-send-24h",
      "name": "\u2753 \u00bfEnviar Mensaje?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        528,
        400
      ]
    },
    {
      "parameters": {
        "jsCode": "const items = $input.all();\n\nconst results = items.map(item => {\n  const data = item.json;\n  let phone = data.customer_phone || '';\n  \n  if (phone && !phone.startsWith('+')) {\n    if (phone.startsWith('34')) {\n      phone = '+' + phone;\n    } else if (phone.startsWith('0034')) {\n      phone = '+' + phone.substring(2);\n    } else {\n      phone = '+34' + phone;\n    }\n  }\n  \n  return {\n    json: {\n      ...data,\n      customer_phone_normalized: phone\n    }\n  };\n});\n\nreturn results;"
      },
      "id": "328006ee-56b3-4071-8c11-be0af71d4d46",
      "name": "\ud83d\udcde Normalizar Tel\u00e9fono",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        752,
        320
      ]
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "restaurants",
        "returnAll": true,
        "filters": {
          "conditions": [
            {
              "keyName": "id",
              "condition": "eq",
              "keyValue": "={{ $json.restaurant_id }}"
            }
          ]
        }
      },
      "id": "dad76973-867b-495e-bdd8-74230c02a031",
      "name": "\ud83d\udccd Obtener Config Restaurante",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        944,
        320
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "getAll",
        "tableId": "message_templates",
        "returnAll": false,
        "limit": 1,
        "filters": {
          "conditions": [
            {
              "keyName": "restaurant_id",
              "condition": "eq",
              "keyValue": "={{ $('\ud83d\udccd Obtener Config Restaurante').first().json.id }}"
            },
            {
              "keyName": "category",
              "condition": "eq",
              "keyValue": "='confirmacion_24h'"
            },
            {
              "keyName": "is_active",
              "condition": "eq",
              "keyValue": "true"
            }
          ]
        }
      },
      "id": "aaa111bb-2222-3333-4444-555566667777",
      "name": "\ud83d\udcc4 Obtener Plantilla 24h",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1136,
        320
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const templateData = $json;\nconst restaurantData = $('\ud83d\udccd Obtener Config Restaurante').first().json;\nconst reservaData = $('\ud83d\udcde Normalizar Tel\u00e9fono').first().json;\n\nif (!reservaData || !restaurantData) {\n  throw new Error('\u274c Faltan datos de reserva o restaurante');\n}\n\nconst reserva = reservaData;\nconst restaurant = restaurantData;\n\nlet template = templateData?.content_markdown || '';\n\nif (!template || template.trim() === '') {\n  template = `Hola {{customer_name}}! \ud83d\udc4b\\n\\nTe recordamos tu reserva en {{restaurant_name}} para ma\u00f1ana a las {{reservation_time}} para {{party_size}} persona(s).\\n\\n\u00a1Te esperamos!`;\n}\n\nconst variables = {\n  customer_name: reserva.customer_name || 'Cliente',\n  restaurant_name: restaurant.name || 'Restaurante',\n  reservation_time: reserva.reservation_time || '',\n  party_size: (reserva.party_size || 1).toString()\n};\n\nlet message = template;\nfor (const [key, value] of Object.entries(variables)) {\n  const regex = new RegExp(`\\\\{\\\\{${key}\\\\}\\\\}`, 'g');\n  message = message.replace(regex, value);\n}\n\nif (!message || message.trim() === '') {\n  message = `Hola ${variables.customer_name}! Te recordamos tu reserva en ${variables.restaurant_name} para ma\u00f1ana a las ${variables.reservation_time}.`;\n}\n\nreturn [{\n  json: {\n    id: reserva.id,\n    restaurant_id: reserva.restaurant_id,\n    customer_id: reserva.customer_id,\n    customer_name: reserva.customer_name,\n    customer_phone: reserva.customer_phone,\n    customer_phone_normalized: reserva.customer_phone_normalized,\n    reservation_date: reserva.reservation_date,\n    reservation_time: reserva.reservation_time,\n    party_size: reserva.party_size,\n    status: reserva.status,\n    restaurant_phone: restaurant.phone,\n    restaurant_name: restaurant.name,\n    message_final: message\n  }\n}];"
      },
      "id": "bbb222cc-3333-4444-5555-666677778888",
      "name": "\ud83d\udd04 Reemplazar Variables",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1328,
        320
      ]
    },
    {
      "parameters": {
        "from": "={{ $json.restaurant_phone }}",
        "to": "={{ $json.customer_phone_normalized }}",
        "toWhatsapp": true,
        "message": "={{ $json.message_final }}",
        "options": {}
      },
      "id": "fc22dd7a-a03e-4a35-87da-4b148f90f6f5",
      "name": "\ud83d\udcf1 Twilio: Enviar WhatsApp",
      "type": "n8n-nodes-base.twilio",
      "typeVersion": 1,
      "position": [
        1520,
        320
      ],
      "credentials": {
        "twilioApi": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "operation": "create",
        "tableId": "confirmation_messages",
        "fieldsUi": {
          "fieldValues": [
            {
              "fieldId": "reservation_id",
              "fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.id }}"
            },
            {
              "fieldId": "restaurant_id",
              "fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.restaurant_id }}"
            },
            {
              "fieldId": "message_type",
              "fieldValue": "24h"
            },
            {
              "fieldId": "status",
              "fieldValue": "sent"
            },
            {
              "fieldId": "customer_phone",
              "fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.customer_phone_normalized }}"
            },
            {
              "fieldId": "customer_name",
              "fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.customer_name }}"
            },
            {
              "fieldId": "reservation_date",
              "fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.reservation_date }}"
            },
            {
              "fieldId": "reservation_time",
              "fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.reservation_time }}"
            },
            {
              "fieldId": "message_content",
              "fieldValue": "={{ $('\ud83d\udd04 Reemplazar Variables').first().json.message_final }}"
            }
          ]
        },
        "options": {}
      },
      "id": "45a665bb-1706-4f77-a66f-0c14db86251f",
      "name": "\ud83d\udcbe Registrar en confirmation_messages",
      "type": "n8n-nodes-base.supabase",
      "typeVersion": 1,
      "position": [
        1712,
        320
      ],
      "credentials": {
        "supabaseApi": {
          "name": "<your credential>"
        }
      },
      "notes": "\u2705 Registro en la tabla del nuevo sistema"
    },
    {
      "parameters": {
        "jsCode": "console.log('\u2705 Mensaje 24h enviado y registrado');\nreturn { success: true };"
      },
      "id": "log-sent-24h",
      "name": "\u2705 Log: Enviado",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1904,
        320
      ]
    },
    {
      "parameters": {
        "jsCode": "console.log('\u26a0\ufe0f Mensaje ya enviado anteriormente - Saltando');\nreturn { skipped: true };"
      },
      "id": "log-skipped-24h",
      "name": "\u26a0\ufe0f Log: Saltado",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        752,
        480
      ]
    },
    {
      "parameters": {},
      "id": "95e5a6ba-9bfc-4c24-a68d-c39cd570be42",
      "name": "\u2705 Fin: Sin Reservas",
      "type": "n8n-nodes-base.noOp",
      "typeVersion": 1,
      "position": [
        -128,
        848
      ]
    }
  ],
  "connections": {
    "\u23f0 Cron Diario 10:00 AM": {
      "main": [
        [
          {
            "node": "\ud83d\udcca Obtener TODAS las Reservas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcca Obtener TODAS las Reservas": {
      "main": [
        [
          {
            "node": "\ud83d\udd0d Filtrar: Ma\u00f1ana + Pending",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0d Filtrar: Ma\u00f1ana + Pending": {
      "main": [
        [
          {
            "node": "\u2753 \u00bfHay Reservas?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2753 \u00bfHay Reservas?": {
      "main": [
        [
          {
            "node": "\ud83d\udd01 Loop Cada Reserva",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u2705 Fin: Sin Reservas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd01 Loop Cada Reserva": {
      "main": [
        [],
        [
          {
            "node": "\ud83d\udd0d Verificar Si Ya Enviado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd0d Verificar Si Ya Enviado": {
      "main": [
        [
          {
            "node": "\ud83d\udd04 Procesar Verificaci\u00f3n",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd04 Procesar Verificaci\u00f3n": {
      "main": [
        [
          {
            "node": "\u2753 \u00bfEnviar Mensaje?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2753 \u00bfEnviar Mensaje?": {
      "main": [
        [
          {
            "node": "\ud83d\udcde Normalizar Tel\u00e9fono",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "\u26a0\ufe0f Log: Saltado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcde Normalizar Tel\u00e9fono": {
      "main": [
        [
          {
            "node": "\ud83d\udccd Obtener Config Restaurante",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udccd Obtener Config Restaurante": {
      "main": [
        [
          {
            "node": "\ud83d\udcc4 Obtener Plantilla 24h",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcc4 Obtener Plantilla 24h": {
      "main": [
        [
          {
            "node": "\ud83d\udd04 Reemplazar Variables",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udd04 Reemplazar Variables": {
      "main": [
        [
          {
            "node": "\ud83d\udcf1 Twilio: Enviar WhatsApp",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcf1 Twilio: Enviar WhatsApp": {
      "main": [
        [
          {
            "node": "\ud83d\udcbe Registrar en confirmation_messages",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\ud83d\udcbe Registrar en confirmation_messages": {
      "main": [
        [
          {
            "node": "\u2705 Log: Enviado",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u2705 Log: Enviado": {
      "main": [
        [
          {
            "node": "\ud83d\udd01 Loop Cada Reserva",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "\u26a0\ufe0f Log: Saltado": {
      "main": [
        [
          {
            "node": "\ud83d\udd01 Loop Cada Reserva",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "meta": {
    "templateCredsSetupCompleted": true
  },
  "tags": [
    {
      "createdAt": "2025-10-22T19:00:00.000Z",
      "updatedAt": "2025-10-22T19:00:00.000Z",
      "id": "recordatorio-24h-verificacion",
      "name": "\u2705 Con Verificaci\u00f3n de Duplicados"
    }
  ]
}