refactor: enhance saldo calculation and display in registro-pontos page

- Updated the logic for calculating daily and period saldo differences to ensure accuracy by directly computing the difference between worked and expected hours.
- Improved the display of saldo differences in the UI, including formatting adjustments for better readability.
- Refactored the rendering logic to ensure consistent styling and user experience across the registro-pontos page.
This commit is contained in:
2025-11-23 14:29:49 -03:00
parent 35e7c10ed0
commit 1ad0ee91cb
2 changed files with 70 additions and 34 deletions

View File

@@ -34,7 +34,7 @@
? 'bg-red-50 border-red-200 text-red-700 dark:bg-red-900/20 dark:border-red-800 dark:text-red-400'
: 'bg-green-50 border-green-200 text-green-700 dark:bg-green-900/20 dark:border-green-800 dark:text-green-400'
}">
<span class="font-bold">+{trabalhado.horas}h {trabalhado.minutos}min</span>
<span class="font-bold text-green-600 dark:text-green-400">+{trabalhado.horas}h {trabalhado.minutos}min</span>
<span class="text-base-content/50">/</span>
<span class={isNegativo ? 'text-red-600 dark:text-red-400' : 'text-green-600 dark:text-green-400'}>
{sinalDiferenca}{diferenca.horas}h {diferenca.minutos}min

View File

