Files
sigrhapf/Documents/sigfip/sigefp/VALIDACAO_IMPLEMENTACAO_RECOMENDACOES_RH.md
T
2026-05-19 11:45:46 +00:00

16 KiB

Validação de Implementação das Recomendações

Módulo RH & Folha de Pagamento

Data: 2025-01-27
Objetivo: Verificar se todas as recomendações da análise ultra profunda foram implementadas


📋 Resumo Executivo

Status Geral

Categoria Implementado Parcial Pendente Total
Crítico 3 1 2 6
Alta Prioridade 3 1 2 6
Média Prioridade 0 0 8 8
TOTAL 6 2 12 20

Taxa de Implementação: 30% (6/20)
Taxa de Implementação Crítica: 50% (3/6)
Taxa de Implementação Alta: 50% (3/6)


1. Validação Detalhada por Problema

🔴 Prioridade CRÍTICA

P1 - Validação de Duplicidade de PayrollRun

Status: IMPLEMENTADO

Evidências:

// Localização: PayrollService.createPayrollRun() - Linhas 90-107
// Validation: Prevent duplicate run for same Period + Ministry + OrgUnit
if (dto.getMinistryId() != null) {
    List<PayrollRun> existingRuns = payrollRunRepository
        .findByPeriodIdAndMinistry(dto.getPeriodId(), dto.getMinistryId());
    boolean duplicate = existingRuns.stream()
        .anyMatch(run -> Objects.equals(run.getOrgUnit(), dto.getOrgUnitId())
            && Objects.equals(run.getRunType(), dto.getRunType())
            && !"CLOSED".equals(run.getStatus())
            && !"FAILED".equals(run.getStatus()));
    
    if (duplicate) {
        throw new BusinessException(
            "Já existe uma execução de folha ativa (não fechada/falha) para este período, ministério e unidade orgânica.",
            "DUPLICATE_PAYROLL_RUN",
            HttpStatus.CONFLICT);
    }
}

Validação:

  • Validação de duplicidade implementada
  • Verifica período, ministério, orgUnit e runType
  • Exclui folhas CLOSED e FAILED da verificação
  • Lança BusinessException com código apropriado

Observação: Falta constraint de banco de dados (ver P6)


⚠️ P2 - Validação de Agentes Elegíveis para Folha

Status: ⚠️ PARCIALMENTE IMPLEMENTADO

Evidências:

// Localização: PayrollService.generatePayrollItems() - Linhas 225-249

// Validações implementadas:
1.  Verifica se agente tem salaryStep e posseDate (linhas 227-231)
2.  Verifica idade mínima de 18 anos (linhas 233-241)
3.  Verifica se tem contrato ativo válido (linhas 243-249)

Validações Implementadas:

  • SalaryStep e posseDate
  • Idade mínima (18 anos)
  • Contrato ativo válido

Validações Faltantes:

  • ⚠️ Verificação se contrato está vigente no período (startDate/endDate) - Parcialmente coberto por calculateWorkingDaysFraction()
  • Verificação se agente foi desligado durante o período - Implementado em calculateWorkingDaysFraction()
  • Verificação se agente está suspenso durante o período
  • Verificação de período probatório

Recomendação: Implementar método isAgentEligibleForPayroll() completo conforme análise.


P3 - Cálculo Proporcional de Salário

Status: IMPLEMENTADO

Evidências:

// Localização: PayrollService.generatePayrollItems() - Linhas 259-285

// Calculate Proportional Fraction (0.0 to 1.0)
BigDecimal workingFraction = calculateWorkingDaysFraction(agent, pStart, pEnd);

// If fraction is 0, skip agent implies no working days in period
if (workingFraction.compareTo(BigDecimal.ZERO) == 0) {
    log.info("Agente {} ignorado: 0 dias trabalhados no período.", agent.getMatricula());
    continue;
}

BigDecimal baseAmount = salary.getBaseAmount().multiply(workingFraction).setScale(2,
    RoundingMode.HALF_UP);

// Aplicado também em:
- Salário Base (linha 268)
- Subsídio (linha 298)
- Descrição indica "(Proporcional)" quando fraction < 1.0 (linhas 280-281, 312-313)

