{
  "name": "P2-C2 Detector de Promesas (Skeleton)",
  "nodes": [
    {
      "parameters": {
        "path": "cobranza-in",
        "responseMode": "responseNode",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        0
      ],
      "id": "b2c00001-2222-4b02-a001-000000000001",
      "name": "Verificacion GET"
    },
    {
      "parameters": {
        "respondWith": "text",
        "responseBody": "={{ $json.query[\"hub.challenge\"] }}",
        "options": {}
      },
      "type": "n8n-nodes-base.respondToWebhook",
      "typeVersion": 1.5,
      "position": [
        208,
        0
      ],
      "id": "b2c00001-2222-4b02-a001-000000000002",
      "name": "Respond to Webhook"
    },
    {
      "parameters": {
        "httpMethod": "POST",
        "path": "cobranza-in",
        "options": {}
      },
      "type": "n8n-nodes-base.webhook",
      "typeVersion": 2.1,
      "position": [
        0,
        176
      ],
      "id": "b2c00001-2222-4b02-a001-000000000003",
      "name": "Entrada POST"
    },
    {
      "parameters": {
        "jsCode": "// Desenvuelve el sobre de Meta y filtra: solo mensajes de texto\nconst value = $json.body?.entry?.[0]?.changes?.[0]?.value;\nif (!value?.messages?.[0]?.text?.body) return []; // ignora estados, reacciones, etc.\nreturn [{ json: value }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        208,
        176
      ],
      "id": "b2c00001-2222-4b02-a001-000000000004",
      "name": "Normalizar"
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "value": "TU_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "facturas",
          "mode": "name"
        },
        "filtersUI": {
          "values": [
            {
              "lookupColumn": "telefono",
              "lookupValue": "={{ $('Normalizar').item.json.messages[0].from }}"
            }
          ]
        },
        "combineFilters": "AND",
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        416,
        176
      ],
      "id": "b2c00001-2222-4b02-a001-000000000005",
      "name": "Buscar Factura",
      "alwaysOutputData": true
    },
    {
      "parameters": {
        "jsCode": "// Solo seguimos si el n\u00famero tiene una factura abierta (pendiente, recordada o promesa).\n// Si no es un deudor, este flujo lo ignora en silencio.\nconst abiertas = [\"pendiente\", \"recordada\", \"promesa\"];\nconst facturas = $input.all().map(i => i.json)\n  .filter(f => f.factura_id && abiertas.includes(String(f.estado || \"\").trim().toLowerCase()));\nif (!facturas.length) return [];\n// Si tiene varias facturas abiertas, tomamos la de vencimiento m\u00e1s antiguo\nfacturas.sort((a, b) => String(a.fecha_vencimiento).localeCompare(String(b.fecha_vencimiento)));\nconst msg = $('Normalizar').first().json;\nreturn [{ json: {\n  factura: facturas[0],\n  telefono: msg.messages[0].from,\n  nombre: msg.contacts?.[0]?.profile?.name || facturas[0].cliente || \"\",\n  mensaje: msg.messages[0].text.body\n} }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        624,
        176
      ],
      "id": "b2c00001-2222-4b02-a001-000000000006",
      "name": "Filtrar Deudor"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://generativelanguage.googleapis.com/v1beta/models/MODELO_GEMINI:generateContent",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "key",
              "value": "TU_GEMINI_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={\n  \"contents\": [{\n    \"parts\": [{\n      \"text\": \"Extrae del siguiente mensaje SOLO un objeto JSON estricto con esta forma exacta: {\\\"hay_promesa\\\": <true|false>, \\\"fecha_prometida\\\": \\\"<YYYY-MM-DD o null>\\\", \\\"monto\\\": <n\u00famero o null>}.\\n\\nMensaje del cliente: {{ $json.mensaje }}\"\n    }]\n  }],\n  \"generationConfig\": { \"responseMimeType\": \"application/json\", \"temperature\": 0.1 }\n}",
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        832,
        176
      ],
      "id": "b2c00001-2222-4b02-a001-000000000007",
      "name": "Gemini Extrae Promesa"
    },
    {
      "parameters": {
        "jsCode": "// Convierte la respuesta de Gemini a objeto. Si algo falla,\n// hay_promesa queda en false y el caso se escala a gesti\u00f3n humana (nunca se pierde).\nlet out = { hay_promesa: false, fecha_prometida: null, monto: null };\n\ntry {\n  const raw = $input.first().json.candidates[0].content.parts[0].text;\n  const clean = raw.replace(/```json|```/g, \"\").trim();\n  const parsed = JSON.parse(clean);\n  out = { ...out, ...parsed };\n  if (out.fecha_prometida && !/^\\d{4}-\\d{2}-\\d{2}$/.test(String(out.fecha_prometida))) out.fecha_prometida = null;\n  if (out.hay_promesa && !out.fecha_prometida) out.hay_promesa = false; // sin fecha no hay promesa agendable\n  out.hay_promesa = out.hay_promesa === true || out.hay_promesa === \"true\";\n} catch (e) {\n  out.error_parseo = e.message;\n}\n\nconst d = $('Filtrar Deudor').first().json;\nout.telefono = d.telefono;\nout.nombre = d.nombre;\nout.mensaje = d.mensaje;\nout.factura_id = d.factura.factura_id;\nout.cliente = d.factura.cliente || d.nombre;\nout.monto_factura = d.factura.monto;\nout.hoy = new Date().toISOString().slice(0, 10);\n\nreturn [{ json: out }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1040,
        176
      ],
      "id": "b2c00001-2222-4b02-a001-000000000008",
      "name": "Parsear Promesa"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 2
          },
          "conditions": [
            {
              "id": "b2c00001-2222-4b02-a001-000000000201",
              "leftValue": "={{ $json.hay_promesa }}",
              "rightValue": "",
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "looseTypeValidation": true,
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1248,
        176
      ],
      "id": "b2c00001-2222-4b02-a001-000000000009",
      "name": "Hay Promesa"
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "TU_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "promesas",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "factura_id": "={{ $json.factura_id }}",
            "telefono": "={{ $json.telefono }}",
            "cliente": "={{ $json.cliente }}",
            "fecha_promesa": "={{ $json.hoy }}",
            "fecha_prometida": "={{ $json.fecha_prometida }}",
            "monto_prometido": "={{ $json.monto || $json.monto_factura }}",
            "estado": "activa",
            "recordada": "no",
            "mensaje_original": "={{ $json.mensaje }}"
          },
          "matchingColumns": [
            "factura_id"
          ],
          "schema": []
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1456,
        64
      ],
      "id": "b2c00001-2222-4b02-a001-000000000010",
      "name": "Registrar Promesa"
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "TU_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "facturas",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "factura_id": "={{ $('Parsear Promesa').item.json.factura_id }}",
            "estado": "promesa",
            "notas": "={{ 'Promesa: ' + $('Parsear Promesa').item.json.fecha_prometida + ' \u2014 \"' + $('Parsear Promesa').item.json.mensaje + '\"' }}"
          },
          "matchingColumns": [
            "factura_id"
          ],
          "schema": []
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1664,
        64
      ],
      "id": "b2c00001-2222-4b02-a001-000000000011",
      "name": "Marcar Factura Promesa"
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "phoneNumberId": "TU_PHONE_NUMBER_ID",
        "recipientPhoneNumber": "={{ $('Parsear Promesa').item.json.telefono }}",
        "textBody": "=\u00a1Perfecto{{ $('Parsear Promesa').item.json.nombre ? ', ' + $('Parsear Promesa').item.json.nombre : '' }}! \ud83d\ude4c Anotamos tu pago de ${{ $('Parsear Promesa').item.json.monto || $('Parsear Promesa').item.json.monto_factura }} para el {{ $('Parsear Promesa').item.json.fecha_prometida }}. Ese d\u00eda te enviaremos un recordatorio. \u00a1Gracias!",
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.whatsApp",
      "typeVersion": 1,
      "position": [
        1872,
        64
      ],
      "id": "b2c00001-2222-4b02-a001-000000000012",
      "name": "Confirmar al Cliente"
    },
    {
      "parameters": {
        "operation": "appendOrUpdate",
        "documentId": {
          "__rl": true,
          "value": "TU_SHEET_ID",
          "mode": "id"
        },
        "sheetName": {
          "__rl": true,
          "value": "facturas",
          "mode": "name"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "factura_id": "={{ $('Parsear Promesa').item.json.factura_id }}",
            "estado": "gestion_humana",
            "notas": "={{ 'Respuesta sin promesa clara: \"' + $('Parsear Promesa').item.json.mensaje + '\"' }}"
          },
          "matchingColumns": [
            "factura_id"
          ],
          "schema": []
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        1456,
        288
      ],
      "id": "b2c00001-2222-4b02-a001-000000000013",
      "name": "Marcar Gestion Humana"
    },
    {
      "parameters": {
        "resource": "message",
        "operation": "send",
        "phoneNumberId": "TU_PHONE_NUMBER_ID",
        "recipientPhoneNumber": "TU_NUMERO_DUENO",
        "textBody": "=\u26a0\ufe0f COBRANZA \u2014 Requiere gesti\u00f3n humana\n{{ $('Parsear Promesa').item.json.cliente || $('Parsear Promesa').item.json.telefono }} respondi\u00f3 sobre la factura {{ $('Parsear Promesa').item.json.factura_id }} (${{ $('Parsear Promesa').item.json.monto_factura }}) y no se detect\u00f3 una promesa de pago clara:\n\"{{ $('Parsear Promesa').item.json.mensaje }}\"\nResponder personalmente.",
        "additionalFields": {}
      },
      "type": "n8n-nodes-base.whatsApp",
      "typeVersion": 1,
      "position": [
        1664,
        288
      ],
      "id": "b2c00001-2222-4b02-a001-000000000014",
      "name": "Alerta al Dueno"
    }
  ],
  "connections": {
    "Verificacion GET": {
      "main": [
        [
          {
            "node": "Respond to Webhook",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Entrada POST": {
      "main": [
        [
          {
            "node": "Normalizar",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Normalizar": {
      "main": [
        [
          {
            "node": "Buscar Factura",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Buscar Factura": {
      "main": [
        [
          {
            "node": "Filtrar Deudor",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filtrar Deudor": {
      "main": [
        [
          {
            "node": "Gemini Extrae Promesa",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Gemini Extrae Promesa": {
      "main": [
        [
          {
            "node": "Parsear Promesa",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parsear Promesa": {
      "main": [
        [
          {
            "node": "Hay Promesa",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Hay Promesa": {
      "main": [
        [
          {
            "node": "Registrar Promesa",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Marcar Gestion Humana",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Registrar Promesa": {
      "main": [
        [
          {
            "node": "Marcar Factura Promesa",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcar Factura Promesa": {
      "main": [
        [
          {
            "node": "Confirmar al Cliente",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Marcar Gestion Humana": {
      "main": [
        [
          {
            "node": "Alerta al Dueno",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1",
    "timezone": "America/Guayaquil"
  },
  "tags": []
}