Relatório Financeiro Automático no E-mail com Apps Script e IA

Relatório Financeiro Automático no E-mail com Apps Script e IA

Nos posts anteriores, Como usar IA para planejar metas financeiras realistasAutomatizando o Planejamento de Metas com IA e Google Apps Script, você aprendeu a usar IA para definir metas financeiras realistas e a conectar sua planilha à API da OpenAI para gerar análises automáticas. Hoje fechamos o ciclo: vamos fazer o sistema enviar um relatório financeiro completo por e-mail, com as análises da IA, direto para a sua caixa de entrada — todo dia 1º do mês, sem precisar abrir o Google Sheets.

Fonte: https://br.freepik.com/

O que será implementado

Ao final deste tutorial, sua planilha irá apresentar três novas funcionalidades no menu 🤖 Análise IA:

  • Enviar relatório por e-mail — disparo manual com um clique
  • Ativar envio automático mensal — configura um trigger (gatilho) para rodar todo dia 1º do mês às 08h
  • Desativar envio automático — remove o trigger quando quiser pausar

O e-mail gerado inclui um resumo financeiro do mês, as três análises da IA (resumo, alertas e sugestões) e um indicador visual de status (🟢 Saudável, 🟡 Atenção ou 🔴 Crítico) baseado em regras que você mesmo define.

Pré-requisitos

  • O código dos posts anteriores já instalado no Apps Script da planilha
  • A chave da OpenAI já salva no PropertiesService
  • A planilha com pelo menos a aba IA_Analises preenchida (rode 🤖 Análise IA → Analisar minhas metas uma vez antes)

O código Apps Script

Com o editor de script aberto, inclua as seguintes linhas no trecho de configuração que se encontra no início do código:

const EMAIL_DESTINATARIO = Session.getActiveUser().getEmail(); // usa o e-mail do dono da planilha
const LIMITE_ALERTA_COMPROMETIMENTO = 80; // % da renda — acima disso dispara alerta crítico
const LIMITE_ALERTA_META = 50;            // % de cumprimento das metas — abaixo disso dispara alerta

Em seguida, substitua a função onOpen() por esta:

function onOpen() {
  SpreadsheetApp.getUi()
    .createMenu("🤖 Análise IA")
    .addItem("Analisar minhas metas", "analisarMetas")
    .addSeparator()
    .addItem("Enviar relatório por e-mail", "enviarRelatorioEmail")
    .addItem("Ativar envio automático mensal", "ativarTriggerMensal")
    .addItem("Desativar envio automático", "desativarTriggerMensal")
    .addToUi();
}

Ela acrescenta mais três itens no menu 🤖 Análise IA.  E, por fim, acrescente estas funções no final do script existente:

//  ENVIO MANUAL DO RELATÓRIO
function enviarRelatorioEmail() {
  const ss      = SpreadsheetApp.getActiveSpreadsheet();
  const analises = lerAbaComoObjeto(ss, "IA_Analises");
  const resumo   = lerAbaComoObjeto(ss, "Resumo_Mensal");
  const config   = lerAbaComoObjeto(ss, "Configurações");

  // Verifica se já existe análise gerada
  if (!analises["Resumo mensal"]) {
    SpreadsheetApp.getUi().alert("Nenhuma análise encontrada. Execute 'Analisar minhas metas' primeiro.");
    return;
  }

  const nivel  = avaliarNivelAlerta(resumo, ss);
  const html   = montarEmailHtml(config, analises, resumo, nivel);
  const assunto = montarAssunto(config, nivel);

  GmailApp.sendEmail(EMAIL_DESTINATARIO, assunto, "", { htmlBody: html });
  SpreadsheetApp.getUi().alert(`✅ Relatório enviado para ${EMAIL_DESTINATARIO}`);
}

