diff --git a/apps/web/src/lib/components/ponto/SaldoDiarioComparativoBadge.svelte b/apps/web/src/lib/components/ponto/SaldoDiarioComparativoBadge.svelte index 8233a1e..9344ac0 100644 --- a/apps/web/src/lib/components/ponto/SaldoDiarioComparativoBadge.svelte +++ b/apps/web/src/lib/components/ponto/SaldoDiarioComparativoBadge.svelte @@ -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' }"> - +{trabalhado.horas}h {trabalhado.minutos}min + +{trabalhado.horas}h {trabalhado.minutos}min / {sinalDiferenca}{diferenca.horas}h {diferenca.minutos}min diff --git a/apps/web/src/routes/(dashboard)/recursos-humanos/registro-pontos/+page.svelte b/apps/web/src/routes/(dashboard)/recursos-humanos/registro-pontos/+page.svelte index b837ccb..4172efb 100644 --- a/apps/web/src/routes/(dashboard)/recursos-humanos/registro-pontos/+page.svelte +++ b/apps/web/src/routes/(dashboard)/recursos-humanos/registro-pontos/+page.svelte @@ -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 @@