AutomationFlowsEmail & Gmail › Relatórios Aos Laboratórios

Relatórios Aos Laboratórios

Relatórios aos Laboratórios. Uses httpRequest, gmail, formTrigger. Scheduled trigger; 48 nodes.

Cron / scheduled trigger★★★★★ complexity48 nodesHTTP RequestGmailForm Trigger
Email & Gmail Trigger: Cron / scheduled Nodes: 48 Complexity: ★★★★★ Added:

This workflow follows the Form Trigger → Gmail recipe pattern — see all workflows that pair these two integrations.

The workflow JSON

Copy or download the full n8n JSON below. Paste it into a new n8n workflow, add your credentials, activate. Full import guide →

Download .json
{
  "name": "Relat\u00f3rios aos Laborat\u00f3rios",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "cronExpression",
              "expression": "0 30 6 * * *"
            }
          ]
        }
      },
      "id": "18ba9477-684a-4922-923c-06d1e815fbff",
      "name": "Schedule 06:30",
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -3040,
        464
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// agenda_relatorios.js \u2014 Code node do n8n (o \"c\u00e9rebro\" da agenda)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Roda no in\u00edcio do workflow (depois do Schedule Trigger). Decide\n// QUAIS laborat\u00f3rios disparam HOJE, com QUAL per\u00edodo de venda, e j\u00e1\n// anexa os E-MAILS de cada um. A sa\u00edda alimenta:\n//   1) POST {hub}/relatorios/iniciar  \u2192 { envios: [...] }\n//   2) o loop de e-mail (emailSend SMTP) por lab, usando o manifesto.\n//\n// Fonte da verdade: planilha \"ENVIO DE RELAT\u00d3RIOS - VENDAS/ESTOQUE\".\n//\n// \u26a0\ufe0f DOIS PONTOS A CONFIRMAR (ver constantes abaixo):\n//   \u2022 REGRA_TIPO1: a planilha diz \"1\u00ba DIA \u00daTIL\"; antes t\u00ednhamos falado\n//     \"todo dia 1\u00ba (mesmo fim de semana)\". Default aqui = 1\u00ba dia \u00fatil.\n//   \u2022 SEMANA_ANTERIOR: seg\u2192dom (default) ou seg\u2192sex? (Bayer/Roche)\n//\n// OBS.: o ESTOQUE n\u00e3o tem filtro de data \u2014 \u00e9 a foto atual. A coluna\n// \"PER\u00cdODO ESTOQUE\" da planilha s\u00f3 explica POR QUE se envia naquele dia\n// (rodar no 1\u00ba dia \u00fatil \u2248 \"como o m\u00eas fechou\"). Por isso o rob\u00f4 n\u00e3o\n// recebe per\u00edodo de estoque, s\u00f3 o per\u00edodo de VENDA.\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// \u2500\u2500 Config: e-mails por laborat\u00f3rio (lab_id casa com o Hub/LABS) \u2500\u2500\nconst EMAILS = {\n  bayer:        [\"operacoescomerciaisbhp@bayer.com\", \"elias.zanatta@bayer.com\", \"paula.yokomizo@bayer.com\"],\n  csl:          [\"csl@wfbconsultoria.com.br\", \"Rafael.Esteves@csl.com.au\"],\n  fresenius:    [\"jailton.silva@fresenius-kabi.com\"],\n  glaxo:        [\"alexandre.x.mitsuda@gsk.com\"],\n  organon:      [\"rodrigo.blas@organon.com\"],\n  roche:        [\"brasil.operacoescomerciais@roche.com\", \"joao.pessoa@roche.com\"],\n  sandoz_rs_sc: [\"marcelo.novelli@sandoz.com\", \"isaque.fedrigo@sandoz.com\"],\n  sandoz_sp:    [\"roger.damian@sandoz.com\"],\n  sankyo:       [\"marcio.honorati@daiichisankyo.com\", \"pedro.mota@daiichisankyo.com\", \"alexandre.jesus@daiichisankyo.com\"],\n  sun_geral:    [\"Gisele.Lemos@sunpharma.com\", \"Ana.ferreira@sunpharma.com\"],\n  sun_13082:    [\"Cecilia.Castro@sunpharma.com\", \"Edgar.Lopes@sunpharma.com\"],\n  united:       [\"leonardo.rossi@knighttx.com\"],\n  // Labs do setor p\u00fablico (adicionados 2026-07):\n  apsen:        [\"airton.lima@apsen.com.br\", \"Gabriela.Teixeira@apsen.com.br\"],\n  mappel_quifa: [\"jeferson.squefi@interplayers.com.br\"],\n};\n\nconst NOMES = {\n  bayer: \"Bayer\", csl: \"CSL\", fresenius: \"Fresenius\", glaxo: \"Glaxo\",\n  organon: \"Organon\", roche: \"Roche\", sandoz_rs_sc: \"Sandoz RS/SC\",\n  sandoz_sp: \"Sandoz SP\", sankyo: \"Sankyo\", sun_geral: \"Sun Geral\",\n  sun_13082: \"Sun Item 13082\", united: \"United\",\n  apsen: \"Apsen\", mappel_quifa: \"Mappel / Quifa\",\n};\n\n// Respons\u00e1veis internos que recebem C\u00d3PIA OCULTA (BCC) de cada envio.\n// \u26a0\ufe0f PREENCHER (ex.: a colega que gera hoje + quem monitora).\nconst RESPONSAVEIS_BCC = [];\n\n// \u2500\u2500 MODO TESTE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Enquanto MODO_TESTE = true, NENHUM e-mail vai aos laborat\u00f3rios:\n// todos os relat\u00f3rios s\u00e3o redirecionados para EMAIL_TESTE (sem BCC).\n// Os avisos de conclus\u00e3o/erro v\u00e3o para EMAIL_AVISOS.\n// Para ir a PRODU\u00c7\u00c3O, basta trocar MODO_TESTE para false.\nconst MODO_TESTE = true;\nconst EMAIL_TESTE = \"maristela@ciamedrs.com.br\";  // destino dos relat\u00f3rios em teste\nconst EMAIL_AVISOS = \"ti6@ciamedrs.com.br\";         // avisos de conclus\u00e3o/erro (rob\u00f4)\nconst EMAIL_COLEGA = \"leonardodiascaumo@gmail.com\"; // colega que confere os envios (aviso por lab)\n\n// \u2500\u2500 FOR\u00c7AR TESTE \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Enquanto true, ignora as regras de data e emite 1 envio fixo (Bayer,\n// m\u00eas anterior) \u2014 serve para testar o WORKFLOW INTEIRO no n8n em qualquer\n// dia. Voltar para false em produ\u00e7\u00e3o (a\u00ed volta a respeitar o calend\u00e1rio).\nconst FORCAR_TESTE = false;\n\n// \u2500\u2500 Quem dispara em cada tipo + qual a regra de per\u00edodo da VENDA \u2500\u2500\n// regra_periodo \u2208 {mes_anterior, semana_anterior, mes_ate_ultima_sexta, mes_ate_ultima_quarta}\nconst TIPO1_PRIMEIRO_DIA_UTIL = [   // todos os 14, venda = todo m\u00eas anterior\n  \"bayer\", \"csl\", \"fresenius\", \"glaxo\", \"organon\", \"roche\",\n  \"sandoz_rs_sc\", \"sandoz_sp\", \"sankyo\", \"sun_geral\", \"sun_13082\", \"united\",\n  \"apsen\", \"mappel_quifa\",          // labs do setor p\u00fablico (mensais, m\u00eas anterior)\n];\nconst TIPO2_SEGUNDA = {\n  bayer: \"semana_anterior\", roche: \"semana_anterior\",\n  csl: \"mes_ate_ultima_sexta\", fresenius: \"mes_ate_ultima_sexta\",\n  glaxo: \"mes_ate_ultima_sexta\", sun_geral: \"mes_ate_ultima_sexta\",\n};\nconst TIPO3_QUINTA = { sun_13082: \"mes_ate_ultima_quarta\" };\n\n// \u2500\u2500 PAR\u00c2METROS (decididos com o Leonardo) \u2500\u2500\nconst REGRA_TIPO1 = \"dia_1\";        // dispara sempre no dia 1\u00ba, mesmo fim de semana\nconst SEMANA_ANTERIOR = \"seg_sex\";  // semana anterior = segunda a sexta\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 helpers de data \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nfunction fmt(d) {\n  const dd = String(d.getDate()).padStart(2, \"0\");\n  const mm = String(d.getMonth() + 1).padStart(2, \"0\");\n  return `${dd}/${mm}/${d.getFullYear()}`;\n}\nfunction addDias(d, n) { const x = new Date(d); x.setDate(x.getDate() + n); return x; }\nfunction ehFimDeSemana(d) { const w = d.getDay(); return w === 0 || w === 6; }\n\nfunction primeiroDiaUtilDoMes(ref) {\n  let d = new Date(ref.getFullYear(), ref.getMonth(), 1);\n  while (ehFimDeSemana(d)) d = addDias(d, 1); // sem feriados, conforme combinado\n  return d;\n}\nfunction ultimoDiaSemanaAntes(ref, weekday) {\n  // weekday: 1=seg ... 5=sex, 3=qua. Retorna a ocorr\u00eancia mais recente ANTES de ref.\n  let d = addDias(ref, -1);\n  while (d.getDay() !== weekday) d = addDias(d, -1);\n  return d;\n}\n\nfunction periodo(regra, hoje) {\n  if (regra === \"mes_anterior\") {\n    const ini = new Date(hoje.getFullYear(), hoje.getMonth() - 1, 1);\n    const fim = new Date(hoje.getFullYear(), hoje.getMonth(), 0); // \u00faltimo dia do m\u00eas anterior\n    return [ini, fim];\n  }\n  if (regra === \"semana_anterior\") {\n    // segunda desta semana \u2192 volta 7 dias = segunda passada\n    const segDestaSemana = addDias(hoje, -((hoje.getDay() + 6) % 7));\n    const segAnterior = addDias(segDestaSemana, -7);\n    const fim = SEMANA_ANTERIOR === \"seg_sex\" ? addDias(segAnterior, 4) : addDias(segAnterior, 6);\n    return [segAnterior, fim];\n  }\n  if (regra === \"mes_ate_ultima_sexta\" || regra === \"mes_ate_ultima_quarta\") {\n    const weekday = regra === \"mes_ate_ultima_sexta\" ? 5 : 3; // 5=sexta, 3=quarta\n    const dia1 = new Date(hoje.getFullYear(), hoje.getMonth(), 1);\n    const fim = ultimoDiaSemanaAntes(hoje, weekday); // ocorr\u00eancia mais recente antes de hoje\n    if (fim >= dia1) return [dia1, fim];             // caso normal: 1\u00ba do m\u00eas \u2192 \u00faltima sexta/quarta\n    // Borda (1\u00aa segunda/quinta do m\u00eas, sem essa data ainda no m\u00eas vigente):\n    // usa a semana que antecede hoje = \u00faltima semana do m\u00eas passado (seg \u2192 sexta/quarta).\n    const segDestaSemana = addDias(hoje, -((hoje.getDay() + 6) % 7));\n    return [addDias(segDestaSemana, -7), fim];\n  }\n  return [null, null];\n}\n\nfunction novoEnvio(lab_id, tipo, regra, hoje) {\n  const [ini, fim] = periodo(regra, hoje);\n  // Blindagem: per\u00edodo inv\u00e1lido (ex.: 1\u00aa segunda do m\u00eas, sem sexta ainda no m\u00eas vigente).\n  const valido = ini && fim && fim >= ini;\n  return {\n    id: `${lab_id}__T${tipo}`,            // chave p/ casar o manifesto do Hub com o e-mail\n    lab_id,\n    lab_nome: NOMES[lab_id],\n    tipo,\n    regra_periodo: regra,\n    periodo_ini: valido ? fmt(ini) : \"\",\n    periodo_fim: valido ? fmt(fim) : \"\",\n    valido,\n    emails: MODO_TESTE ? [EMAIL_TESTE] : (EMAILS[lab_id] || []),\n    bcc: MODO_TESTE ? [] : RESPONSAVEIS_BCC,\n    // refer\u00eancia: para quem iria em produ\u00e7\u00e3o (\u00fatil p/ conferir no teste)\n    emails_producao: EMAILS[lab_id] || [],\n  };\n}\n\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 main \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Aten\u00e7\u00e3o ao fuso: assume o servidor do n8n em America/Sao_Paulo.\n// \u2500\u2500 DISPARO MANUAL (Form Trigger \"Disparo Manual\") \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Se o workflow foi iniciado pelo formul\u00e1rio, chega um lab_id no input:\n// monta UM envio para o lab escolhido e ignora o calend\u00e1rio. Reusa os\n// mapas EMAILS/NOMES e RESPEITA o MODO_TESTE (mesma seguran\u00e7a do autom\u00e1tico).\n// Per\u00edodo em branco => m\u00eas anterior inteiro.\nlet _manual = null;\ntry {\n  const _itens = $input.all();\n  if (_itens.length && _itens[0].json && _itens[0].json.lab_id) _manual = _itens[0].json;\n} catch (_e) { _manual = null; }\n\nif (_manual) {\n  const hojeM = new Date();\n  const lab_id = String(_manual.lab_id || \"\").trim();\n  const conhecido = Object.prototype.hasOwnProperty.call(NOMES, lab_id);\n  let pini = String(_manual.periodo_ini || \"\").trim();\n  let pfim = String(_manual.periodo_fim || \"\").trim();\n  if (!pini && !pfim) {                       // vazio => m\u00eas anterior inteiro\n    const [a, b] = periodo(\"mes_anterior\", hojeM);\n    pini = fmt(a); pfim = fmt(b);\n  }\n  const reData = /^\\d{2}\\/\\d{2}\\/\\d{4}$/;\n  const valido = conhecido && reData.test(pini) && reData.test(pfim);\n  const envio = {\n    id: `${lab_id}__M`,\n    lab_id,\n    lab_nome: NOMES[lab_id] || lab_id,\n    tipo: \"M\",\n    regra_periodo: \"manual\",\n    periodo_ini: valido ? pini : \"\",\n    periodo_fim: valido ? pfim : \"\",\n    valido,\n    emails: MODO_TESTE ? [EMAIL_TESTE] : (EMAILS[lab_id] || []),\n    bcc: MODO_TESTE ? [] : RESPONSAVEIS_BCC,\n    emails_producao: EMAILS[lab_id] || [],\n  };\n  const enviosM = valido ? [envio] : [];\n  const ignoradosM = valido ? [] : [{ id: envio.id, motivo: conhecido ? \"periodo_invalido\" : \"lab_desconhecido\" }];\n  return [{\n    json: {\n      data_ref: fmt(hojeM),\n      modo_teste: MODO_TESTE,\n      forcar_teste: false,\n      disparo_manual: true,\n      email_avisos: EMAIL_AVISOS,\n      email_colega: EMAIL_COLEGA,\n      tem_disparo: enviosM.length > 0,\n      tipos: { manual: true },\n      qtd_envios: enviosM.length,\n      envios: enviosM,\n      ignorados: ignoradosM,\n    },\n  }];\n}\n\nconst hoje = new Date();\nconst w = hoje.getDay(); // 0=Dom..6=Sab\n\nconst ehTipo1 = REGRA_TIPO1 === \"dia_1\"\n  ? hoje.getDate() === 1\n  : fmt(hoje) === fmt(primeiroDiaUtilDoMes(hoje));\nconst ehTipo2 = w === 1; // segunda\nconst ehTipo3 = w === 4; // quinta\n\nconst candidatos = [];\nif (FORCAR_TESTE) {\n  // Teste manual do workflow: for\u00e7a TODOS os 12 labs (Tipo 1, m\u00eas anterior),\n  // independente do dia \u2014 serve para testar o fluxo inteiro de uma vez.\n  for (const lab of TIPO1_PRIMEIRO_DIA_UTIL) candidatos.push(novoEnvio(lab, 1, \"mes_anterior\", hoje));\n} else {\n  if (ehTipo1) for (const lab of TIPO1_PRIMEIRO_DIA_UTIL) candidatos.push(novoEnvio(lab, 1, \"mes_anterior\", hoje));\n  if (ehTipo2) for (const [lab, regra] of Object.entries(TIPO2_SEGUNDA)) candidatos.push(novoEnvio(lab, 2, regra, hoje));\n  if (ehTipo3) for (const [lab, regra] of Object.entries(TIPO3_QUINTA)) candidatos.push(novoEnvio(lab, 3, regra, hoje));\n}\n\n// S\u00f3 seguem os envios com per\u00edodo v\u00e1lido; os inv\u00e1lidos ficam reportados \u00e0 parte.\nconst envios = candidatos.filter((e) => e.valido);\nconst ignorados = candidatos.filter((e) => !e.valido).map((e) => ({ id: e.id, motivo: \"periodo_invalido\" }));\n\nreturn [{\n  json: {\n    data_ref: fmt(hoje),\n    modo_teste: MODO_TESTE,\n    forcar_teste: FORCAR_TESTE,\n    email_avisos: EMAIL_AVISOS,\n    email_colega: EMAIL_COLEGA,\n    tem_disparo: envios.length > 0,\n    tipos: { tipo1: ehTipo1, tipo2: ehTipo2, tipo3: ehTipo3 },\n    qtd_envios: envios.length,\n    envios,\n    ignorados,\n  },\n}];\n"
      },
      "id": "11800443-5204-47a7-9776-705e9eb220b8",
      "name": "Agenda",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -2736,
        464
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 1
          },
          "combinator": "and",
          "conditions": [
            {
              "leftValue": "={{ $json.tem_disparo }}",
              "rightValue": true,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            }
          ]
        },
        "options": {}
      },
      "id": "e938bc69-17c4-456a-8fc3-abbdd7810106",
      "name": "Tem disparo?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -2480,
        464
      ]
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "a1",
              "name": "tipo",
              "value": 1,
              "type": "number"
            },
            {
              "id": "a2",
              "name": "envios",
              "value": "={{ $json.envios }}",
              "type": "array"
            },
            {
              "id": "a3",
              "name": "email_avisos",
              "value": "={{ $json.email_avisos }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "id": "32c0d4f2-f86d-4469-bbfb-ddfbe7777bb5",
      "name": "Payload do rob\u00f4",
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -2128,
        464
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://192.168.210.215:5000/relatorios/fila/entrar",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Hub-Token",
              "value": "um_segredo_forte_qualquer_aqui"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"resume_url\": $execution.resumeUrl } }}",
        "options": {}
      },
      "id": "ecc8cb9e-2b51-4c25-9c40-b2f344d97b08",
      "name": "Fila Entrar",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -1872,
        464
      ]
    },
    {
      "parameters": {
        "resume": "webhook",
        "options": {}
      },
      "id": "9ef396e7-366b-44af-b857-fefe89f46f1c",
      "name": "Vez na fila",
      "type": "n8n-nodes-base.wait",
      "typeVersion": 1.1,
      "position": [
        -1616,
        464
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://192.168.210.215:5000/relatorios/iniciar",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Hub-Token",
              "value": "um_segredo_forte_qualquer_aqui"
            }
          ]
        },
        "sendBody": true,
        "specifyBody": "json",
        "jsonBody": "={{ { \"tipo\": $(\"Payload do rob\u00f4\").item.json.tipo, \"envios\": $(\"Payload do rob\u00f4\").item.json.envios } }}",
        "options": {
          "timeout": 600000
        }
      },
      "id": "494fd50f-8355-4248-bf65-30dfb14f6f4b",
      "name": "Iniciar Robo",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -1344,
        448
      ]
    },
    {
      "parameters": {
        "method": "POST",
        "url": "http://192.168.210.215:5000/relatorios/fila/liberar_proximo",
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Hub-Token",
              "value": "um_segredo_forte_qualquer_aqui"
            }
          ]
        },
        "options": {}
      },
      "id": "d0640b90-0b4d-4374-9f58-08e30ee604bf",
      "name": "Fila Liberar",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -1072,
        432
      ]
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "loose",
            "version": 1
          },
          "combinator": "and",
          "conditions": [
            {
              "leftValue": "={{ $(\"Iniciar Robo\").item.json.status }}",
              "rightValue": "sucesso",
              "operator": {
                "type": "string",
                "operation": "equals"
              }
            }
          ]
        },
        "options": {}
      },
      "id": "2015cb9b-3de1-4b48-963e-42ac2fecf47b",
      "name": "Rob\u00f4 OK?",
      "type": "n8n-nodes-base.if",
      "typeVersion": 2,
      "position": [
        -800,
        432
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// preparar_envios.js \u2014 Code node do n8n (p\u00f3s /relatorios/iniciar)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Junta o MANIFESTO do rob\u00f4 (itens: id, lab_id, arquivos) com os\n// ENVIOS da agenda (emails, per\u00edodo) pelo campo \"id\". Produz UM item\n// por laborat\u00f3rio, pronto para o loop de download + emailSend.\n//\n// Requisitos de nomes dos n\u00f3s (ajuste se os seus tiverem outro nome):\n//   \u2022 \"Agenda\"      \u2192 Code node agenda_relatorios.js\n//   \u2022 \"Iniciar Robo\"\u2192 HTTP Request que chamou POST /relatorios/iniciar\n//\n// Sa\u00edda (1 item por lab):\n//   { id, lab_id, lab_nome, to, bcc, subject, exec, arquivos: [...] }\n//   \u2192 o exec + cada \"arquivo\" alimentam GET /relatorios/download\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst agenda = $('Agenda').first().json;\nconst resp = $('Iniciar Robo').first().json || {};\nconst manifesto = resp.resultado || {};\nconst itens = manifesto.itens || [];\nconst exec = manifesto.exec || '';\n\n// Indexa os envios da agenda por id (para pegar emails/per\u00edodo de cada lab).\nconst porId = {};\nfor (const e of (agenda.envios || [])) porId[e.id] = e;\n\nconst saida = [];\nfor (const it of itens) {\n  const env = porId[it.id] || {};\n  const periodo = env.periodo_ini\n    ? `${env.periodo_ini} a ${env.periodo_fim}`\n    : 'estoque atual';\n  saida.push({\n    json: {\n      id: it.id,\n      lab_id: it.lab_id,\n      lab_nome: it.nome || env.lab_nome || it.lab_id,\n      to: (env.emails || []).join('; '),\n      bcc: (env.bcc || []).join('; '),\n      subject: `Relat\u00f3rio Vendas / Estoque \u2014 ${it.nome || env.lab_nome || it.lab_id} (${periodo})`,\n      exec,\n      arquivos: it.arquivos || [],\n    },\n  });\n}\n\nreturn saida;\n"
      },
      "id": "42fc0c74-e6cb-43b1-978a-a0bb9bdc1d0f",
      "name": "Preparar Envios",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -416,
        464
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// explodir_arquivos.js \u2014 Code node do n8n (antes do Download)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Recebe 1 item por laborat\u00f3rio (de \"Preparar Envios\") e emite 1 item\n// por ARQUIVO, carregando os metadados do e-mail. Cada item alimenta o\n// HTTP \"Download\": GET /relatorios/download?exec=<exec>&arquivo=<arquivo>\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\nconst saida = [];\nfor (const it of $input.all()) {\n  const j = it.json;\n  for (let idx = 0; idx < (j.arquivos || []).length; idx++) {\n    saida.push({\n      json: {\n        lab_id: j.lab_id,\n        lab_nome: j.lab_nome,\n        id: j.id,\n        to: j.to,\n        bcc: j.bcc,\n        subject: j.subject,\n        exec: j.exec,\n        arquivo: j.arquivos[idx],\n        idx,\n      },\n    });\n  }\n}\nreturn saida;\n"
      },
      "id": "e5182d1c-5a30-4daf-9479-6891ef493271",
      "name": "Arquivos do lab",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -144,
        464
      ]
    },
    {
      "parameters": {
        "url": "http://192.168.210.215:5000/relatorios/download",
        "sendQuery": true,
        "queryParameters": {
          "parameters": [
            {
              "name": "exec",
              "value": "={{ $json.exec }}"
            },
            {
              "name": "arquivo",
              "value": "={{ $json.arquivo }}"
            }
          ]
        },
        "sendHeaders": true,
        "headerParameters": {
          "parameters": [
            {
              "name": "X-Hub-Token",
              "value": "um_segredo_forte_qualquer_aqui"
            }
          ]
        },
        "options": {
          "response": {
            "response": {
              "responseFormat": "file"
            }
          }
        }
      },
      "id": "e225c028-1398-4184-b2d4-10dc6116ba30",
      "name": "Download",
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        128,
        464
      ]
    },
    {
      "parameters": {
        "jsCode": "// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n// juntar_binarios.js \u2014 Code node do n8n (depois do Download)\n// \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Reagrupa os arquivos baixados por laborat\u00f3rio: junta todos os\n// bin\u00e1rios de um mesmo lab num \u00daNICO item, para o emailSend mandar\n// um e-mail por lab com TODOS os anexos.\n//\n// Pareia cada download (na ordem) com o item correspondente do n\u00f3\n// \"Arquivos do lab\" (Split Out) \u2014 n8n preserva ordem e contagem. Assim os\n// metadados (to/bcc/subject) n\u00e3o dependem do HTTP repassar o json.\n// (Se voc\u00ea usar o atalho com o Code \"Explodir Arquivos\", troque o nome abaixo.)\n//\n// No emailSend, configure o campo \"Attachments\" com a express\u00e3o:\n//   {{ Object.keys($binary).join(',') }}\n// para anexar dinamicamente todos os bin\u00e1rios do item.\n// \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n\n// Logo: wordmark em TEXTO \"CIAMED\" (sem imagem) \u2014 n\u00e3o depende de host\n// p\u00fablico nem de anexo inline, aparece em qualquer cliente de e-mail.\n\nfunction montarHtml(g) {\n  const itens = (g.arquivos || [])\n    .map((a) => `<li style=\"margin:2px 0;\">${a}</li>`)\n    .join(\"\");\n  return `<!doctype html>\n<html><body style=\"margin:0;padding:0;background:#f4f6f8;font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#333;\">\n  <table role=\"presentation\" width=\"100%\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#f4f6f8;padding:24px 0;\">\n    <tr><td align=\"center\">\n      <table role=\"presentation\" width=\"600\" cellpadding=\"0\" cellspacing=\"0\" style=\"background:#ffffff;border-radius:8px;overflow:hidden;border:1px solid #e6e9ec;\">\n        <tr><td align=\"center\" style=\"background:#00B3B2;padding:20px 24px;\">\n          <div style=\"font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#ffffff;font-size:30px;font-weight:bold;letter-spacing:6px;line-height:1;\">CIAMED</div>\n          <div style=\"font-family:'Helvetica Neue',Helvetica,Arial,sans-serif;color:#e6f7f7;font-size:10px;letter-spacing:3px;text-transform:uppercase;margin-top:7px;\">Distribuidora de Medicamentos</div>\n        </td></tr>\n        <tr><td style=\"padding:28px 24px 8px;\">\n          <h2 style=\"margin:0 0 12px;font-size:18px;color:#00B3B2;\">Relat\u00f3rio de Vendas / Estoque</h2>\n          <p style=\"margin:0 0 12px;font-size:14px;line-height:1.5;\">Prezados(as),</p>\n          <p style=\"margin:0 0 16px;font-size:14px;line-height:1.5;\">\n            Segue em anexo o relat\u00f3rio referente a <strong>${g.lab_nome}</strong>.\n          </p>\n          <p style=\"margin:0 0 6px;font-size:13px;color:#6E6E6E;\">Arquivos anexados:</p>\n          <ul style=\"margin:0 0 16px;padding-left:20px;font-size:13px;color:#444;\">${itens}</ul>\n        </td></tr>\n        <tr><td style=\"padding:16px 24px 24px;border-top:1px solid #eee;\">\n          <p style=\"margin:0;font-size:12px;color:#8A8A8A;line-height:1.5;\">\n            Mensagem autom\u00e1tica do Hub de Automa\u00e7\u00f5es \u2014 Ciamed.<br>\n            Por favor, n\u00e3o responda a este e-mail.\n          </p>\n        </td></tr>\n      </table>\n    </td></tr>\n  </table>\n</body></html>`;\n}\n\nconst downloads = $input.all();\nconst explodidos = $('Arquivos do lab').all();\n\nconst grupos = {};\nfor (let i = 0; i < downloads.length; i++) {\n  const meta = explodidos[i].json;\n  const labId = meta.lab_id;\n\n  if (!grupos[labId]) {\n    grupos[labId] = {\n      json: {\n        lab_id: labId,\n        lab_nome: meta.lab_nome,\n        to: meta.to,\n        bcc: meta.bcc,\n        subject: meta.subject,\n        arquivos: [],\n      },\n      binary: {},\n    };\n  }\n\n  const g = grupos[labId];\n  const bin = downloads[i].binary || {};\n  for (const k of Object.keys(bin)) {\n    const nome = `anexo_${Object.keys(g.binary).length}`;\n    g.binary[nome] = bin[k];\n    g.json.arquivos.push(meta.arquivo);\n  }\n}\n\n// Monta o corpo HTML (com a lista de anexos completa) para cada lab.\nconst saida = Object.values(grupos);\nfor (const g of saida) {\n  g.json.html = montarHtml(g.json);\n}\nreturn saida;\n"
      },
      "id": "65f1102d-cbc7-4714-86e2-a9dcedd6a09e",
      "name": "Juntar Bin\u00e1rios",
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        384,
        432
      ]
    },
    {
      "parameters": {
        "rules": {
          "values": [
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "bayer",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "62e2279c-3adf-4849-8fa3-cbdf3b089915"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Bayer"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "csl",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "ebf4c998-c9b3-4e0f-aeca-3c39f45b5def"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "CSL"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "fresenius",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "4ab27062-68ff-4e03-ada3-fcf60a5ed9d0"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Fresenius"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "glaxo",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "2d24ff53-a7f2-4699-bacb-2376e57af936"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Glaxo"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "organon",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "691e6f69-f9cf-4680-ba0e-dd52a783ab3e"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Organon"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "roche",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "12c5c2a7-3802-429e-8106-ae0c96e89006"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Roche"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "sandoz_rs_sc",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "c76d03cc-6426-42d8-b54d-66f3b092fdb4"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Sandoz RS-SC"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "sandoz_sp",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "6e424385-2ce9-4fb3-945e-d4b76d75ebf8"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Sandoz SP"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "sankyo",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "b3cac0d0-6155-4e75-bd80-8575cabd0d56"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Sankyo"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "sun_geral",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "e29d518d-4f12-4211-ac8e-aeb6405ed06e"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Sun Geral"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "sun_13082",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "ac7910ff-eb92-4ca7-829e-41ce88c920e0"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Sun 13082"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "united",
                    "operator": {
                      "type": "string",
                      "operation": "equals"
                    },
                    "id": "9b1eda72-831f-46bb-87cb-118c4d1cd6c9"
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "United"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "21cefd73-2ccd-4bcd-bfe1-2145746af6aa",
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "apsen",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Apsen"
            },
            {
              "conditions": {
                "options": {
                  "caseSensitive": true,
                  "leftValue": "",
                  "typeValidation": "strict",
                  "version": 2
                },
                "conditions": [
                  {
                    "id": "e2f2700e-da3f-4206-8ac0-516f6fd78cb7",
                    "leftValue": "={{ $json.lab_id }}",
                    "rightValue": "mappel_quifa",
                    "operator": {
                      "type": "string",
                      "operation": "equals",
                      "name": "filter.operator.equals"
                    }
                  }
                ],
                "combinator": "and"
              },
              "renameOutput": true,
              "outputKey": "Mappel / Quifa"
            }
          ]
        },
        "options": {}
      },
      "id": "30541f61-b182-4f26-9a98-2858c0ef1158",
      "name": "Rotear por lab",
      "type": "n8n-nodes-base.switch",
      "typeVersion": 3.2,
      "position": [
        704,
        960
      ]
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "7f777b99-d7e0-48ad-8ef0-fb3692d8ab32",
      "name": "Gmail: Bayer",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        96
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 Bayer",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "94ef077b-c782-40dc-8f70-ea80fccfa393",
      "name": "Aviso: Bayer",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        96
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "a83ecd3e-f69a-4251-951b-726e2a715b88",
      "name": "Gmail: CSL",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        256
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 CSL",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "5f2cabb2-ec9a-492b-855e-d76aac4ea152",
      "name": "Aviso: CSL",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        256
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "2a873ff3-2ea5-41e9-9261-2ffb950eb32d",
      "name": "Gmail: Fresenius",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        400
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 Fresenius",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "5f2ee876-2c76-402d-843e-ae67653118b3",
      "name": "Aviso: Fresenius",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        400
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "52443727-17d6-4880-b924-4093f992349f",
      "name": "Gmail: Glaxo",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        560
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 Glaxo",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "5acd62b3-2fc1-4b69-beeb-1dbf6b9386c8",
      "name": "Aviso: Glaxo",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        560
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "e4d83f44-7950-4dcf-8cff-f1759b437fc9",
      "name": "Gmail: Organon",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        704
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 Organon",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "4f2a809c-6951-4e68-bab9-19852d7fdbe8",
      "name": "Aviso: Organon",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        704
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "7e3f900e-9c0a-4c94-ae4d-1ce86a3e84a3",
      "name": "Gmail: Roche",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        848
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 Roche",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "cab1c753-f862-42f2-b109-6783eb71c5b9",
      "name": "Aviso: Roche",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        848
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "d9ef603b-4965-4899-8bc1-fed2eada7d9d",
      "name": "Gmail: Sandoz RS-SC",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        1008
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 Sandoz RS-SC",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "9119c91b-2f9d-489f-9bf4-94885a73a1ae",
      "name": "Aviso: Sandoz RS-SC",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        1008
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      }
    },
    {
      "parameters": {
        "sendTo": "={{ $json.to }}",
        "subject": "={{ $json.subject }}",
        "message": "={{ $json.html }}",
        "options": {
          "appendAttribution": false,
          "attachmentsUi": {
            "attachmentsBinary": [
              {
                "property": "={{ Object.keys($binary).join(',') }}"
              }
            ]
          },
          "bccList": "={{ $json.bcc }}"
        }
      },
      "id": "beaa13ae-8eac-4af8-ab78-6181bc672c56",
      "name": "Gmail: Sandoz SP",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1008,
        1168
      ],
      "credentials": {
        "gmailOAuth2": {
          "name": "<your credential>"
        }
      },
      "onError": "continueErrorOutput"
    },
    {
      "parameters": {
        "sendTo": "={{ $(\"Agenda\").first().json.email_colega }}",
        "subject": "={{ $json.error ? '[FALHA] ' : '[OK] ' }}Relat\u00f3rio \u2014 Sandoz SP",
        "emailType": "text",
        "message": "={{ $json.error\n  ? ('\u274c Erro ao enviar o relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ': ' + ($json.error.message || JSON.stringify($json.error)))\n  : ('\u2705 Relat\u00f3rio de ' + $('Juntar Bin\u00e1rios').item.json.lab_nome + ' enviado com sucesso aos destinat\u00e1rios.\\n\\n'\n     + '\ud83d\udcce Com dados: ' + ($('Juntar Bin\u00e1rios').item.json.arquivos || []).join(', ') + '\\n'\n     + (($('Juntar Bin\u00e1rios').item.json.vazios || []).length\n        ? '\u26a0\ufe0f SEM dados (n\u00e3o anexado): ' + $('Juntar Bin\u00e1rios').item.json.vazios.join(', ')\n        : '\u2714\ufe0f Todos os relat\u00f3rios vieram com dados.')) }}",
        "options": {
          "appendAttribution": false
        }
      },
      "id": "1bdd06df-455a-4dc3-9969-7a1ae56ce22e",
      "name": "Aviso: Sandoz SP",
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1344,
        1168
      ],
      "credentials": {
        "gmailOAuth2": {
          "nam

Credentials you'll need

Each integration node will prompt for credentials when you import. We strip credential IDs before publishing — you'll add your own.

Pro

For the full experience including quality scoring and batch install features for each workflow upgrade to Pro

About this workflow

Relatórios aos Laboratórios. Uses httpRequest, gmail, formTrigger. Scheduled trigger; 48 nodes.

Source: https://github.com/oleodias/hub-automacoes/blob/4ffb01928535eb473fb713d92f7baab695aa482b/n8n/fluxo_relatorios.json — original creator credit. Request a take-down →

More Email & Gmail workflows → · Browse all categories →

Related workflows

Workflows that share integrations, category, or trigger type with this one. All free to copy and import.

Email & Gmail

Relatórios aos Laboratórios. Uses httpRequest, gmail, formTrigger. Scheduled trigger; 54 nodes.

HTTP Request, Gmail, Form Trigger
Email & Gmail

HVACFlow - Smart HVAC Service Management System copy. Uses mongoDb, gmail, twilio, googleCalendar. Scheduled trigger; 42 nodes.

MongoDB, Gmail, Twilio +3
Email & Gmail

Zalando Price Patrol Monitor Price Evolution With Email Notification. Uses googleSheets, scheduleTrigger, httpRequest, gmail. Scheduled trigger; 14 nodes.

Google Sheets, HTTP Request, Gmail +1
Email & Gmail

🎉 Do you want to master AI automation, so you can save time and build cool stuff?

Google Sheets, HTTP Request, Gmail +1
Email & Gmail

Workflow 2212. Uses googleSheets, httpRequest, gmail, formTrigger. Scheduled trigger; 14 nodes.

Google Sheets, HTTP Request, Gmail +1