//  FUNÇÃO CHAMADA PELO TRIGGER AUTOMÁTICO
function enviarRelatorioAutomatico() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();

  // Roda a análise de IA antes de enviar
  analisarMetas();

  const analises = lerAbaComoObjeto(ss, "IA_Analises");
  const resumo   = lerAbaComoObjeto(ss, "Resumo_Mensal");
  const config   = lerAbaComoObjeto(ss, "Configurações");

  const nivel  = avaliarNivelAlerta(resumo, ss);
  const html   = montarEmailHtml(config, analises, resumo, nivel);
  const assunto = montarAssunto(config, nivel);

  GmailApp.sendEmail(EMAIL_DESTINATARIO, assunto, "", { htmlBody: html });
}

//  AVALIAÇÃO DO NÍVEL DE ALERTA
function avaliarNivelAlerta(resumo, ss) {
  const comprometido = parseFloat(resumo["% da Renda Comprometida"]) || 0;
  const metas        = lerAbaComoTabela(ss, "Metas_Mensais");

  const metasComDesvio = metas.filter(m => {
    const ideal     = parseFloat(m["Valor ideal"]) || 0;
    const realizado = parseFloat(m["Valor realizado"]) || 0;
    return ideal > 0 && (realizado / ideal) * 100 < LIMITE_ALERTA_META;
  });

  if (comprometido >= LIMITE_ALERTA_COMPROMETIMENTO || metasComDesvio.length >= 2) return "CRÍTICO 🔴";
  if (comprometido >= 65 || metasComDesvio.length === 1) return "ATENÇÃO 🟡";
  return "SAUDÁVEL 🟢";
}

//  MONTAGEM DO ASSUNTO DO E-MAIL
function montarAssunto(config, nivel) {
  const periodo = config["Mês/Ano"] || "este mês";
  return `[${nivel}] Relatório de Metas Financeiras — ${periodo}`;
}

//  MONTAGEM DO HTML DO E-MAIL
function montarEmailHtml(config, analises, resumo, nivel) {
  const corNivel = nivel.includes("CRÍTICO") ? "#e74c3c"
                 : nivel.includes("ATENÇÃO")  ? "#f39c12"
                 : "#27ae60";

  return `
  <div style="font-family: Arial, sans-serif; max-width: 620px; margin: auto; color: #333;">

    <div style="background: ${corNivel}; padding: 20px; border-radius: 8px 8px 0 0;">
      <h2 style="color: #fff; margin: 0;">📊 Relatório de Metas Financeiras</h2>
      <p style="color: #fff; margin: 6px 0 0;">${config["Mês/Ano"] || ""} — Status: <strong>${nivel}</strong></p>
    </div>

    <div style="background: #f9f9f9; padding: 20px; border: 1px solid #ddd;">

      <h3 style="color: #555;">📌 Resumo do Mês</h3>
      <table style="width:100%; border-collapse: collapse; font-size: 14px;">
        <tr><td style="padding:6px; border-bottom:1px solid #eee;">Total de Receitas</td>
            <td style="padding:6px; border-bottom:1px solid #eee; font-weight:bold;">R$ ${resumo["Total de Receitas"] || 0}</td></tr>
        <tr><td style="padding:6px; border-bottom:1px solid #eee;">Total de Despesas</td>
            <td style="padding:6px; border-bottom:1px solid #eee; font-weight:bold;">R$ ${resumo["Total de Despesas"] || 0}</td></tr>
        <tr><td style="padding:6px; border-bottom:1px solid #eee;">Saldo do Mês</td>
            <td style="padding:6px; border-bottom:1px solid #eee; font-weight:bold;">R$ ${resumo["Saldo do Mês"] || 0}</td></tr>
        <tr><td style="padding:6px;">% da Renda Comprometida</td>
            <td style="padding:6px; font-weight:bold;">${resumo["% da Renda Comprometida"] || 0}</td></tr>
      </table>

      <h3 style="color: #555; margin-top: 24px;">🤖 Análise da IA</h3>

      <h4 style="margin-bottom: 4px;">Resumo mensal</h4>
      <p style="font-size: 14px; line-height: 1.6;">${analises["Resumo mensal"] || "—"}</p>

      <h4 style="margin-bottom: 4px; color: ${corNivel};">⚠️ Alertas</h4>
      <p style="font-size: 14px; line-height: 1.6;">${analises["Alertas"] || "Nenhum alerta identificado."}</p>

      <h4 style="margin-bottom: 4px; color: #2980b9;">💡 Sugestões</h4>
      <p style="font-size: 14px; line-height: 1.6;">${analises["Sugestões"] || "—"}</p>

    </div>

    <div style="background: #eee; padding: 12px 20px; border-radius: 0 0 8px 8px; font-size: 12px; color: #888;">
      Gerado automaticamente pela sua planilha de Metas Financeiras Inteligentes.
    </div>

  </div>
  `;
}