@@ -421,11 +421,11 @@
};
} else {
// Fallback: usar cálculo simples se não houver configuração
const primeiroRegistro = grupoData.registros[0];
if (primeiroRegistro && 'saldoDiario' in primeiroRegistro && primeiroRegistro.saldoDiario) {
grupoData.saldoDiario = primeiroRegistro.saldoDiario;
} else {
grupoData.saldoDiario = calcularSaldoDiario(grupoData.registros);
const primeiroRegistro = grupoData.registros[0];
if (primeiroRegistro && 'saldoDiario' in primeiroRegistro && primeiroRegistro.saldoDiario) {
grupoData.saldoDiario = primeiroRegistro.saldoDiario;
} else {
grupoData.saldoDiario = calcularSaldoDiario(grupoData.registros);
}
}
}
@@ -1496,9 +1496,12 @@
saldoDiarioTotalDiferencaMinutos = -saldoDiarioTotalEsperadoMinutos; // Diferença negativa (0 - esperado)
}
// Calcular diferença corretamente: trabalhado - esperado (não somar diferenças dos pares)
const diferencaDiariaCorrigida = saldoDiarioTotalTrabalhadoMinutos - saldoDiarioTotalEsperadoMinutos;
// Armazenar saldo diário completo (usado no resumo do banco de horas)
saldosDiariosPorData[data] = {
diferencaMinutos: saldoDiarioTotalDiferencaMinutos,
diferencaMinutos: diferencaDiariaCorrigida,
trabalhadoMinutos: saldoDiarioTotalTrabalhadoMinutos,
esperadoMinutos: saldoDiarioTotalEsperadoMinutos
};
@@ -1646,9 +1649,9 @@
// Se não há tempo trabalhado, decrementar o tempo esperado completo
if (saldoEsperado.trabalhadoMinutos === 0) {
saldoDiarioAcumuladoMinutos -= saldoEsperado.esperadoMinutos;
} else {
} else {
saldoDiarioAcumuladoMinutos -= saldoEsperado.trabalhadoMinutos;
}
}
paresProcessadosParaSaldo.add(chavePar);
}
@@ -1664,7 +1667,7 @@
// Marcar linha para aplicar cor no saldo
if (trabalhouMaisQueEsperado) {
linha._saldoPositivo = true; // Verde: trabalhou mais que o esperado
} else {
} else {
linha._saldoNegativo = true; // Vermelho: ainda falta trabalhar
}
@@ -1756,9 +1759,9 @@
content: `0h 0min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
rowSpan: 2 // retorno_almoco + saida
});
} else {
linha.push('-');
}
} else {
linha.push('-');
}
} else {
// Há registros reais mas este par não foi marcado completamente
// Verificar se é um par completamente não marcado
@@ -1921,7 +1924,6 @@
const cargaHorariaDiariaEsperadaMinutos = minutosPar1EsperadoAjustadoConfig + minutosPar2EsperadoAjustadoConfig;
// Calcular saldos do período selecionado baseado nos saldos diários calculados
let saldoPeriodoDiferencaMinutos = 0;
let saldoPeriodoTrabalhadoMinutos = 0;
let diasComSaldoPositivo = 0;
let diasComSaldoNegativo = 0;
@@ -1930,12 +1932,14 @@
if (sections.registrosPonto && Object.keys(saldosDiariosPorData).length > 0) {
// Somar todos os saldos diários do período
for (const saldo of Object.values(saldosDiariosPorData)) {
saldoPeriodoDiferencaMinutos += saldo.diferencaMinutos;
saldoPeriodoTrabalhadoMinutos += saldo.trabalhadoMinutos;
if (saldo.diferencaMinutos > 0) {
// Calcular diferença diária corretamente: trabalhado - esperado
const diferencaDiaria = saldo.trabalhadoMinutos - saldo.esperadoMinutos;
if (diferencaDiaria > 0) {
diasComSaldoPositivo++;
} else if (saldo.diferencaMinutos < 0) {
} else if (diferencaDiaria < 0) {
diasComSaldoNegativo++;
}
@@ -1962,14 +1966,21 @@
for (const regs of Object.values(registrosPorDataPeriodo)) {
const saldoDiario = calcularSaldoDiario(regs);
if (saldoDiario) {
saldoPeriodoDiferencaMinutos += saldoDiario.saldoMinutos;
saldoPeriodoTrabalhadoMinutos += saldoDiario.saldoMinutos;
}
}
}
// Calcular saldo esperado do período: carga horária diária × número de dias
// SEMPRE calcular diretamente, não somar saldos diários esperados (pode duplicar)
const saldoPeriodoEsperadoMinutos = cargaHorariaDiariaEsperadaMinutos * totalDiasPeriodo;
// Calcular diferença do período corretamente: trabalhado - esperado (para "Saldo do Período Exibido")
const saldoPeriodoDiferencaMinutos = saldoPeriodoTrabalhadoMinutos - saldoPeriodoEsperadoMinutos;
// Calcular diferença do período (trabalhado - esperado) para exibição na linha "Diferença do Período"
// Negativo quando trabalhado < esperado (vermelho), positivo quando trabalhado > esperado (verde)
const diferencaPeriodoTrabalhadoMenosEsperado = saldoPeriodoTrabalhadoMinutos - saldoPeriodoEsperadoMinutos;
// Calcular médias diárias
const mediaDiariaTrabalhadaHoras = totalDiasPeriodo > 0 ? Math.floor(saldoPeriodoTrabalhadoMinutos / 60 / totalDiasPeriodo) : 0;
@@ -1994,11 +2005,19 @@
const mediaDiariaEsperadaMinutos = totalEsperadoDiarioMinutos % 60;
// Formatar valores
// Saldo do Período Exibido: diferença (trabalhado - esperado)
const horasPeriodoDiferenca = Math.floor(Math.abs(saldoPeriodoDiferencaMinutos) / 60);
const minutosPeriodoDiferenca = Math.abs(saldoPeriodoDiferencaMinutos) % 60;
const sinalPeriodoDiferenca = saldoPeriodoDiferencaMinutos >= 0 ? '+' : '-';
const saldoPeriodoDiferencaFormatado = `${sinalPeriodoDiferenca}${horasPeriodoDiferenca}h ${minutosPeriodoDiferenca}min`;
// Diferença do Período: trabalhado - esperado
// Negativo quando trabalhado < esperado (vermelho), positivo quando trabalhado > esperado (verde)
const horasDiferencaPeriodo = Math.floor(Math.abs(diferencaPeriodoTrabalhadoMenosEsperado) / 60);
const minutosDiferencaPeriodo = Math.abs(diferencaPeriodoTrabalhadoMenosEsperado) % 60;
const sinalDiferencaPeriodo = diferencaPeriodoTrabalhadoMenosEsperado >= 0 ? '+' : '-';
const diferencaPeriodoFormatado = `${sinalDiferencaPeriodo}${horasDiferencaPeriodo}h ${minutosDiferencaPeriodo}min`;
const horasPeriodoTrabalhado = Math.floor(saldoPeriodoTrabalhadoMinutos / 60);
const minutosPeriodoTrabalhado = saldoPeriodoTrabalhadoMinutos % 60;
const saldoPeriodoTrabalhadoFormatado = `+${horasPeriodoTrabalhado}h ${minutosPeriodoTrabalhado}min`;
@@ -2049,7 +2068,7 @@
bancoHorasData.push(['', '']); // Linha separadora
bancoHorasData.push(['Saldo Trabalhado do Período', saldoPeriodoTrabalhadoFormatado]);
bancoHorasData.push(['Saldo Esperado do Período', saldoPeriodoEsperadoFormatado]);
bancoHorasData.push(['Diferença do Período', saldoPeriodoDiferencaFormatado]);
bancoHorasData.push(['Diferença do Período', diferencaPeriodoFormatado]);
// Adicionar estatísticas
bancoHorasData.push(['', '']); // Linha separadora
@@ -2105,11 +2124,14 @@
}
} else if (valor.includes('+') || campo.includes('Trabalhado') || campo.includes('Esperado')) {
// Verde para valores positivos ou campos de trabalhado/esperado
if (campo.includes('Diferença') && saldoPeriodoDiferencaMinutos < 0) {
data.cell.styles.textColor = [200, 0, 0]; // Vermelho se diferença negativa
data.cell.styles.fontStyle = 'bold';
} else if (campo.includes('Diferença') && saldoPeriodoDiferencaMinutos > 0) {
data.cell.styles.textColor = [0, 128, 0]; // Verde se diferença positiva
if (campo.includes('Diferença do Período')) {
// Diferença do Período: trabalhado - esperado
// Negativo quando trabalhado < esperado (vermelho), positivo quando trabalhado > esperado (verde)
if (diferencaPeriodoTrabalhadoMenosEsperado < 0) {
data.cell.styles.textColor = [200, 0, 0]; // Vermelho quando trabalhado < esperado
} else if (diferencaPeriodoTrabalhadoMenosEsperado > 0) {
data.cell.styles.textColor = [0, 128, 0]; // Verde quando trabalhado > esperado
}
data.cell.styles.fontStyle = 'bold';
} else if (campo.includes('Trabalhado') || campo.includes('Esperado')) {
data.cell.styles.textColor = [0, 128, 0]; // Verde para trabalhado/esperado
@@ -2226,6 +2248,20 @@
theme: 'grid',
headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
styles: { fontSize: 9 },
columnStyles: {
0: { cellWidth: 30 }, // Data
1: { cellWidth: 40 }, // Tipo
2: { cellWidth: 50, cellPadding: { top: 2, bottom: 2, left: 2, right: 2 } }, // Detalhes - maior largura
3: { cellWidth: 40 }, // Motivo
4: { cellWidth: 'auto' }, // Observações
},
didParseCell: function(data) {
// Permitir quebra de linha na coluna Detalhes (índice 2)
if (data.column.index === 2) {
data.cell.styles.overflow = 'linebreak';
data.cell.styles.cellWidth = 50;
}
},
});
const lastPage = doc.getNumberOfPages();
@@ -2985,8 +3021,8 @@
? `${distanciaExcedenteKm} km além do permitido`
: `${distanciaExcedenteMetros} metros além do permitido`;
geofencingData.push(['Distância Excedente', excedenteTexto]);
}
}
}
geofencingData.push(['Status', statusTexto]);
autoTable(doc, {
@@ -3759,14 +3795,14 @@
<td class="whitespace-nowrap font-semibold text-sm">{dataFormatada}</td>
<td class="whitespace-nowrap">
<span class="badge badge-outline badge-sm font-medium text-xs">
{config
? getTipoRegistroLabel(registro.tipo, {
nomeEntrada: config.nomeEntrada,
nomeSaidaAlmoco: config.nomeSaidaAlmoco,
nomeRetornoAlmoco: config.nomeRetornoAlmoco,
nomeSaida: config.nomeSaida,
})
: getTipoRegistroLabel(registro.tipo)}
{config
? getTipoRegistroLabel(registro.tipo, {
nomeEntrada: config.nomeEntrada,
nomeSaidaAlmoco: config.nomeSaidaAlmoco,
nomeRetornoAlmoco: config.nomeRetornoAlmoco,
nomeSaida: config.nomeSaida,
})
: getTipoRegistroLabel(registro.tipo)}
</span>
</td>
<td class="whitespace-nowrap font-mono text-sm font-medium">{formatarHoraPonto(registro.hora, registro.minuto)}</td>
@@ -3784,7 +3820,7 @@
{#if grupoData.saldoDiarioComparativo}
<SaldoDiarioComparativoBadge saldo={grupoData.saldoDiarioComparativo} size="md" />
{:else if grupoData.saldoDiario}
<SaldoDiarioBadge saldo={grupoData.saldoDiario} size="md" />
<SaldoDiarioBadge saldo={grupoData.saldoDiario} size="md" />
{:else}
<span class="badge badge-ghost badge-lg">-</span>
{/if}