Validação:

  • Método calculateWorkingDaysFraction() implementado
  • Cálculo proporcional aplicado ao salário base
  • Cálculo proporcional aplicado ao subsídio
  • Descrição indica quando é proporcional
  • Agente é ignorado se fraction = 0

Validação do Método calculateWorkingDaysFraction():

// Localização: PayrollService.calculateWorkingDaysFraction() - Linhas 750-781

// Considera:
1.  Data de admissão (HireDate)
2.  Data de posse (PosseDate) 
3.  Data de término (TerminationDate)
4.  Validação de datas inválidas (start > end)
5.  Cálculo de fração (effectiveDays / totalDays)

// Não considera:
-  Contrato ativo (startDate/endDate do contrato)
-  Suspensões durante o período

Nota: O método está bem implementado, mas poderia considerar também as datas do contrato ativo.


P4 - Controle de Concorrência em PayrollRun

Status: NÃO IMPLEMENTADO

Evidências:

  • Não há @Version em PayrollRun
  • Não há uso de @Lock ou LockModeType.PESSIMISTIC
  • Não há validação de status após lock

Risco: Múltiplos usuários podem processar a mesma folha simultaneamente.

Recomendação: Implementar optimistic locking com @Version ou pessimistic locking.


⚠️ P5 - Validação de Promoção Incompleta

Status: ⚠️ PARCIALMENTE IMPLEMENTADO

Evidências:

// Localização: AgentService.validatePromotion() - Linhas 411-431

// Validações implementadas:
1.  Verifica se tem pelo menos 3 anos de avaliações
2.  Verifica se todas as avaliações têm score >= 14 (Bom)

Validações Faltantes:

  • Tempo mínimo no escalão atual (3 anos)
  • Tempo mínimo na categoria (para promoção)
  • Habilitação literária adequada
  • Status das avaliações (deve ser "FINAL", não "DRAFT")
  • Vagas disponíveis na categoria/grau de destino

Recomendação: Completar validação conforme análise detalhada.


P6 - Constraint UNIQUE para PayrollRun

Status: IMPLEMENTADO

Evidências:

  • Script phase7_hardening.sql contém índice único parcial.
CREATE UNIQUE INDEX IF NOT EXISTS idx_payroll_run_active_unique 
ON payroll_run (period_id, ministry_id, org_unit_id, run_type) 
WHERE status NOT IN ('CLOSED', 'CANCELLED');

Validação:

  • Constraint existe e cobre os campos corretos.

🟡 Prioridade ALTA

P7 - Validação de Período Fechado

Status: NÃO IMPLEMENTADO

Evidências:

  • Não há verificação de period.getStatus() antes de criar PayrollRun

Recomendação:

if (!"OPEN".equals(period.getStatus())) {
    throw new BusinessException(
        "Não é possível criar execução de folha para período fechado",
        "PERIOD_CLOSED",
        HttpStatus.BAD_REQUEST
    );
}

P8 - Validação de Sobreposição de Contratos

Status: NÃO IMPLEMENTADO

Evidências:

  • Não há validação em AgentContractService.saveContract()

Recomendação: Implementar método validateContractDates() conforme análise.


P9 - Cálculo de Faltas Assume 30 Dias Fixos

Status: IMPLEMENTADO

Evidências:

// Localização: PayrollService.calculateWorkingDaysFraction() - Linhas 772-780
long totalDays = java.time.temporal.ChronoUnit.DAYS.between(pStart, pEnd) + 1;
// ...
return new BigDecimal(effectiveDays).divide(new BigDecimal(totalDays)...)

Validação:

  • Fallback de 30 dias removido.
  • Usa ChronoUnit.DAYS para cálculo exato.

P10 - Sincronização de deductedInPayrollRunId

Status: NÃO IMPLEMENTADO

Evidências:

  • Não há atualização de absence.setDeductedInPayrollRunId() após criar PayrollItem de falta

Recomendação: Atualizar ausências após processamento.


P11 - N+1 Queries em Geração de Folha

Status: IMPLEMENTADO

Evidências:

// Localização: PayrollService.generatePayrollItems() - Linhas 167-223

// PERFORMANCE OPTIMIZATION: BATCH FETCHING

