{
  "name": "ALLSoft — Recordatorio de pagos",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "days",
              "triggerAtHour": 9
            }
          ]
        }
      },
      "name": "Todos los días a las 9",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.3,
      "position": [
        -640,
        300
      ],
      "id": "c4rt-0001-4000-8000-000000000001"
    },
    {
      "parameters": {
        "jsCode": "// Facturas vencidas. Sustituye por tu sistema contable, Siigo, Alegra o una\n// hoja de cálculo.\nconst facturas = [\n  { cliente: 'Distribuidora Andina', correo: 'pagos@ejemplo.com',    factura: 'FV-2041', monto: 2400000, mora: 4,  recordatorios: 0 },\n  { cliente: 'Clínica del Norte',    correo: 'admin@ejemplo.com',    factura: 'FV-1998', monto: 850000,  mora: 19, recordatorios: 1 },\n  { cliente: 'Ferretería Central',   correo: 'gerencia@ejemplo.com', factura: 'FV-1877', monto: 3100000, mora: 47, recordatorios: 2 },\n  { cliente: 'Colegio San Marcos',   correo: 'rector@ejemplo.com',   factura: 'FV-2103', monto: 1200000, mora: 0,  recordatorios: 0 },\n];\n\nconst MAX_RECORDATORIOS = 3;\nconst MORA_PARA_HUMANO = 30;   // pasado ese punto no lo maneja un automatismo\n\nconst cop = (n) => '$ ' + Number(n).toLocaleString('es-CO');\n\nreturn facturas\n  .filter((f) => f.mora >= 1)                       // al día no se les escribe\n  .filter((f) => f.recordatorios < MAX_RECORDATORIOS)\n  .map((f) => {\n    // El tono sube con la mora, pero NUNCA se vuelve amenazante. Una cobranza\n    // agresiva por correo automático puede meterte en problemas legales y\n    // seguro te cuesta el cliente.\n    const tono = f.mora <= 7 ? 'amable' : 'firme';\n\n    return {\n      json: {\n        ...f,\n        nombre_corto: f.cliente.split(' ')[0],\n        monto_texto: cop(f.monto),\n        tono,\n        // Mora larga: esto lo habla una persona, no un correo automático\n        requiere_humano: f.mora > MORA_PARA_HUMANO,\n      },\n    };\n  });\n"
      },
      "name": "Facturas vencidas",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -420,
        300
      ],
      "notes": "Máximo 3 recordatorios por factura. Pasados 30 días de mora, deja de ser automático.",
      "id": "c4rt-0002-4000-8000-000000000002"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://api.groq.com/openai/v1/chat/completions",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "Authorization",
              "value": "Bearer TU_GROQ_API_KEY"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ JSON.stringify({ model: 'llama-3.3-70b-versatile', temperature: 0.6, max_tokens: 160, messages: [ { role: 'system', content: 'Escribes recordatorios de pago para un negocio en Colombia. Maximo 40 palabras, en tu-teo. Si el tono es amable, asume que fue un olvido. Si es firme, se claro pero cordial. NUNCA amenaces, NUNCA menciones abogados, demandas, centrales de riesgo ni acciones legales. NUNCA escribas cifras ni numeros de factura: se agregan despues. No uses emojis.' }, { role: 'user', content: 'Cliente: ' + $json.nombre_corto + '. Tono: ' + $json.tono + '. Dias de mora: ' + $json.mora } ] }) }}",
        "options": {}
      },
      "name": "Escribir el recordatorio (Groq)",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -180,
        300
      ],
      "onError": "continueErrorOutput",
      "retryOnFail": true,
      "maxTries": 3,
      "waitBetweenTries": 2000,
      "notes": "Solo el tono. Las cifras y el número de factura se pegan después.",
      "id": "c4rt-0003-4000-8000-000000000003"
    },
    {
      "parameters": {
        "mode": "runOnceForEachItem",
        "jsCode": "// Arma el recordatorio de pago. Corre una vez por factura.\n//\n// El número de factura, el monto y los días de mora se pegan desde los datos.\n// Al modelo solo se le pide el tono. Un cobro que cita un monto equivocado no\n// solo es vergonzoso: te quita toda autoridad para cobrar el correcto.\nconst f = $('Facturas vencidas').item.json;\n\nlet cuerpo;\ntry {\n  cuerpo = $input.item.json.choices[0].message.content.trim();\n} catch (e) {\n  cuerpo = `Hola, te escribimos para recordarte una factura pendiente.`;\n}\n\ncuerpo = cuerpo.replace(/^[\"']|[\"']$/g, '').slice(0, 450);\n\n// Red de seguridad: si el texto se fue a amenazas, reporte a centrales de\n// riesgo o acciones legales, NO sale. Eso lo decide una persona.\nconst amenaza = /(juríd|jurid|legal|abogad|demand|embarg|datacr[eé]dit|central de riesgo|report|cobro coactivo)/i\n  .test(cuerpo);\n\nconst mensaje =\n  `${cuerpo}\\n\\n` +\n  `Factura: ${f.factura}\\n` +\n  `Valor: ${f.monto_texto}\\n` +\n  `Días desde el vencimiento: ${f.mora}\\n\\n` +\n  `Si ya realizaste el pago, ignora este mensaje y avísanos para cruzarlo.`;\n\nreturn {\n  json: {\n    cliente: f.cliente,\n    correo: f.correo,\n    factura: f.factura,\n    monto_texto: f.monto_texto,\n    mora: f.mora,\n    asunto: `Recordatorio — factura ${f.factura}`,\n    mensaje,\n    // Se detiene si hay amenaza o si la mora ya es larga\n    detener: amenaza || f.requiere_humano,\n    motivo: amenaza\n      ? 'El texto menciona acciones legales o centrales de riesgo'\n      : f.requiere_humano\n        ? `Lleva ${f.mora} días de mora: esto lo habla una persona`\n        : '',\n  },\n};\n"
      },
      "name": "Armar el recordatorio",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        60,
        200
      ],
      "notes": "Pega factura y monto reales, y detecta si el texto se fue a amenazas.",
      "id": "c4rt-0004-4000-8000-000000000004"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "version": 2
          },
          "conditions": [
            {
              "id": "c1",
              "leftValue": "={{ $json.detener }}",
              "rightValue": "={{ true }}",
              "operator": {
                "type": "boolean",
                "operation": "false",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "name": "¿Se puede enviar solo?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.3,
      "position": [
        280,
        200
      ],
      "notes": "Si hay amenaza o la mora pasa de 30 días, no sale: lo maneja una persona.",
      "id": "c4rt-0005-4000-8000-000000000005"
    },
    {
      "parameters": {
        "sendTo": "={{ $json.correo }}",
        "subject": "={{ $json.asunto }}",
        "emailType": "text",
        "message": "={{ $json.mensaje }}",
        "options": {}
      },
      "name": "Enviar recordatorio",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.2,
      "position": [
        520,
        120
      ],
      "notes": "Conecta tu Gmail. Pruébalo primero contigo mismo.",
      "id": "c4rt-0006-4000-8000-000000000006"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "h1",
              "name": "estado",
              "value": "lo_maneja_una_persona",
              "type": "string"
            },
            {
              "id": "h2",
              "name": "cliente",
              "value": "={{ $json.cliente }}",
              "type": "string"
            },
            {
              "id": "h3",
              "name": "factura",
              "value": "={{ $json.factura }}",
              "type": "string"
            },
            {
              "id": "h4",
              "name": "mora",
              "value": "={{ $json.mora }}",
              "type": "number"
            },
            {
              "id": "h5",
              "name": "motivo",
              "value": "={{ $json.motivo }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "name": "Pasar a cobranza",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        520,
        300
      ],
      "notes": "Engánchalo a tu equipo de cartera. Una mora larga se habla, no se automatiza.",
      "id": "c4rt-0007-4000-8000-000000000007"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "e1",
              "name": "estado",
              "value": "error_ia",
              "type": "string"
            },
            {
              "id": "e2",
              "name": "detalle",
              "value": "={{ $json.error || 'Groq no respondió' }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "name": "Avisar del fallo",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        60,
        440
      ],
      "notes": "Salida de error del nodo de IA: el flujo no se cae, avisa.",
      "id": "c4rt-0008-4000-8000-000000000008"
    }
  ],
  "connections": {
    "Todos los días a las 9": {
      "main": [
        [
          {
            "node": "Facturas vencidas",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Facturas vencidas": {
      "main": [
        [
          {
            "node": "Escribir el recordatorio (Groq)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Escribir el recordatorio (Groq)": {
      "main": [
        [
          {
            "node": "Armar el recordatorio",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Avisar del fallo",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Armar el recordatorio": {
      "main": [
        [
          {
            "node": "¿Se puede enviar solo?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "¿Se puede enviar solo?": {
      "main": [
        [
          {
            "node": "Enviar recordatorio",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Pasar a cobranza",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1"
  },
  "pinData": {},
  "meta": {
    "instanceId": "allsoft-academia"
  },
  "tags": []
}
