Skip to main content
Fechamento é o marco que separa “dados em revisão” de “dados definitivos”. Sua integração deve respeitar esse marco para evitar divergência entre Pontua e sistemas downstream (folha, BI, contabilidade). Este guia mostra os padrões mais comuns para lidar com fechamento.

Cenário 1 — Sync só após fechamento

Use quando a integração não precisa de dados em tempo real e prioriza consistência (ex.: alimenta sistema de folha externa).
async function syncSeFechado(token, mes) {
  const headers = { Authorization: `Bearer ${token}` }

  // 1. Verifica fechamento do mês
  const { resultados } = await fetch(
    `https://api.pontua.com.br/fechamentos?` +
    `dataInicio=${mes}-01&dataFim=${mes}-31&limite=10`,
    { headers },
  ).then((r) => r.json())

  const fechamento = resultados.find((f) => f.status === 'CONCLUIDO')

  if (!fechamento) {
    console.log(`Mês ${mes} ainda não fechado — pulando sync.`)
    return null
  }

  // 2. Fechado — pode sync com confiança
  console.log(`Mês ${mes} fechado em ${fechamento.dataConclusao}, sync OK.`)
  return await sincronizarDadosDoMes(token, mes)
}

Cenário 2 — Sync com cursor de fechamento

Use quando você tem um job que processa vários meses ao longo do tempo. Evita reprocessar meses fechados que já sincronizou.
async function syncIncrementalComCursor(token) {
  const ultimoFechamentoSincronizado = await storage.get('pontua.ultimo_fechamento_sync')

  // 1. Listar fechamentos concluídos depois do último sync
  const params = new URLSearchParams({
    status: 'CONCLUIDO',
    dataInicio: ultimoFechamentoSincronizado || '2026-01-01',
    limite: '100',
  })

  const { resultados } = await fetch(
    `https://api.pontua.com.br/fechamentos?${params}`,
    { headers: { Authorization: `Bearer ${token}` } },
  ).then((r) => r.json())

  // 2. Processar em ordem cronológica
  const ordenados = resultados.sort((a, b) =>
    a.dataInicio.localeCompare(b.dataInicio)
  )

  for (const fechamento of ordenados) {
    console.log(`Sync ${fechamento.dataInicio} a ${fechamento.dataFim}`)
    await sincronizarDadosDoFechamento(token, fechamento)
    await storage.set('pontua.ultimo_fechamento_sync', fechamento.dataConclusao)
  }
}

Cenário 3 — Reabrir fechamento para correção

Caso raro mas inevitável: descobriu-se erro após fechamento.
Reabrir fechamento é evento auditável. Em fiscalização, reaberturas frequentes podem ser questionadas pelo MTE. Use com parcimônia e sempre documente o motivo.
async function reabrirFechamentoComMotivo(token, fechamentoId, motivo) {
  // 1. Reabrir
  await fetch(
    `https://api.pontua.com.br/fechamentos/cancelar/${fechamentoId}`,
    {
      method: 'PATCH',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        motivo,  // string descrevendo a razão
      }),
    },
  )

  // 2. Aplicar correção (ajuste, registro, etc.)
  // ...

  // 3. Re-fechar
  await fetch(
    `https://api.pontua.com.br/fechamentos/concluir/${fechamentoId}`,
    {
      method: 'PATCH',
      headers: { Authorization: `Bearer ${token}` },
    },
  )

  // 4. CRÍTICO: invalidar exports de folha já enviados
  // Notifica seu sistema downstream que o mês precisa re-importar
  await meuSistemaFolha.invalidarMes(fechamentoId)
}

Cenário 4 — Fluxo de ajuste em período fechado

Não reabra para cada ajuste — use o fluxo formal de Ajustes.
async function ajustarRetroativo(token, colaboradorId, dataHora, motivoId) {
  // 1. Solicitar ajuste (não cria registro direto)
  const solicitacao = await fetch(
    'https://api.pontua.com.br/ajuste-registro/solicitar',
    {
      method: 'POST',
      headers: {
        Authorization: `Bearer ${token}`,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        colaboradorId,
        dataHora,
        motivoId,
        observacao: 'Correção retroativa via integração',
      }),
    },
  )
  const { id: ajusteId } = await solicitacao.json()

  // 2. Pode ser aprovado por gestor pelo dashboard ou via API
  // (workflow depende do design da empresa)
  // PATCH /ajuste-registro/status com status: APROVADO

  // 3. Após aprovação, registro é criado e frequência recalculada
  // automaticamente. Sem precisar reabrir fechamento.

  return ajusteId
}

Verificar antes de cada operação sensível

Padrão defensivo: sempre verifique fechamento antes de operações que mudam dados:
async function validarSeAberto(token, data) {
  const { resultados } = await fetch(
    `https://api.pontua.com.br/fechamentos?` +
    `dataInicio=${data}&dataFim=${data}&limite=1`,
    { headers: { Authorization: `Bearer ${token}` } },
  ).then((r) => r.json())

  const fechado = resultados.some((f) => f.status === 'CONCLUIDO')

  if (fechado) {
    throw new Error(
      `Data ${data} está em fechamento concluído. ` +
      `Use fluxo de ajuste em vez de modificação direta.`
    )
  }
}

// Uso
await validarSeAberto(token, '2026-04-15')
await fetch('https://api.pontua.com.br/registro-ponto', { /* ... */ })

Notificações ao seu time

Considere alertar Slack/Email quando:
  • Fechamento foi concluído → tempo de exportar folha
  • Fechamento foi reaberto → invalidar exports já feitos
  • Mês passou data limite sem fechamento → cobrar RH
// Polling diário das 9h
async function alertasFechamento(token) {
  const hoje = new Date().toISOString().slice(0, 10)
  const mesAnterior = new Date(Date.now() - 30 * 86400000)
    .toISOString().slice(0, 7)

  const { resultados } = await fetch(
    `https://api.pontua.com.br/fechamentos?` +
    `dataInicio=${mesAnterior}-01&limite=10`,
    { headers: { Authorization: `Bearer ${token}` } },
  ).then((r) => r.json())

  const ultimo = resultados[0]

  if (!ultimo || ultimo.status === 'PROCESSANDO') {
    await slack.alert(`⚠️ Mês ${mesAnterior} ainda não fechado!`)
  }
}

Veja também