{
  "name": "Reporte ejecutivo semanal (plano de Saphia Labs)",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 16 * * 5"
            }
          ]
        }
      },
      "id": "trig",
      "name": "Schedule Trigger",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        0,
        0
      ]
    },
    {
      "parameters": {
        "operation": "read",
        "documentId": {
          "__rl": true,
          "mode": "id",
          "value": "TU_SPREADSHEET_ID"
        },
        "sheetName": {
          "__rl": true,
          "mode": "name",
          "value": "metricas"
        },
        "options": {}
      },
      "id": "rd",
      "name": "Sheets: métricas semanales",
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        260,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "// Última semana vs anterior y vs misma semana del año pasado (si existe). Las cifras vienen de la hoja; aquí no se inventa nada.\nconst rows = $input.all().map(i => i.json).filter(r => /^\\d{4}-\\d{2}-\\d{2}/.test(String(r.semana))).sort((a, b) => String(a.semana).localeCompare(String(b.semana)));\nif (rows.length < 2) throw new Error('Necesito al menos dos semanas en la hoja');\nconst num = v => Number(String(v).replace(/[^\\d.-]/g, '')) || 0;\nconst last = rows[rows.length - 1], prev = rows[rows.length - 2];\nconst d = new Date(last.semana); const ly = new Date(d); ly.setFullYear(d.getFullYear() - 1);\nconst lyRow = rows.find(r => Math.abs(new Date(r.semana) - ly) < 4 * 86400000) || null;\nconst metricas = Object.keys(last).filter(k => k !== 'semana' && k !== 'row_number').map(k => { const a = num(last[k]), b = num(prev[k]); const c = lyRow ? num(lyRow[k]) : null; return { metrica: k, actual: a, anterior: b, variacion_pct: b ? Math.round((a - b) / b * 1000) / 10 : null, anio_pasado: c, variacion_anual_pct: c ? Math.round((a - c) / c * 1000) / 10 : null }; });\nreturn [{ json: { semana: last.semana, semana_anterior: prev.semana, hay_dato_anual: !!lyRow, metricas } }];"
      },
      "id": "calc",
      "name": "Code: variaciones",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        520,
        0
      ]
    },
    {
      "parameters": {
        "resource": "text",
        "operation": "message",
        "modelId": {
          "__rl": true,
          "mode": "list",
          "value": "claude-sonnet-5-5"
        },
        "messages": {
          "values": [
            {
              "role": "user",
              "content": "={{ 'Semana que cierra el ' + $json.semana + ' (comparada con la del ' + $json.semana_anterior + ($json.hay_dato_anual ? ' y con la misma semana del año pasado' : '; no hay dato del año pasado') + ').\\nCifras en pesos colombianos salvo conteos.\\n\\n' + JSON.stringify($json.metricas, null, 2) }}"
            }
          ]
        },
        "options": {
          "system": "Eres el jefe de gabinete de [organización]. Recibes las cifras de la semana con sus variaciones. Escribe un reporte de máximo 350 palabras con esta estructura:\n1. Titular: una frase con lo más importante de la semana.\n2. Qué pasó: 3 a 5 viñetas con cifra y variación, de mayor a menor impacto.\n3. Por qué: hipótesis solo si los datos la sostienen; si no, di que falta información.\n4. Qué mirar la próxima semana: 2 o 3 puntos concretos con responsable sugerido.\nReglas: no inventes causas ni cifras; usa los números tal como vienen; sin adjetivos vacíos; lenguaje de dueño de negocio, no de analista.",
          "maxTokens": 4000
        }
      },
      "id": "cl",
      "name": "Claude: redactar reporte",
      "type": "@n8n/n8n-nodes-langchain.anthropic",
      "typeVersion": 1,
      "position": [
        780,
        0
      ]
    },
    {
      "parameters": {
        "jsCode": "const texto = ($json.content || []).filter(b => b.type === 'text').map(b => b.text).join('\\n').trim();\nconst semana = $('Code: variaciones').first().json.semana;\nreturn [{ json: { semana, reporte: texto, nombre: 'reporte-semanal-' + semana + '.md' } }];"
      },
      "id": "tx",
      "name": "Code: texto del reporte",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1040,
        0
      ]
    },
    {
      "parameters": {
        "operation": "toText",
        "sourceProperty": "reporte",
        "options": {
          "fileName": "={{ $json.nombre }}",
          "encoding": "utf8"
        }
      },
      "id": "cf",
      "name": "Convert to File",
      "type": "n8n-nodes-base.convertToFile",
      "typeVersion": 1.1,
      "position": [
        1300,
        -120
      ]
    },
    {
      "parameters": {
        "name": "={{ $('Code: texto del reporte').first().json.nombre }}",
        "driveId": {
          "__rl": true,
          "mode": "list",
          "value": "My Drive"
        },
        "folderId": {
          "__rl": true,
          "mode": "id",
          "value": "TU_CARPETA_ID"
        },
        "options": {}
      },
      "id": "up",
      "name": "Drive: guardar reporte",
      "type": "n8n-nodes-base.googleDrive",
      "typeVersion": 3,
      "position": [
        1560,
        -120
      ]
    },
    {
      "parameters": {
        "resource": "draft",
        "subject": "=Reporte semanal · semana del {{ $json.semana }}",
        "emailType": "text",
        "message": "={{ $json.reporte }}",
        "options": {
          "sendTo": "TU_CORREO"
        }
      },
      "id": "gm",
      "name": "Gmail: borrador a dirección",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1300,
        120
      ]
    }
  ],
  "connections": {
    "Schedule Trigger": {
      "main": [
        [
          {
            "node": "Sheets: métricas semanales",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Sheets: métricas semanales": {
      "main": [
        [
          {
            "node": "Code: variaciones",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: variaciones": {
      "main": [
        [
          {
            "node": "Claude: redactar reporte",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Claude: redactar reporte": {
      "main": [
        [
          {
            "node": "Code: texto del reporte",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Code: texto del reporte": {
      "main": [
        [
          {
            "node": "Convert to File",
            "type": "main",
            "index": 0
          },
          {
            "node": "Gmail: borrador a dirección",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Convert to File": {
      "main": [
        [
          {
            "node": "Drive: guardar reporte",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "settings": {
    "executionOrder": "v1",
    "timezone": "America/Bogota"
  },
  "meta": {
    "templateCredsSetupCompleted": false,
    "probadoEn": "n8n 2.41.6, octubre 2026",
    "fuente": "https://saphialabs.com/recursos/flujos/#f6",
    "preparacion": "Crea una pestaña \"metricas\" con una fila por semana (columna semana en formato AAAA-MM-DD y una columna por métrica, con la estructura congelada) y una carpeta en Drive para los reportes. Reemplaza TU_SPREADSHEET_ID, TU_CARPETA_ID y TU_CORREO. Para sumar Analytics o el CRM, añade sus nodos antes de \"Code: variaciones\" y combínalos en una sola fila por semana."
  }
}