// 1. Fetch ALL relevant Salary Grids in one go
List<SalaryGrid> allGrids = salaryGridRepository.findAll();
Map<UUID, SalaryGrid> salaryGridMap = allGrids.stream()
    .filter(g -> g.getStep() != null && g.getValidFrom().isBefore(LocalDate.now())
        && (g.getValidTo() == null || g.getValidTo().isAfter(LocalDate.now())))
    .collect(Collectors.toMap(
        g -> g.getStep().getId(),
        g -> g,
        (existing, replacement) -> existing));

// 2. Fetch Budget Lines (Bulk by OrgUnit + FiscalYear)
List<BudgetLine> budgetLines = budgetLineRepository.findByFiscalYearId(fiscalYearId);
Map<String, BudgetLine> budgetLineMap = budgetLines.stream()
    .filter(bl -> bl.getOrgUnit() != null)
    .collect(Collectors.toMap(
        bl -> bl.getOrgUnit() + "_" + bl.getEconomicClass(),
        bl -> bl,
        (existing, replacement) -> existing));

// 3. Fetch Attendance & Absences (Bulk by AgentIds + DateRange)
Map<UUID, List<AttendanceRecord>> attendanceMap = attendanceRecordRepository
    .findByAgentIdInAndDateRange(agentIds, pStart, pEnd)
    .stream()
    .collect(Collectors.groupingBy(r -> r.getAgent().getId()));

Map<UUID, List<Absence>> absenceMap = absenceRepository
    .findByAgentIdInAndDateRange(agentIds, pStart, pEnd)
    .stream()
    .collect(Collectors.groupingBy(a -> a.getAgent().getId()));

// 4. Fetch Contracts (Bulk)
Map<UUID, List<AgentContract>> contractMap = agentContractRepository
    .findByAgentIdIn(agentIds)
    .stream()
    .filter(c -> Boolean.TRUE.equals(c.getIsActive())
        && !c.getStartDate().isAfter(pEnd))
    .collect(Collectors.groupingBy(c -> c.getAgent().getId()));

Validação:

  • Batch fetching de SalaryGrids
  • Batch fetching de BudgetLines
  • Batch fetching de AttendanceRecords
  • Batch fetching de Absences
  • Batch fetching de AgentContracts
  • Repositórios têm métodos findByAgentIdIn() e findByAgentIdInAndDateRange()

Melhoria Sugerida: Otimizar salaryGridRepository.findAll() para usar findByStepIdIn() se possível.


P12 - Cobertura de Testes

Status: PARCIALMENTE IMPLEMENTADO

Evidências:

// Localização: PayrollServiceTest.java

// Testes implementados:
1.  generatePayrollItems_WithSuccess() - Testa geração de itens com cálculos
2.  processPayrollRun_FailsWhenBudgetLineMissing() - Testa validação de budget line

Testes Faltantes:

  • Teste de validação de duplicidade de PayrollRun
  • Teste de cálculo proporcional
  • Teste de validação de elegibilidade de agentes
  • Teste de controle de concorrência
  • Teste de validação de promoções
  • Teste de integrações (Orçamento, Tesouro)

Recomendação: Expandir suite de testes conforme análise.


🟢 Prioridade MÉDIA

P13 - Validação de Sobreposição de Regras Globais

Status: NÃO IMPLEMENTADO

Evidências:

// Localização: TaxService.saveRule() - Linha 48
// Futuro: Adicionar validações de sobreposição de datas

P14 - Validação de Sobreposição de Escalões de Imposto

Status: NÃO IMPLEMENTADO


P15 - Constraint CHECK para Datas

Status: NÃO IMPLEMENTADO

Evidências:

  • Não há constraints CHECK no banco de dados
  • Apenas 1 constraint CHECK encontrada (para enum de CareerEventType)

Recomendação: Adicionar constraints:

ALTER TABLE payroll_period
ADD CONSTRAINT chk_period_dates CHECK (start_date <= end_date);

ALTER TABLE agent_contract
ADD CONSTRAINT chk_contract_dates CHECK (end_date IS NULL OR start_date <= end_date);

ALTER TABLE absence
ADD CONSTRAINT chk_absence_dates CHECK (start_date <= end_date);

P16 - Validação de Idade Mínima

Status: IMPLEMENTADO

Evidências:

// Localização: PayrollService.generatePayrollItems() - Linhas 233-241
// Regra: Idade Mínima 18 Anos
if (agent.getBirthDate() != null) {
    int age = Period.between(agent.getBirthDate(), LocalDate.now()).getYears();
    if (age < 18) {
        log.warn("Agente {} ignorado: Menor de idade ({} anos).", agent.getMatricula(), age);
        continue;
    }
}

Nota: Implementado em generatePayrollItems(), mas deveria estar também em AgentService.create().


P17 - Validação de Habilitação Literária

Status: NÃO IMPLEMENTADO


P18 - Uso Inconsistente de Exceções

Status: NÃO CORRIGIDO

Evidências:

  • Ainda há mistura de IllegalArgumentException, BusinessException, ResourceNotFoundException

P19 - Logging Adequado

Status: PARCIALMENTE IMPLEMENTADO

Evidências:

  • Há logging em alguns pontos (warn, info)
  • Falta logging de erros em catch blocks

P20 - Validação de Permissões

Status: NÃO IMPLEMENTADO


2. Resumo por Categoria

Implementações Completas (6)

  1. P1 - Validação de Duplicidade de PayrollRun
  2. P3 - Cálculo Proporcional de Salário
  3. P11 - N+1 Queries (Batch Fetching)
  4. P12 - Cobertura de Testes (parcial)
  5. P16 - Validação de Idade Mínima

⚠️ Implementações Parciais (2)

  1. ⚠️ P2 - Validação de Agentes Elegíveis (faltam algumas validações)
  2. ⚠️ P5 - Validação de Promoção (faltam validações de tempo mínimo)

Não Implementadas (12)

  1. P4 - Controle de Concorrência
  2. P6 - Constraint UNIQUE para PayrollRun
  3. P7 - Validação de Período Fechado
  4. P8 - Validação de Sobreposição de Contratos
  5. P9 - Cálculo de Faltas (fallback ainda existe)
  6. P10 - Sincronização de deductedInPayrollRunId
  7. P13 - Validação de Sobreposição de Regras Globais
  8. P14 - Validação de Sobreposição de Escalões de Imposto
  9. P15 - Constraint CHECK para Datas
  10. P17 - Validação de Habilitação Literária
  11. P18 - Uso Inconsistente de Exceções
  12. P19 - Logging Adequado
  13. P20 - Validação de Permissões

3. Recomendações Prioritárias

🔴 Urgente (Próxima Sprint)

  1. P4 - Controle de Concorrência: Implementar @Version em PayrollRun
  2. P6 - Constraint UNIQUE: Adicionar índice parcial no banco
  3. P7 - Validação de Período Fechado: Adicionar verificação simples

🟡 Importante (Próximas 2 Sprints)

  1. P2 - Completar Validação de Elegibilidade: Implementar método completo
  2. P5 - Completar Validação de Promoção: Adicionar validações de tempo mínimo
  3. P10 - Sincronização de deductedInPayrollRunId: Atualizar ausências após processamento
  4. P9 - Remover Fallback: Lançar exceção ao invés de usar 30 dias

🟢 Desejável (Backlog)

  1. P8, P13, P14 - Validações de Sobreposição: Implementar conforme análise
  2. P15 - Constraints CHECK: Adicionar no banco de dados
  3. P17, P18, P19, P20 - Melhorias Gerais: Implementar conforme necessário

4. Conclusão

Pontos Positivos

  1. Implementações Críticas: 3 das 6 correções críticas foram implementadas
  2. Performance: Otimização N+1 foi bem implementada
  3. Cálculo Proporcional: Lógica de fração implementada corretamente
  4. Validação de Duplicidade: Implementada em código (falta apenas constraint de BD)

Pontos de Atenção ⚠️

  1. Controle de Concorrência: Falta implementação (risco de race conditions)
  2. Validações Parciais: Algumas validações foram implementadas parcialmente
  3. Constraints de Banco: Falta adicionar constraints para garantir integridade

Próximos Passos

  1. Implementar controle de concorrência (P4)
  2. Adicionar constraint UNIQUE no banco (P6)
  3. Completar validações parciais (P2, P5)
  4. Expandir suite de testes

Validação realizada por: Cursor AI
Data: 2025-01-27
Versão: 1.0