{"id":26,"date":"2026-08-13T03:27:24","date_gmt":"2026-08-13T03:27:24","guid":{"rendered":"https:\/\/techiando.com\/?p=26"},"modified":"2026-08-26T04:58:53","modified_gmt":"2026-08-26T04:58:53","slug":"sincronizar-google-sheets-calendario-iphone","status":"publish","type":"post","link":"https:\/\/techiando.com\/blog\/sincronizar-google-sheets-calendario-iphone\/","title":{"rendered":"Como sincronizar o Google Sheets com o Calend\u00e1rio do iPhone usando Apps Script"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Voc\u00ea j\u00e1 usa uma planilha para controlar suas contas, mas continua precisando abrir o arquivo para descobrir o que vence nos pr\u00f3ximos dias?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Foi exatamente esse problema que me levou a criar esta automa\u00e7\u00e3o.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Eu tinha uma planilha com minhas despesas, vencimentos, valores e status de pagamento. Ela funcionava bem para registrar e atualizar as informa\u00e7\u00f5es, mas n\u00e3o era o melhor lugar para consultar o que precisava ser pago.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A solu\u00e7\u00e3o foi transformar o Google Sheets na fonte dos dados e o Google Calendar na interface para acompanhar os vencimentos.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O resultado \u00e9 que hoje consigo simplesmente olhar o widget do Calend\u00e1rio no iPhone \u2014 ou perguntar \u00e0 Siri \u2014 quais s\u00e3o os pr\u00f3ximos vencimentos.<\/p>\n\n\n\n<div class=\"wp-block-group is-style-tk-callout-nota has-global-padding is-layout-constrained wp-block-group-is-layout-constrained\">\n<p class=\"has-small-font-size wp-block-paragraph\"><strong>Nota<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">As imagens e valores utilizados neste artigo representam um projeto real testado. Informa\u00e7\u00f5es pessoais podem ser ocultadas ou alteradas antes da publica\u00e7\u00e3o.<\/p>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">O problema: a planilha funciona, mas n\u00e3o \u00e9 onde eu quero consultar minhas contas<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Minha planilha possui basicamente quatro informa\u00e7\u00f5es para cada despesa:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Informa\u00e7\u00e3o<\/th><th>Exemplo<\/th><\/tr><\/thead><tbody><tr><td>Item<\/td><td>Faculdade<\/td><\/tr><tr><td>Vencimento<\/td><td>12\/08\/2026<\/td><\/tr><tr><td>Valor<\/td><td>R$ 260,00<\/td><\/tr><tr><td>Status<\/td><td>A VENCER<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Ela \u00e9 excelente para gerenciar as contas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Posso alterar o status de uma despesa quando ela \u00e9 paga, adicionar uma nova conta ou alterar um vencimento.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O problema \u00e9 outro: eu n\u00e3o queria precisar abrir a planilha toda vez que quisesse saber o que estava pr\u00f3ximo de vencer.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"730\" src=\"https:\/\/techiando.com\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-1024x730.jpg\" alt=\"Detalhe da planilha financeira usada como fonte de dados da sincroniza\u00e7\u00e3o\" class=\"wp-image-21\" srcset=\"https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-1024x730.jpg 1024w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-300x214.jpg 300w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-768x547.jpg 768w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira.jpg 1344w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">A ideia: deixar o calend\u00e1rio cuidar dos lembretes<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Calend\u00e1rio \u00e9 uma ferramenta que eu j\u00e1 consulto naturalmente durante o dia.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ent\u00e3o, em vez de criar mais um aplicativo ou uma interface para acompanhar as contas, a ideia foi simples:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Planilha \u2192 Apps Script \u2192 Google Calendar \u2192 Calend\u00e1rio do iPhone<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A planilha continua sendo respons\u00e1vel pelos dados.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O calend\u00e1rio passa a ser respons\u00e1vel pela visualiza\u00e7\u00e3o dos pr\u00f3ximos vencimentos e pelos alertas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">E o melhor: o Google Calendar sincroniza com o aplicativo Calend\u00e1rio do iPhone.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Como funciona a automa\u00e7\u00e3o<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A automa\u00e7\u00e3o foi feita utilizando o Google Apps Script, que permite executar JavaScript integrado aos servi\u00e7os do Google.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O fluxo ficou assim:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Google Sheets\n      \u2193\nApps Script\n      \u2193\nGoogle Calendar\n      \u2193\nCalend\u00e1rio do iPhone\n      \u2193\nWidget \/ Siri \/ notifica\u00e7\u00f5es<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O script l\u00ea cada linha da planilha, verifica se j\u00e1 existe um evento correspondente e:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>cria o evento se ele ainda n\u00e3o existir;<\/li>\n\n\n\n<li>atualiza o evento se ele j\u00e1 existir;<\/li>\n\n\n\n<li>evita eventos duplicados;<\/li>\n\n\n\n<li>mant\u00e9m um identificador \u00fanico para cada despesa;<\/li>\n\n\n\n<li>coloca o valor e o status dentro do evento;<\/li>\n\n\n\n<li>configura um lembrete para um dia antes do vencimento.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">1. Prepare sua planilha<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">No projeto utilizado neste artigo:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>E \u2192 ITEM\nF \u2192 VENCIMENTO\nG \u2192 VALOR\nH \u2192 STATUS\nI \u2192 ID_CALENDAR<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Exemplo:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>ITEM<\/th><th>VENCIMENTO<\/th><th>VALOR<\/th><th>STATUS<\/th><\/tr><\/thead><tbody><tr><td>Faculdade<\/td><td>12\/08\/2026<\/td><td>R$ 260,00<\/td><td>A VENCER<\/td><\/tr><tr><td>Seguro<\/td><td>13\/08\/2026<\/td><td>R$ 235,00<\/td><td>A VENCER<\/td><\/tr><tr><td>Condom\u00ednio<\/td><td>15\/08\/2026<\/td><td>R$ 150,00<\/td><td>A VENCER<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">A coluna <code>ID_CALENDAR<\/code> \u00e9 utilizada para identificar permanentemente cada despesa.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"730\" src=\"https:\/\/techiando.com\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-1024x730.jpg\" alt=\"Detalhe da planilha financeira usada como fonte de dados da sincroniza\u00e7\u00e3o\" class=\"wp-image-21\" srcset=\"https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-1024x730.jpg 1024w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-300x214.jpg 300w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira-768x547.jpg 768w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/01-planilha-financeira.jpg 1344w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">2. Crie o Apps Script<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Na planilha, acesse:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Extens\u00f5es \u2192 Apps Script<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Crie o projeto e cole o c\u00f3digo completo abaixo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O script cria automaticamente um menu chamado <strong>\ud83d\udcc5 Calend\u00e1rio Financeiro<\/strong>, com op\u00e7\u00f5es como:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\ud83d\udd04 Sincronizar agora<\/li>\n\n\n\n<li>\ud83e\uddf9 Limpar duplicados<\/li>\n\n\n\n<li>\ud83d\udd0e Diagnosticar planilha<\/li>\n\n\n\n<li>\ud83d\udccb Ver calend\u00e1rio configurado<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"518\" src=\"https:\/\/techiando.com\/wp-content\/uploads\/2026\/08\/02-apps-script-editor-1024x518.png\" alt=\"Editor do Google Apps Script com o projeto Sincroniza\u00e7\u00e3o Financeira\" class=\"wp-image-22\" srcset=\"https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/02-apps-script-editor-1024x518.png 1024w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/02-apps-script-editor-300x152.png 300w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/02-apps-script-editor-768x389.png 768w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/02-apps-script-editor-1536x777.png 1536w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/02-apps-script-editor-2048x1036.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">C\u00f3digo completo (Apps Script)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>\/*******************************************************\n * SINCRONIZA\u00c7\u00c3O DE DESPESAS COM GOOGLE CALENDAR\n *\n * E = ITEM\n * F = VENCIMENTO\n * G = VALOR\n * H = STATUS\n * I = ID_CALENDAR\n *******************************************************\/\n\nconst CONFIG = {\n  CALENDAR_NAME: '\ud83d\udcb0 Vencimentos Financeiros',\n\n  ITEM_COL: 5,\n  VENCIMENTO_COL: 6,\n  VALOR_COL: 7,\n  STATUS_COL: 8,\n  ID_COL: 9,\n\n  FIRST_DATA_ROW: 2,\n\n  REMINDER_DAYS_BEFORE: 1,\n\n  EVENT_PREFIX: '\ud83d\udcb0 Vencimento \u2014 ',\n\n  EVENT_ID_PREFIX: 'ID_CALENDAR: ',\n\n  OLD_EVENT_ID_PREFIX: 'ID_DESPESA: '\n};\n\nfunction onOpen() {\n  SpreadsheetApp\n    .getUi()\n    .createMenu('\ud83d\udcc5 Calend\u00e1rio Financeiro')\n    .addItem('\ud83d\udd04 Sincronizar agora', 'sincronizarDespesas')\n    .addItem('\ud83e\uddf9 Limpar duplicados', 'limparEventosDuplicados')\n    .addItem('\ud83d\udd0e Diagnosticar planilha', 'diagnosticarPlanilha')\n    .addItem('\ud83d\udccb Ver calend\u00e1rio configurado', 'mostrarCalendario')\n    .addToUi();\n}\n\nfunction sincronizarDespesas() {\n  const ss = SpreadsheetApp.getActiveSpreadsheet();\n  const calendar = obterOuCriarCalendario();\n  const sheets = ss.getSheets();\n\n  let totalCriados = 0;\n  let totalAtualizados = 0;\n  let totalIgnorados = 0;\n  let totalEncontrados = 0;\n  let totalDuplicadosRemovidos = 0;\n  let totalIdsCriados = 0;\n\n  sheets.forEach(function(sheet) {\n    const nomeAba = sheet.getName();\n    const ultimaLinha = sheet.getLastRow();\n\n    if (ultimaLinha &lt; CONFIG.FIRST_DATA_ROW) {\n      return;\n    }\n\n    const quantidadeLinhas =\n      ultimaLinha - CONFIG.FIRST_DATA_ROW + 1;\n\n    const dados =\n      sheet\n        .getRange(\n          CONFIG.FIRST_DATA_ROW,\n          CONFIG.ITEM_COL,\n          quantidadeLinhas,\n          5\n        )\n        .getValues();\n\n    dados.forEach(function(linha, index) {\n      const linhaPlanilha =\n        CONFIG.FIRST_DATA_ROW + index;\n\n      const item = linha&#91;0];\n      const vencimento = linha&#91;1];\n      const valor = linha&#91;2];\n      const status = linha&#91;3];\n      let idCalendar = linha&#91;4];\n\n      if (\n        item === null ||\n        item === undefined ||\n        String(item).trim() === ''\n      ) {\n        totalIgnorados++;\n        return;\n      }\n\n      if (\n        String(item)\n          .trim()\n          .toUpperCase() === 'TOTAL'\n      ) {\n        totalIgnorados++;\n        return;\n      }\n\n      if (\n        !vencimento ||\n        !(vencimento instanceof Date) ||\n        isNaN(vencimento.getTime())\n      ) {\n        totalIgnorados++;\n        return;\n      }\n\n      totalEncontrados++;\n\n      const idAntigo =\n        criarIdDespesaAntigo(\n          nomeAba,\n          linhaPlanilha,\n          item\n        );\n\n      if (\n        idCalendar === null ||\n        idCalendar === undefined ||\n        String(idCalendar).trim() === ''\n      ) {\n        idCalendar = Utilities.getUuid();\n\n        sheet\n          .getRange(\n            linhaPlanilha,\n            CONFIG.ID_COL\n          )\n          .setValue(idCalendar);\n\n        totalIdsCriados++;\n      } else {\n        idCalendar = String(idCalendar).trim();\n      }\n\n      const eventos =\n        buscarEventosNaData(\n          calendar,\n          vencimento\n        );\n\n      let eventosEncontrados =\n        eventos.filter(function(evento) {\n          const descricao =\n            evento.getDescription() || '';\n\n          const id =\n            extrairId(\n              descricao,\n              CONFIG.EVENT_ID_PREFIX\n            );\n\n          return id === idCalendar;\n        });\n\n      if (eventosEncontrados.length === 0) {\n        eventosEncontrados =\n          eventos.filter(function(evento) {\n            const descricao =\n              evento.getDescription() || '';\n\n            const id =\n              extrairId(\n                descricao,\n                CONFIG.OLD_EVENT_ID_PREFIX\n              );\n\n            return id === idAntigo;\n          });\n      }\n\n      if (eventosEncontrados.length &gt; 0) {\n        const eventoPrincipal =\n          eventosEncontrados&#91;0];\n\n        if (eventosEncontrados.length &gt; 1) {\n          for (\n            let i = 1;\n            i &lt; eventosEncontrados.length;\n            i++\n          ) {\n            try {\n              eventosEncontrados&#91;i]\n                .deleteEvent();\n\n              totalDuplicadosRemovidos++;\n            } catch (erro) {\n              Logger.log(\n                'Erro ao remover duplicado: ' +\n                erro\n              );\n            }\n          }\n        }\n\n        atualizarEvento(\n          eventoPrincipal,\n          item,\n          vencimento,\n          valor,\n          status,\n          nomeAba,\n          linhaPlanilha,\n          idCalendar\n        );\n\n        totalAtualizados++;\n      } else {\n        criarEvento(\n          calendar,\n          item,\n          vencimento,\n          valor,\n          status,\n          nomeAba,\n          linhaPlanilha,\n          idCalendar\n        );\n\n        totalCriados++;\n      }\n    });\n  });\n\n  SpreadsheetApp\n    .getUi()\n    .alert(\n      '\u2705 Sincroniza\u00e7\u00e3o conclu\u00edda!\\n\\n' +\n      '\ud83d\udcca Despesas encontradas: ' +\n      totalEncontrados +\n      '\\n\\n\ud83d\udfe2 Eventos criados: ' +\n      totalCriados +\n      '\\n\ud83d\udd04 Eventos atualizados: ' +\n      totalAtualizados +\n      '\\n\ud83e\uddf9 Duplicados removidos: ' +\n      totalDuplicadosRemovidos +\n      '\\n\ud83c\udd94 IDs criados: ' +\n      totalIdsCriados +\n      '\\n\u26aa Linhas ignoradas: ' +\n      totalIgnorados\n    );\n}\n\nfunction buscarEventosNaData(calendar, data) {\n  const inicio = new Date(data);\n  inicio.setHours(0, 0, 0, 0);\n\n  const fim = new Date(data);\n  fim.setHours(23, 59, 59, 999);\n\n  return calendar.getEvents(\n    inicio,\n    fim\n  );\n}\n\nfunction obterOuCriarCalendario() {\n  const calendars =\n    CalendarApp.getCalendarsByName(\n      CONFIG.CALENDAR_NAME\n    );\n\n  if (calendars.length &gt; 0) {\n    return calendars&#91;0];\n  }\n\n  return CalendarApp.createCalendar(\n    CONFIG.CALENDAR_NAME\n  );\n}\n\nfunction criarIdDespesaAntigo(\n  nomeAba,\n  linha,\n  item\n) {\n  return (\n    nomeAba +\n    '|' +\n    linha +\n    '|' +\n    String(item).trim()\n  );\n}\n\nfunction extrairId(\n  descricao,\n  prefixo\n) {\n  const linhas =\n    String(descricao || '')\n      .split('\\n');\n\n  for (\n    let i = 0;\n    i &lt; linhas.length;\n    i++\n  ) {\n    const linha =\n      linhas&#91;i].trim();\n\n    if (\n      linha.indexOf(prefixo) === 0\n    ) {\n      return linha\n        .substring(prefixo.length)\n        .trim();\n    }\n  }\n\n  return null;\n}\n\nfunction criarEvento(\n  calendar,\n  item,\n  vencimento,\n  valor,\n  status,\n  nomeAba,\n  linhaPlanilha,\n  idCalendar\n) {\n  const titulo =\n    CONFIG.EVENT_PREFIX +\n    String(item).trim();\n\n  const descricao =\n    criarDescricao(\n      item,\n      valor,\n      status,\n      nomeAba,\n      linhaPlanilha,\n      idCalendar\n    );\n\n  const evento =\n    calendar.createAllDayEvent(\n      titulo,\n      vencimento,\n      {\n        description: descricao\n      }\n    );\n\n  evento.addPopupReminder(\n    CONFIG.REMINDER_DAYS_BEFORE *\n    24 *\n    60\n  );\n\n  return evento;\n}\n\nfunction atualizarEvento(\n  evento,\n  item,\n  vencimento,\n  valor,\n  status,\n  nomeAba,\n  linhaPlanilha,\n  idCalendar\n) {\n  evento.setTitle(\n    CONFIG.EVENT_PREFIX +\n    String(item).trim()\n  );\n\n  evento.setDescription(\n    criarDescricao(\n      item,\n      valor,\n      status,\n      nomeAba,\n      linhaPlanilha,\n      idCalendar\n    )\n  );\n\n  evento.setAllDayDate(\n    vencimento\n  );\n\n  evento.removeAllReminders();\n\n  evento.addPopupReminder(\n    CONFIG.REMINDER_DAYS_BEFORE *\n    24 *\n    60\n  );\n}\n\nfunction criarDescricao(\n  item,\n  valor,\n  status,\n  nomeAba,\n  linhaPlanilha,\n  idCalendar\n) {\n  let valorFormatado;\n\n  if (\n    typeof valor === 'number'\n  ) {\n    valorFormatado =\n      valor.toLocaleString(\n        'pt-BR',\n        {\n          style: 'currency',\n          currency: 'BRL'\n        }\n      );\n  } else {\n    valorFormatado =\n      String(valor || '');\n  }\n\n  return (\n    '\ud83d\udcb0 DESPESA FINANCEIRA\\n\\n' +\n    'Item: ' +\n    item +\n    '\\nValor: ' +\n    valorFormatado +\n    '\\nStatus: ' +\n    (status || 'A VENCER') +\n    '\\n\\nOrigem:\\n' +\n    'Aba: ' +\n    nomeAba +\n    '\\nLinha: ' +\n    linhaPlanilha +\n    '\\n\\n' +\n    CONFIG.EVENT_ID_PREFIX +\n    idCalendar\n  );\n}\n\nfunction limparEventosDuplicados() {\n  const calendar =\n    obterOuCriarCalendario();\n\n  const hoje =\n    new Date();\n\n  const inicio =\n    new Date(hoje);\n\n  inicio.setMonth(\n    inicio.getMonth() - 12\n  );\n\n  const fim =\n    new Date(hoje);\n\n  fim.setMonth(\n    fim.getMonth() + 24\n  );\n\n  const eventos =\n    calendar.getEvents(\n      inicio,\n      fim\n    );\n\n  const grupos = {};\n\n  eventos.forEach(function(evento) {\n    const descricao =\n      evento.getDescription() || '';\n\n    const id =\n      extrairId(\n        descricao,\n        CONFIG.EVENT_ID_PREFIX\n      );\n\n    if (!id) {\n      return;\n    }\n\n    if (!grupos&#91;id]) {\n      grupos&#91;id] = &#91;];\n    }\n\n    grupos&#91;id].push(evento);\n  });\n\n  let removidos = 0;\n\n  Object.keys(grupos).forEach(function(id) {\n    const grupo =\n      grupos&#91;id];\n\n    if (grupo.length &lt;= 1) {\n      return;\n    }\n\n    for (\n      let i = 1;\n      i &lt; grupo.length;\n      i++\n    ) {\n      try {\n        grupo&#91;i].deleteEvent();\n        removidos++;\n      } catch (erro) {\n        Logger.log(erro);\n      }\n    }\n  });\n\n  SpreadsheetApp\n    .getUi()\n    .alert(\n      '\ud83e\uddf9 Limpeza conclu\u00edda!\\n\\n' +\n      'Duplicados removidos: ' +\n      removidos\n    );\n}\n\nfunction diagnosticarPlanilha() {\n  const ss =\n    SpreadsheetApp\n      .getActiveSpreadsheet();\n\n  const sheets =\n    ss.getSheets();\n\n  let resultado =\n    'DIAGN\u00d3STICO DA PLANILHA\\n\\n';\n\n  sheets.forEach(function(sheet) {\n    const nome =\n      sheet.getName();\n\n    const ultimaLinha =\n      sheet.getLastRow();\n\n    resultado +=\n      'ABA: ' +\n      nome +\n      '\\n';\n\n    resultado +=\n      '\u00daltima linha: ' +\n      ultimaLinha +\n      '\\n';\n\n    if (\n      ultimaLinha &gt;=\n      CONFIG.FIRST_DATA_ROW\n    ) {\n      const quantidade =\n        Math.min(\n          ultimaLinha,\n          10\n        ) -\n        CONFIG.FIRST_DATA_ROW +\n        1;\n\n      const dados =\n        sheet\n          .getRange(\n            CONFIG.FIRST_DATA_ROW,\n            CONFIG.ITEM_COL,\n            quantidade,\n            5\n          )\n          .getValues();\n\n      dados.forEach(function(linha, i) {\n        resultado +=\n          'Linha ' +\n          (\n            i +\n            CONFIG.FIRST_DATA_ROW\n          ) +\n          ': ITEM=' +\n          linha&#91;0] +\n          ' | DATA=' +\n          linha&#91;1] +\n          ' | VALOR=' +\n          linha&#91;2] +\n          ' | STATUS=' +\n          linha&#91;3] +\n          ' | ID=' +\n          linha&#91;4] +\n          '\\n';\n      });\n    }\n\n    resultado +=\n      '\\n----------------------\\n\\n';\n  });\n\n  Logger.log(resultado);\n\n  SpreadsheetApp\n    .getUi()\n    .alert(\n      resultado.substring(\n        0,\n        5000\n      )\n    );\n}\n\nfunction mostrarCalendario() {\n  const calendars =\n    CalendarApp.getCalendarsByName(\n      CONFIG.CALENDAR_NAME\n    );\n\n  if (\n    calendars.length === 0\n  ) {\n    SpreadsheetApp\n      .getUi()\n      .alert(\n        'Calend\u00e1rio n\u00e3o encontrado.'\n      );\n\n    return;\n  }\n\n  SpreadsheetApp\n    .getUi()\n    .alert(\n      'Calend\u00e1rio encontrado:\\n\\n' +\n      calendars&#91;0].getName()\n    );\n}\n<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">3. O calend\u00e1rio \u00e9 criado automaticamente<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Uma das partes da implementa\u00e7\u00e3o \u00e9 a cria\u00e7\u00e3o autom\u00e1tica do calend\u00e1rio.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O script procura por:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\ud83d\udcb0 Vencimentos Financeiros<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Se o calend\u00e1rio j\u00e1 existir, utiliza o existente. Caso contr\u00e1rio, cria um novo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">4. A primeira sincroniza\u00e7\u00e3o<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Depois de autorizar o Apps Script a acessar o Google Sheets e o Google Calendar, basta utilizar:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\ud83d\udcc5 Calend\u00e1rio Financeiro \u2192 \ud83d\udd04 Sincronizar agora<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O script percorre as abas e verifica os registros.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Linhas vazias e a linha <code>TOTAL<\/code> s\u00e3o ignoradas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Para cada despesa v\u00e1lida:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>se n\u00e3o existir evento correspondente, cria;<\/li>\n\n\n\n<li>se existir, atualiza.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">5. Como fica o evento<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O t\u00edtulo segue este padr\u00e3o:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\ud83d\udcb0 Vencimento \u2014 FACULDADE<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O evento \u00e9 criado como evento de dia inteiro.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A descri\u00e7\u00e3o cont\u00e9m o item, valor, status, origem na planilha e o identificador <code>ID_CALENDAR<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Al\u00e9m disso, o script configura um lembrete para um dia antes do vencimento.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"526\" src=\"https:\/\/techiando.com\/wp-content\/uploads\/2026\/08\/03-evento-google-calendar-1024x526.jpg\" alt=\"Google Calendar em vis\u00e3o semanal com eventos de vencimento sincronizados da planilha\" class=\"wp-image-23\" srcset=\"https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/03-evento-google-calendar-1024x526.jpg 1024w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/03-evento-google-calendar-300x154.jpg 300w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/03-evento-google-calendar-768x395.jpg 768w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/03-evento-google-calendar-1536x789.jpg 1536w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/03-evento-google-calendar-2048x1052.jpg 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">6. E finalmente: o iPhone<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Depois que o Google Calendar est\u00e1 sincronizado com o Calend\u00e1rio do iPhone, os vencimentos aparecem junto com os outros compromissos.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"471\" height=\"1024\" src=\"https:\/\/techiando.com\/wp-content\/uploads\/2026\/08\/04-iphone-siri-calendario-471x1024.png\" alt=\"Widget do Calend\u00e1rio no iPhone listando vencimentos como Faculdade, Seguro e Condom\u00ednio\" class=\"wp-image-24\" srcset=\"https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/04-iphone-siri-calendario-471x1024.png 471w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/04-iphone-siri-calendario-138x300.png 138w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/04-iphone-siri-calendario-768x1670.png 768w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/04-iphone-siri-calendario-706x1536.png 706w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/04-iphone-siri-calendario-942x2048.png 942w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/04-iphone-siri-calendario-scaled.png 1177w\" sizes=\"auto, (max-width: 471px) 100vw, 471px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"471\" height=\"1024\" src=\"https:\/\/techiando.com\/wp-content\/uploads\/2026\/08\/05-sync-result-471x1024.png\" alt=\"App Calend\u00e1rio do iPhone em vis\u00e3o mensal com os vencimentos sincronizados\" class=\"wp-image-25\" srcset=\"https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/05-sync-result-471x1024.png 471w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/05-sync-result-138x300.png 138w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/05-sync-result-768x1670.png 768w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/05-sync-result-706x1536.png 706w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/05-sync-result-942x2048.png 942w, https:\/\/techiando.com\/blog\/wp-content\/uploads\/2026\/08\/05-sync-result-scaled.png 1177w\" sizes=\"auto, (max-width: 471px) 100vw, 471px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Na pr\u00e1tica, posso olhar o widget e j\u00e1 saber o que est\u00e1 pr\u00f3ximo.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Tamb\u00e9m posso perguntar \u00e0 Siri quais eventos tenho nos pr\u00f3ximos dias.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Isso muda a experi\u00eancia: n\u00e3o preciso abrir a planilha para descobrir o que vence nesta semana.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">O que mudou no meu uso da planilha<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A planilha n\u00e3o deixou de ser necess\u00e1ria. Ela simplesmente deixou de ser minha interface principal para consultar vencimentos.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Para cadastrar ou alterar algo: <strong>Google Sheets<\/strong>.<\/li>\n\n\n\n<li>Para saber o que est\u00e1 vencendo: <strong>Calend\u00e1rio<\/strong>.<\/li>\n\n\n\n<li>Para receber lembretes: <strong>iPhone<\/strong>.<\/li>\n\n\n\n<li>Para consultar os pr\u00f3ximos compromissos por voz: <strong>Siri<\/strong>.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">E quando uma conta \u00e9 paga?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A planilha continua sendo a fonte de verdade.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Quando uma conta \u00e9 paga, altero:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>STATUS: PAGO<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">e sincronizo novamente.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">O evento correspondente \u00e9 atualizado e passa a mostrar o novo status.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Por que usar um ID?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Cada linha recebe um identificador \u00fanico com <code>Utilities.getUuid()<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Isso evita depender apenas do nome da despesa. Assim, mesmo que existam despesas com nomes semelhantes ou que um nome seja alterado, o script consegue relacionar o evento correto \u00e0 linha correta.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">E se aparecerem eventos duplicados?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">O script possui a op\u00e7\u00e3o <strong>\ud83e\uddf9 Limpar duplicados<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ela procura eventos que possuem o mesmo <code>ID_CALENDAR<\/code> e mant\u00e9m apenas um.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">O resultado<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A arquitetura final \u00e9:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Google Sheets\n      \u2193\nApps Script\n      \u2193\nGoogle Calendar\n      \u2193\niPhone\n \u251c\u2500\u2500 Widget\n \u251c\u2500\u2500 Alertas\n \u2514\u2500\u2500 Siri<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">O interessante \u00e9 que n\u00e3o foi necess\u00e1rio criar um aplicativo financeiro.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A planilha continua fazendo aquilo que faz bem. O Google Calendar cuida da agenda e o iPhone vira a camada de consulta e notifica\u00e7\u00f5es.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">No meu caso, isso resolveu exatamente o problema: parei de depender da planilha para lembrar das contas.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Agora uso uma ferramenta que j\u00e1 faz parte da minha rotina todos os dias: o calend\u00e1rio.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">O que voc\u00ea precisa<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Uma conta Google<\/li>\n\n\n\n<li>Google Sheets<\/li>\n\n\n\n<li>Google Calendar<\/li>\n\n\n\n<li>iPhone, caso queira usar Calend\u00e1rio\/Siri<\/li>\n\n\n\n<li>Google Apps Script<\/li>\n\n\n\n<li>A planilha que voc\u00ea j\u00e1 utiliza<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Para continuar<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Se voc\u00ea est\u00e1 montando outras automa\u00e7\u00f5es no ecossistema Google, explore tamb\u00e9m a categoria <a href=\"\/category\/automacao\/google-workspace\/\">Google Workspace<\/a> e a \u00e1rea de <a href=\"\/category\/tecnologia\/\">Tecnologia<\/a>. D\u00favidas ou corre\u00e7\u00f5es: <a href=\"\/contato\/\">envie feedback<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Aprenda a sincronizar uma planilha do Google Sheets com o Calend\u00e1rio do iPhone usando Google Apps Script e receba alertas dos seus vencimentos.<\/p>\n","protected":false},"author":2,"featured_media":20,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"techiando_formato":"tutorial","techiando_proxima_revisao":"2027-08-13","footnotes":""},"categories":[3,16],"tags":[37,39,38,35,36],"class_list":["post-26","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tecnologia","category-software","tag-apps-script","tag-automacao","tag-google-calendar","tag-google-sheets","tag-iphone"],"_links":{"self":[{"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/posts\/26","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/comments?post=26"}],"version-history":[{"count":4,"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/posts\/26\/revisions"}],"predecessor-version":[{"id":36,"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/posts\/26\/revisions\/36"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/media\/20"}],"wp:attachment":[{"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/media?parent=26"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/categories?post=26"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/techiando.com\/blog\/wp-json\/wp\/v2\/tags?post=26"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}