From 6936a59c21d9ead2b64f8151ddd1bdecec3d7ea0 Mon Sep 17 00:00:00 2001 From: deyvisonwanderley Date: Thu, 11 Dec 2025 16:52:07 -0300 Subject: [PATCH] feat: implement cascading recalculation of monthly hour banks when past months are updated or adjusted --- packages/backend/convex/pontos.ts | 94 +++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 4 deletions(-) diff --git a/packages/backend/convex/pontos.ts b/packages/backend/convex/pontos.ts index a0685d1..65bd06b 100644 --- a/packages/backend/convex/pontos.ts +++ b/packages/backend/convex/pontos.ts @@ -1800,7 +1800,14 @@ async function atualizarBancoHoras( // Atualizar banco de horas mensal const mes = data.substring(0, 7); // YYYY-MM - await calcularBancoHorasMensal(ctx, funcionarioId, mes); + + // Verificar se estamos editando um mês passado + const hoje = new Date(); + const mesAtual = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`; + const estaEditandoMesPassado = mes < mesAtual; + + // Se estamos editando um mês passado, recalcular em cascata para atualizar meses seguintes + await calcularBancoHorasMensal(ctx, funcionarioId, mes, estaEditandoMesPassado); } /** @@ -1918,14 +1925,74 @@ export const obterBancoHorasFuncionario = query({ } }); +/** + * Recalcula meses seguintes em cascata quando um mês anterior é atualizado + * Isso garante que os saldos iniciais dos meses seguintes sejam atualizados corretamente + */ +async function recalcularMesesSeguintes( + ctx: MutationCtx, + funcionarioId: Id<'funcionarios'>, + mesAtualizado: string // YYYY-MM do mês que foi atualizado +): Promise { + const hoje = new Date(); + const mesAtual = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`; + + // Se o mês atualizado já é o mês atual ou futuro, não precisa recalcular nada + if (mesAtualizado >= mesAtual) { + return; + } + + // Recalcular todos os meses do mês seguinte ao atualizado até o mês atual + // Calcular primeiro mês a recalcular (mês seguinte ao atualizado) + const [anoAtualizado, mesNumAtualizado] = mesAtualizado.split('-').map(Number); + let anoIter = anoAtualizado; + let mesNumIter = mesNumAtualizado + 1; + if (mesNumIter > 12) { + mesNumIter = 1; + anoIter += 1; + } + + // Continuar enquanto o mês iterado for menor ou igual ao mês atual + while (true) { + const mesIterStr = `${anoIter}-${String(mesNumIter).padStart(2, '0')}`; + + // Se passou do mês atual, parar + if (mesIterStr > mesAtual) { + break; + } + + // Verificar se existe registro mensal para este mês + const bancoMensalExistente = await ctx.db + .query('bancoHorasMensal') + .withIndex('by_funcionario_mes', (q) => + q.eq('funcionarioId', funcionarioId).eq('mes', mesIterStr) + ) + .first(); + + // Se existe registro, recalcular (o saldo inicial mudou porque o mês anterior mudou) + if (bancoMensalExistente) { + await calcularBancoHorasMensal(ctx, funcionarioId, mesIterStr, false); // false = não recalcular cascata novamente + } + + // Avançar para o próximo mês + mesNumIter += 1; + if (mesNumIter > 12) { + mesNumIter = 1; + anoIter += 1; + } + } +} + /** * Calcula e atualiza banco de horas mensal para um funcionário * Esta função deve ser chamada após atualizações no banco de horas diário + * @param recalcularCascata - Se true, recalcula automaticamente os meses seguintes (padrão: true) */ async function calcularBancoHorasMensal( ctx: MutationCtx, funcionarioId: Id<'funcionarios'>, - mes: string // YYYY-MM + mes: string, // YYYY-MM + recalcularCascata: boolean = true // Por padrão, recalcula em cascata ): Promise { // Buscar todos os bancoHoras do mês const dataInicio = `${mes}-01`; @@ -2045,6 +2112,11 @@ async function calcularBancoHorasMensal( atualizadoEm: agora }); } + + // Recalcular meses seguintes em cascata se solicitado + if (recalcularCascata) { + await recalcularMesesSeguintes(ctx, funcionarioId, mes); + } } /** @@ -2534,7 +2606,14 @@ export const ajustarBancoHoras = mutation({ // Recalcular banco de horas mensal após ajuste const mes = hoje.substring(0, 7); // YYYY-MM - await calcularBancoHorasMensal(ctx, args.funcionarioId, mes); + + // Verificar se estamos ajustando um mês passado + const hojeDate = new Date(); + const mesAtual = `${hojeDate.getFullYear()}-${String(hojeDate.getMonth() + 1).padStart(2, '0')}`; + const estaAjustandoMesPassado = mes < mesAtual; + + // Se estamos ajustando um mês passado, recalcular em cascata para atualizar meses seguintes + await calcularBancoHorasMensal(ctx, args.funcionarioId, mes, estaAjustandoMesPassado); // Criar registro de homologação (mantido para compatibilidade) const homologacaoId = await ctx.db.insert('homologacoesPonto', { @@ -3727,7 +3806,14 @@ export const criarAjusteBancoHoras = mutation({ // Recalcular banco de horas mensal const mes = args.dataAplicacao.substring(0, 7); - await calcularBancoHorasMensal(ctx, args.funcionarioId, mes); + + // Verificar se estamos aplicando ajuste em um mês passado + const hoje = new Date(); + const mesAtual = `${hoje.getFullYear()}-${String(hoje.getMonth() + 1).padStart(2, '0')}`; + const estaAplicandoEmMesPassado = mes < mesAtual; + + // Se estamos aplicando em um mês passado, recalcular em cascata para atualizar meses seguintes + await calcularBancoHorasMensal(ctx, args.funcionarioId, mes, estaAplicandoEmMesPassado); return { ajusteId, success: true }; }