//  TRIGGER MENSAL — ATIVA (todo dia 1º às 08h)
function ativarTriggerMensal() {
  // Evita duplicação de triggers
  desativarTriggerMensal();

  ScriptApp.newTrigger("enviarRelatorioAutomatico")
    .timeBased()
    .onMonthDay(1)
    .atHour(8)
    .create();

  SpreadsheetApp.getUi().alert("✅ Envio automático ativado! Todo dia 1º às 08h você receberá o relatório.");
}

//  TRIGGER MENSAL — DESATIVA
function desativarTriggerMensal() {
  ScriptApp.getProjectTriggers()
    .filter(t => t.getHandlerFunction() === "enviarRelatorioAutomatico")
    .forEach(t => ScriptApp.deleteTrigger(t));

  // Só mostra alerta se chamado diretamente pelo menu
  const ui = SpreadsheetApp.getUi();
  try { ui.alert("🔕 Envio automático desativado."); } catch(e) {}
}

Como o nível de alerta é calculado

Antes de montar o e-mail, o código avalia dois critérios automaticamente:
  • Se o % da renda comprometida ultrapassa 80%, o status vai para 🔴 Crítico
  • Se duas ou mais metas mensais estão abaixo de 50% de cumprimento, também vai para 🔴 Crítico
  • Valores intermediários resultam em 🟡 Atenção
  • Tudo dentro do esperado resulta em 🟢 Saudável
Você pode ajustar esses valores limites nas constantes LIMITE_ALERTA_COMPROMETIMENTO e LIMITE_ALERTA_META no topo do código.

O e-mail em HTML

O método GmailApp.sendEmail() do Apps Script aceita o parâmetro htmlBody, o que permite enviar e-mails formatados com tabelas, cores e tipografia — sem depender de nenhum serviço externo. A cor do cabeçalho muda dinamicamente conforme o nível de alerta: vermelho para crítico, amarelo para atenção e verde para saudável.

Ativando o envio automático

Depois de colar o código e salvar, volte para a planilha. No menu 🤖 Análise IA, clique em Ativar envio automático mensal. O Apps Script vai registrar um trigger baseado em tempo que executa a função enviarRelatorioAutomatico todo dia 1º do mês às 08h.

Os parâmetros dia e hora do envio do e-mail podem ser definidos na área de configuração do script.

Importante: na primeira execução, o Google vai pedir autorização para que o script acesse seu Gmail. Isso é esperado — é o fluxo padrão de OAuth do Apps Script para escopos sensíveis.

Você pode verificar os triggers que estão ativos, a qualquer momento em Apps Script → Acionadores (ícone de relógio na barra lateral).

Resultado esperado

No dia 1º de cada mês você recebe um e-mail com:
  • O status do mês em destaque no assunto, por exemplo: [SAUDÁVEL 🟢] Relatório de Metas Financeiras — Fevereiro/2026
  • Tabela com os números do mês (receitas, despesas, saldo, % comprometido)
  • Os três blocos de análise da IA em linguagem natural
  • Rodapé identificando que foi gerado automaticamente pela planilha

Próximos passos possíveis

Com esse sistema rodando, a evolução natural é trabalhar com histórico e tendências: salvar cada análise mensal na aba Historico e, depois de alguns meses, pedir à IA que identifique padrões de comportamento financeiro ao longo do tempo — o que será tema de outro post no futuro.

Ficou com alguma dúvida na configuração do trigger ou na autorização do Gmail? Deixa nos comentários!

Comentários