Ir para o conteúdo principal

Estudo do método calcularFolhaPagamento() - Governa

Fluxo do método executarCalculoPagamentoFolha() O método está localizado no arquivo CalculoPagamentoFolhaMB e é chamado a partir de um actionListener em calculoPagamentoFolha.xhtml. Este método realiza o cálculo da folha de pagamento, dividindo o processamento em threads e validando o cálculo no final da execução. Ao ser acionado, o método faz duas validações:

  • se o usuário que o acionou tem permissão para acessar. Caso não tenha, a operação é cancelada, uma mensagem de aviso é enviada e a execução do cálculo é definida como falsa
  • se um cálculo já está sendo executado. Se estiver, uma mensagem de erro é enviada e execução do cálculo é definida como falsa
try {  
   super.verificarPermissaoAcessar();  
   this.calculoBO.verificarSeCalculoEstaExecutando();  
} catch (PermissaoNegadaExcecao e) {  
   this.cancelar();  
   log.warn(e.getMessage());  
   this.mostrarAlerta(msgServico.getMsgGenericaPermissaoNegada());  
   this.calculoBO.getCalculoFinanceiro().setExecutando(false);  
} catch (ProcessandoMensagemNaFilaExcecao e) {  
   log.error(e.getMessage(), e);  
   this.mostrarAlerta(e.getMessage());  
   this.calculoBO.getCalculoFinanceiro().setExecutando(false);  
}

Em seguida, os contadores do cálculo são limpos, para, em seguida, preencher uma lista dos servidores que irão para o cálculo da folha de pagamento. Essa função utiliza a matrícula inicial e final inseridas no front: 

front_calculo_folha_pagamento.png

Essa lista é passada como parâmetro para o método excluirFinanceiros(), que excluirá as averbações mensais, as pensão judicial, as verbas do financeiro mensal e o financeiro mensal.

carregarServidorParaCalculo()

Em seguida, uma validação com uma função booleana é feita, carregarListaServidoresExecucao(), ela retorna outra função booleana (carregarServidorParaCalculo()) Essa função utiliza o método carregarListaServidoresCalculoFolhaPagamento(), responsável pela busca dos dados dos servidores para o cálculo da folha de pagamento. A busca dos dados é realizada pela função buscarServidoresCalculoFolhaPagamento(), que recebe vários parâmetros:

  • matrícula inicial;
  • matrícula final;
  • admissão inicial;
  • admissão final;
  • salário inicial;
  • salário final;
  • lista dos cargos selecionados;
  • lista dos vínculos selecionados;
  • lista das classificações funcionais selecionadas;
  • lista das lotações selecionadas;
  • lista das situações selecionadas;
  • o mês referência
  • id da empresa autenticada Essa função faz uma consulta na base, trazendo todos os dados acima dos servidores e os retorna em uma lista. Após os dados dos servidores estarem armazenados na lista, é feita uma validação dessa lista, verificando se ela está nula ou vazia. Caso esteja em uma condição ou outra, retorna falso e para a execução do cálculo.

executarCalculoPagamentoFolha()

Se a condição anterior for satisfeita, o cálculo da folha é iniciado utilizando a lista dos servidores mensais. Em seguida busca parâmetros de verba.

buscarParametros()

Esse método busca e insere todas as verbas de empresas em um mapa de rotina por id. Após a inserção das verbas, os servidores mensais são atualizados e a service de calculo da folha persiste o processo de cálculo com o método processarPersistirCalculoFolha()

processarPersistirCalculoFolha()

Esse método recebe como parâmetro a lista de servidores mensais. Define a execução do cálculo financeiro como true e então verifica se a lista de servidores está vazia. Se a lista estiver vazia, a execução é alterada para false e força a coleta de lixo para ganho de performance. Se a lista não estiver vazia, é iniciado o processo paralelo do calculo da folha com a função processarParaleloCalculoFolha()

processarParaleloCalculoFolha()

Essa função é responsável por dividir o processo de calculo em threads. Antes de realizar este procedimento, ela verifica se a lista de servidores está nula ou vazia, e, caso esteja retorna uma coleção vazia. É definido uma variável para a quantidade de servidores por thread (1000). Se a quantidade de servidores for maior que essa variável, a quantidade de servidores por thread será alterada para a quantidade de servidores divido pela constante CALCULO_FOLHA_THREAD_POOL_TAMANHO (10), ou seja, se a lista possuir 3250 servidores, 325 servidores serão calculados em 10 threads.

Long qtdServidorPorThread = 1000l;  
if (servidores.size() > qtdServidorPorThread) {  
   // Calculando quantos itens serão calculados por thread  
   qtdServidorPorThread = servidores.size() / calculoExecucaoConfig.CALCULO_FOLHA_THREAD_POOL_TAMANHO;
if (qtdServidorPorThread == 0) {  
   qtdServidorPorThread = (long) servidores.size();  
}

Se essa divisão resultar em 0, a variável passa a ser a quantidade de servidores na lista. A seguir as variável de pool são criadas e a lista de servidores é percorrida e uma tarefa é adicionada na pool para finalmente realizar o calculo em si

// Definindo o pool de thread  
List<CalculoFolhaServicoTarefa> calculoThreadPool = new ArrayList<>();  
List<ServidorMensalVisao> servidorListEmUmaThread = new ArrayList<>();  
  
for (int i = 1; i <= servidores.size(); i++) {  
   servidorListEmUmaThread.add(servidores.get(i - 1));  
   if (i % qtdServidorPorThread == 0) {  
  
      // Adicionando uma tarefa no pool de thread  
      calculoThreadPool.add(new CalculoFolhaServicoTarefa(calculoFinanceiro, servidorListEmUmaThread, moedaValorCtx, paramEventoCalculo,  
            rotinaPorVerbaIdMap, adicionalList, execucaoRelatorio, paramSistema, financeiroMensalLotePersistencia));  
      servidorListEmUmaThread = new ArrayList<>();
     ...
   }

CalculoFolhaServicoTarefa()

Esta classe é a responsável por realizar todos os cálculos financeiros ao ser invocada* Ao ser executada, a classe cria uma variável que recebe uma lista com a função calcularPagamentoFolha()

financeiroMensalList = calculoFinanceiro.calcularPagamentoFolha(servidores, moedaValorCtx, paramEventoCalculo, rotinaPorVerbaIdMap, listaParamAdicionalTempo, execucaoRelatorio, paramSistema, this.financeiroMensalLotePersistencia);  
calcularPagamentoFolha() (CalculoFinanceiro)

Essa função percorre a lista de servidores e executa o cálculo do financeiro mensal para cada um, percorrendo a lista do financeiro mensal

calcularPagamentoFolha() (CalculoPagamentoFolha)

Essa função é responsável pelo calculo do pagamento e no fim retorna o contexto do calculo. Iniciando pela verificação se há ou não moedas cadastradas no mês corrente. Caso não possua o sistema lança uma exceção.

// Verificar se no mês corrente há moedas cadastradas  
if (moedaValorCtx == null) {  
   throw new RestricaoNegocioExcecao(String.format("Não há moedas [%s]", servidorEfetivo.getMesReferenciaFormatado()));  
}

Em seguida, verifica se o servidor é comissionado, busca sua lotação mensal e caso seja EMATER, uma variável booleana isEmater recebe true

LotacaoMensal lotacaoMensal = servidorMensalBO.buscarLotacalMensalServidor(servidorEfetivo.getServidorMensalId(),  
      servidorEfetivo.getMesReferencia());  
  
if (lotacaoMensal.getLotacao().getCodigo().equals(CODIGO_LOTACAO_EMATER)) {  
   isEmater = true;  
}

Após a lotação do servidor ser definida, é feita uma verificação se o servidor está afastado. Se o servidor estiver afastado, a função retorna nulo. Em seguida, o DSR é calculado.

calcularDSR()

Para o calculo do DSR, uma variável totalDiasUteisMes recebe todos os dias úteis por município e outra totalDiasMes recebe todos os dias do mês. O valor do DSR é calculado da seguinte forma: 1 / (totalDiasUteisMes * totalDiasMes) - totalDiasUteisMes. O resultado desse calculo é adicionado ao contexto. Após o cálculo do DSR, os dados do servidor mensal são buscados e definidos nas variáveis calculoCtx e servidorEfetivo (parâmetros da função principal); dentre esses dados buscados estão:

  • grau salarial,
  • percurso,
  • cidade,
  • lotação mensal,
  • classificação funcional,
  • vínculo empregatício,
  • cargo,
  • vencimento; Parâmetros de previdência e salário família são armazenados nas variáveis paramPrevidencia e paramAbonoSalarioFamilia (se não houver parâmetro de abono família, uma mensagem é exibida no log)
log.debug("Buscando o parametro de previdencia");  
ParamPrevidencia paramPrevidencia = paramPrevidenciaBO.buscarTodos(servidorEfetivo.getVinculoEmpregaticioId(),  
      servidorEfetivo.getClassificacaoFuncionalId(), servidorEfetivo.getMesReferencia());  
  
log.debug("Buscando o parametro de salário familia");  
ParamAbonoSalarioFamilia paramAbonoSalarioFamilia = paramAbonoSalarioFamiliaBO.buscarTodos(servidorEfetivo.getVinculoEmpregaticioId(),  
      servidorEfetivo.getClassificacaoFuncionalId());  
  
if (paramAbonoSalarioFamilia == null) {  
   log.debug("Não há parametrização de Abono Família para o vinculo empregaticio {}{} e classificação funcional {}{}",  
         servidorEfetivo.getVinculoEmpregaticioId(), servidorEfetivo.getVinculoEmpregaticioDescricao(),  
         servidorEfetivo.getClassificacaoFuncionalId(), servidorEfetivo.getClassificacaoFuncionalDescricao());  
} else {  
   // Calcular salário/abono família (depende das bases)  
   calculoCtx.setAbonoSalarioFamiliaTipo(AbonoSalarioFamilia.parse(paramAbonoSalarioFamilia.getTipo()));  
}

A busca pelo cargo mensal é feita por uma query na função buscarPorCargoEMesReferencia() e definida numa variável cargoMensal.

Inativos

Os servidores inativos são buscados e armazenados na variável servidorInativo e a data de desligamento em servidorDesligamento

Férias

O total de dias do mês descontando férias é armazenados na variável totalFeriasAdiantadas. Se o total de dias for maior que 0, é feita uma verificação se já teve a verba de adiantamento no mês anterior. Então, as verbas do servidor são buscadas via query e armazenas na variável verbas, que será responsável por filtrar as verbas pagas de ferias, inss e desconto, no final a variável feriasAdiantadas recebe o resultado de uma query e então é feito o procedimento para adicionar essas verbas no contexto de cálculo com o método consistirFeriasAdiantadasFolhaProporcional() enviando como parâmetro os valores pagos das férias, inss e desconto, buscados anteriormente e a diferença de dias do inicio ao fim das férias.

Vencimento

Se o vencimento do servidorEfetivo no contexto de calculo do servidor não possuir id, é feito o calculo do vencimento com o método calcularSalarioVencimenot() que adiciona a verba de vencimento em uma variável, define o servidor para o qual o cálculo da folha será realizado, será utilizado a regra de buscar a situação do ultimo dia do mês e se for comissionado utiliza o mesmo cálculo.

// Obtendo a verba de vencimento  
VerbaRotina verbaVencimento = rotinaPorVerbaIdMap.get(verbaPagamento);  
  
ServidorMensalVisao servidorEfetivo = servidorCalculoCtx.getServidorEfetivo();  
VariavelValorContexto variavelValorCtx = servidorCalculoCtx.getVariavelValorCtx();  
VerbaValorContexto verbaValorCtx = servidorCalculoCtx.getVerbaValorCtx();  
Integer mesReferencia = servidorEfetivo.getMesReferencia();  
  
// Define o servidor para o qual o cálculo da folha será realizado, sera  
// utilizado a regra de buscar a situação do ultimo dia do mes  
  
IServidorAcesso servidor = servidorCalculoCtx.getServidorEfetivo();  
// Variável que armazena o salário efetivo  
  
Long tempo = System.currentTimeMillis();  
  
definirServidor(servidorCalculoCtx);  
  
// Se for comissionado utiliza o mesmo para o cálculo  
if (servidorCalculoCtx.getIsServidorComissionado()) {  
   servidor = servidorCalculoCtx.getServidorComissionado();  
}

Busca o percentual da aposentadoria que é calculada pela seguinte fórmula se o tempo trabalhado for nulo e se não possuir percentual ou o percentual for 0

percentualAposentadoria = servidorInativo.getTempoTrabalhado() / servidorInativo.getTempoIntegral();

se não, se o apenas o percentual for nulo a fórmula será:

percentualAposentadoria = servidorInativo.getPercentual() / 100;

O salário mensal é então buscado via query pela função buscarPorMesReferenciaEGrauSalarial() e armazenado na variável salárioMensal, e o salário base armazenado na variável salarioBase com o retorno da função buscarSalarioBase() que verifica se o sistema está parametrizado para informar o valor do salário e busca o salário informado. Caso não possua, pega o valor da tabela salarial. Os dados do cargo e a hora mensal são adicionadas ao contexto. Se o servidor mensal for nulo, e a categoria salarial for mensal, a variável salarioVerbaVencimento recebe o retorno da função calcularSalarioVerbaVencimentoMensal(), que recebe como parâmetro o contexto do calculo do servidor, o salario mensal e o percentual da aposentadoria para calcular o salário pela fórmula:

Double indiceReducaoSalarial = servidorCalculoCtx.getServidorEfetivo().getIndiceReducaoSalarial();  
Double valorSubtrair = salarioVerbaVencimento.doubleValor() * (indiceReducaoSalarial / 100);  
salarioVerbaVencimento = salarioVerbaVencimento.subtrairPor(new Fracao(valorSubtrair));

Após o cálculo, o valor é adicionado ao contexto. Caso a categoria seja unidade trabalho, a variável receberá o retorno da função calcularSalarioVerbaVencimentoUnidadeTrabalho(), que recebe de parâmetro o contexto do calculo do servidor e o salario mensal. Em seguida irá adicionar à variável contexto o valor do salário base e fazer o retorno:

UnidadeTrabalho unidadeTrabalho = unidadeTrabalhoBO.buscarPorServidorAndMesAndRotina(servidorEfetivo.getServidor().getId(),  
      servidorCalculoCtx.getServidorEfetivo().getMesReferencia(), RotinaCalculo.FOLHA_PAGAMENTO.getAbreviacao());  
  
// Adicionando o valor do salário base à variável  
if (servidorEfetivo.getQuantidadeHoraMensal() != null) {  
   salarioVerbaVencimento = salarioVerbaVencimento.multiplicarPor(servidorEfetivo.getQuantidadeHoraMensal());  
} else {  
   // Adicionando o valor do salário base à variável  
   if (unidadeTrabalho != null) {  
      variavelValorCtx.adicionarAoContexto(FormulaVariavelEnum.SALARIO_BASE,  
            salarioVerbaVencimento.multiplicarPor(unidadeTrabalho.getQuantidade()));  
      salarioVerbaVencimento = salarioVerbaVencimento.multiplicarPor(unidadeTrabalho.getQuantidade());  
   }  
}

Após sair das condições o valor da verba é adicionado ao contexto.

Após o calculo do vencimento, será calculado o total de dias a receber a patir da data de admissão utilizando a seguinte estrutura:

Integer totalDiasAReceber = 0;  
servidorCalculoCtx.setTotalDiasEfetivo(TOTAL_DIAS_MES - totalDiasAdmissao - totalDiasComissionado - totalDiasFerias - totalDiasDesligamento);  
if (totalDiasComissionado.equals(0)) {  
   totalDiasAReceber = TOTAL_DIAS_MES - totalDiasAdmissao;  
} else {  
   if (totalDiasComissionado > 29) {  
      totalDiasAReceber = totalDiasComissionado;  
   } else {  
      totalDiasAReceber = TOTAL_DIAS_MES - totalDiasAdmissao - totalDiasComissionado;  
   }  
}  
  
totalDiasAReceber = totalDiasAReceber - totalDiasFerias - totalDiasDesligamento;

As variáveis são adicionadas ao contexto no fim da função.

Adiantamento do mês

Se o adiamento for definido como parâmetro do evento do cálculo, é chamada a função calcularAdiantamentoDoMes(), que obtém o valor do adiantamento do mês e se ele for maior que 0 o adiantamento do mês anterior é lançado na verba de desconto no mês atual. Ao final da execução, adiciona no contexto. Se não foi definido como parâmetro, é exibida uma mensagem no console

Cálculo CDS

Para o calculo do CDS é chamado o método calcularCds(), que percorre uma lista da verba dos cds executando o método executarCalculoCds() que adiciona no contexto as verbas de cds.

Desconto complemento saldo negativo

Se este parâmetro estiver definido, o método calcularDescontoComplementoSaldoNegativo() é chamado, verificando se existe verba rotina configurada e disparando mensagem no console caso não exista. Uma variável recebe o desconto do complemento do saldo negativo do mês anterior e lança na verba de desconto de saldo negativo no mês atual

// Obtem o desconto do complemento do saldo negativo do mês anterior  
Double descontoComplementoSaldoNegativoValor = financeiroMensalBO.buscarValorPorServidorEMesReferenciaEVerba(servidorId,  
      MesReferenciaUtil.retornarMesReferenciaAnterior(servidorEfetivo.getMesReferencia()),  
      rotinaPorVerbaIdMap.get(paramEventoCalculo.getComplementoSaldoNegativo().getId()));  
if (descontoComplementoSaldoNegativoValor > 0.0) {  
   VerbaAutomatica descontoComplementoSaldoNegVerbaAutomatica = new VerbaAutomatica(  
         rotinaPorVerbaIdMap.get(paramEventoCalculo.getDescontoComplementoSaldoNeg().getId()));  
   // O complemento do saldo negativo do mês anterior é lançado na verba de desconto de saldo negativo no mês atual  
   VerbaValorContexto verbaValorCtx = servidorCalculoCtx.getVerbaValorCtx();  
   verbaValorCtx.adicionarAoContexto(descontoComplementoSaldoNegVerbaAutomatica, new Fracao(descontoComplementoSaldoNegativoValor));  
   this.calculoVerba.acumularValores(servidorCalculoCtx, descontoComplementoSaldoNegVerbaAutomatica,  
         new Fracao(descontoComplementoSaldoNegativoValor));  
}

A seguir, as verbas eventuais e periódicas são carregadas em uma lista e dessa lista são separadas e removidas as verbas de consignação para fazer o calculo das verbas dos tipos eventual, periódica e adicional de tempo. Se houver algum afastamento, as verbas que serão pagas ao fim do afastamento são separadas.

Averbações não líquidas

As averbações não líquidas são calculadas iniciando pela montagem da lista de averbações com a função montarListaAverbacoes(), que percorre uma lista com as averbações encontradas para unificar em um Map. Se tiver valor reduzido, adiciona o valor da parcela ao item da averbação e também os valores normais.

// Verifica se há valor reduzido  
if (averbacaoItem.getValorParcelaReduzido() > 0) {  
   if (valorAverbacaoPorVerbaIdMap.get(averbacaoItem.getVerbaVisao().getId()) != null) {  
      valorAverbacaoPorVerbaIdMap.put(averbacaoItem.getVerbaVisao().getId(),  
            valorAverbacaoPorVerbaIdMap.get(averbacaoItem.getVerbaVisao().getId()) + (averbacaoItem.getValorParcelaReduzido()));  
      averbacaoItem.setValorParcela(averbacaoItem.getValorParcelaReduzido());  
   } else {  
      valorAverbacaoPorVerbaIdMap.put(averbacaoItem.getVerbaVisao().getId(), averbacaoItem.getValorParcelaReduzido());  
      averbacaoItem.setValorParcela(averbacaoItem.getValorParcelaReduzido());  
   }  
} else {  
   // Adiciona o valor normal da parcela  
   if (valorAverbacaoPorVerbaIdMap.get(averbacaoItem.getVerbaVisao().getId()) != null) {  
      valorAverbacaoPorVerbaIdMap.put(averbacaoItem.getVerbaVisao().getId(),  
            valorAverbacaoPorVerbaIdMap.get(averbacaoItem.getVerbaVisao().getId()) + averbacaoItem.getValorParcela());  
   } else {  
      valorAverbacaoPorVerbaIdMap.put(averbacaoItem.getVerbaVisao().getId(), averbacaoItem.getValorParcela());  
   }  
}

Em seguida define as verbas de exceção, que não poderão sem calculadas no momento

List<Integer> verbasExcecao = new ArrayList<>();  
// Adicionando a verba de previdência como verba de exceção  
if (paramPrevidencia != null) {  
   verbasExcecao.add(paramPrevidencia.getVerba().getCodigo());  
}  
  
// Adicionando a verba de imposto de renda como verba de exceção  
verbasExcecao.add(paramEventoCalculo.getImpostoRenda().getCodigo());

Em seguida, se a formula possuir cálculo sobre líquido, cada averbação com líquido será adicionada uma lista para persistir no financeiro e retorna essa lista. Se a averbação não possuir fórmula é adicionada na lista de averbação sem líquido e retorna essa lista. Finalmente, as averbação são calculadas com o método calcularVerbasAverbacao(), que inicia como a função anterior, percorrendo todas as averbações encontradas para unificar num Map e adicionada a uma lista de averbações para persistir no financeiro. Para cada verba agrupada é necessário verificar se há críticas. Para isso as averbações do mapa são percorridas, buscando valor caso a averbação seja de fórmula e tenha o valor zerado:

if (NumericoUtil.iguais(valorCalculado.doubleValor(), 0d)) {
...
}

dentro dessa validação são separadas as verbas de exceção (explicadas acima). Outra verificação é feita, para confirmar se existe alguma crítica com o tipo reduzida da verba encontrada se existir, é adicionado na lista de reduzidas:

if (pensionistaId == null) {  
   reduzidasList = analiseCriticaBO.buscarCriticaPorVerbaEStatusEMesReferencia(idServidor, verbaItemId, mesReferencia,  
         AnaliseCriticaStatusEnum.REDUZIR.getAbreviacao());  
} else {  
   reduzidasList = analiseCriticaBO.buscarCriticaPorVerbaEStatusEMesReferencia(idServidor, pensionistaId, verbaItemId, mesReferencia,  
         AnaliseCriticaStatusEnum.REDUZIR.getAbreviacao());  
}

Para cada crítica de redução de uma verba, é necessário atualizar a lista de averbações. Para isso, a lista de reduzidas é percorrida para definir os novos valores (depois de verificar se a lista de reduzidas não está vazia):

for (AnaliseCriticaDetalhe analiseCritica : reduzidasList) {  
  
   Fracao valorAverbacaoReduzido;  
   if (isCalculoComFormula) {  
      valorAverbacaoReduzido = valorCalculado;  
   } else {  
      valorAverbacaoReduzido = new Fracao(  
            averbacaoBO.buscarAverbacaoVisao(analiseCritica.getAverbacao().getId()).getValorParcela());  
   }  
   valorAverbacaoReduzido = valorAverbacaoReduzido.subtrairPor(new Fracao(analiseCritica.getValor()));  
  
   // Procura na lista de averbações para setar o novo valor  
   for (AverbacaoVisao averbacaoItemValor : averbacaoFinanceiroValorList) {  
      if (averbacaoItemValor.getCodigo().equals(analiseCritica.getAverbacao().getCodigo())) {  
         averbacaoItemValor.setValorParcela(analiseCritica.getValor());  
         averbacaoFinanceiroValorList.set(averbacaoFinanceiroValorList.indexOf(averbacaoItemValor), averbacaoItemValor);  
      }  
   }   valorCalculado = valorCalculado.subtrairPor(valorAverbacaoReduzido);  
}

Se a lista de reduzidas estiver vazia, o método verifica se existe alguma crítica do tipo SUSPENSA da verba encontrada e adicionando numa lista de dispensos:

// Verifica se existe alguma critica do tipo SUSPENSA da verba encontrada  
List<AnaliseCriticaDetalhe> suspensaList;  
if (pensionistaId == null) {  
   suspensaList = analiseCriticaBO.buscarCriticaPorVerbaEStatusEMesReferencia(idServidor, verbaItemId, mesReferencia,  
         AnaliseCriticaStatusEnum.SUSPENDER.getAbreviacao());  
  
} else {  
   suspensaList = analiseCriticaBO.buscarCriticaPorVerbaEStatusEMesReferencia(idServidor, pensionistaId, verbaItemId, mesReferencia,  
         AnaliseCriticaStatusEnum.SUSPENDER.getAbreviacao());  
}

Se não estiver vazia, a lista de suspensa é percorrida para subtrair a averbação do valor calculado

if (!suspensaList.isEmpty()) {  
   for (AnaliseCriticaDetalhe analiseCritica : suspensaList) {  
  
      Fracao valorAverbacaoSuspensa = valorCalculado;  
      if (!isCalculoComFormula) {  
         valorAverbacaoSuspensa = new Fracao(  
               averbacaoBO.buscarAverbacaoVisao(analiseCritica.getAverbacao().getId()).getValorParcela());  
      }  
      valorCalculado = valorCalculado.subtrairPor(valorAverbacaoSuspensa);  
      for (AverbacaoVisao averbacaoItemValor : averbacaoFinanceiroValorList) {  
         if (averbacaoItemValor.getCodigo().equals(analiseCritica.getAverbacao().getCodigo())) {  
            averbacaoItemValor.setValorParcela(0d);  
            averbacaoFinanceiroValorList.set(averbacaoFinanceiroValorList.indexOf(averbacaoItemValor), averbacaoItemValor);  
         }  
      }   
  }
}

A verba automática da averbação é enfim adicionada ao contexto com o valor calculado assim como resultado final do valor das averbações

VerbaAutomatica verbaAutomaticaAverbacao = new VerbaAutomatica(rotinaPorVerbaIdMap.get(verbaItemId), 1d);  
verbaValorCtx.adicionarAoContexto(verbaAutomaticaAverbacao, valorCalculado, 1d);  
this.acumularValores(servidorCalculoCtx, verbaAutomaticaAverbacao, valorCalculado);
// Adiciona no contexto de averbações o resultado final  
for (AverbacaoVisao averbacaoCalculadaItem : averbacaoFinanceiroValorList) {  
   averbacaoCtx.adicionarAoContexto(averbacaoCalculadaItem, new Fracao(averbacaoCalculadaItem.getValorParcela()));  
}

Vale transporte

Para o cálculo do vale transporte, é verificado se o percurso do servidor está nulo, se não estiver, é chamado o método calcularValeTransporte(), que vai buscar o percurso do servidor, utilizar como base o próximo mês referência, calcular em uma variável o total de dias úteis ignorando feriados facultativos, o total de dias do mês descontando afastamento e férias para usar como parâmetro para o método executarCalculoValeTransporte(). Este método pega a quantidade de transporte e multiplica pelo total de dias úteis

Fracao transporteQuantidade = new Fracao(percursoValor.getPercurso().getQuantidade());  
transporteQuantidade = transporteQuantidade.multiplicarPor(totalDiasUteis);

e multiplica o valor do transporte uteis pela quantidade de transporte

Fracao transporteValor = new Fracao(percursoValor.getValor());  
transporteValor = transporteValor.multiplicarPor(transporteQuantidade);

Se o valor do transporte for maior que zero e a rotina por verba não for nula, e se o valor do transporte for maior que o valor máximo da verba, o valor do transporte será o valor máximo da verba. Se o percurso consistir no teto e a fórmula não estiver vazia a fórmula é calculada e se o resultado dessa fórmula for menor que valor do transporte, o valor do transporte será o resultado da fórmula

Fracao resultadoFormula;  
if (percursoValor.getConsisteTeto()) {  
   String formula = rotinaPorVerbaIdMap.get(percursoValor.getVerba().getId()).getFormula();  
   if (!formula.trim().isEmpty()) {  
      resultadoFormula = formulaGerenciador.calcularFormula(formula, verbaValorCtx, moedaValorCtx,  
            servidorCalculoCtx.getVariavelValorCtx());  
      if (resultadoFormula.doubleValor() < transporteValor.doubleValor()) {  
         transporteValor = resultadoFormula;  
      }  
   }

Em seguida é lançada a verba automática do transporte e adicionado ao contexto a verba, o valor e a quantidade do transporte.

Complemento salarial

Se o servidor não possuir complemento uma mensagem no log é enviada. Se possuir é chamada o método calcularComplementoSalarial() para realizar o cálculo. Esse método verifica se o servidor pode receber a verba e calcula o complemento salarial, adicionando no contexto ao término

// Calcula o complemento salarial  
VerbaAutomatica complementoSalarialVerba = new VerbaAutomatica(  
      rotinaPorVerbaIdMap.get(paramEventoCalculo.getComplementoSalario().getId()), 0.0);  
  
this.calculoVerba.calcularVerbaAdicionarAoContexto(servidorCalculoCtx, servidorCalculoCtx.getTotalDiasAReceber(),  
      complementoSalarialVerba, moedaValorCtx, OperacaoTipoEnum.INCLUSAO_OU_SUBSTITUICAO, verbaPeriodicaContexto);

Para calcular as verbas que dependem do complemento salarial, é verificado se o servidor possui algum afastamento para separar as verbas que serão pagas após o afastamento.

if (!afastamentos.isEmpty()) {  
   calculoVerba.calcularVerbasDependentesDoComplemento(servidorCalculoCtx, moedaValorCtx, listaParamAdicionalTempo, paramEventoCalculo,  
         rotinaCalculo, listaTipoVerba,  
         verbaEventualList.stream().filter(item -> !item.getVerbaRotina().getVerba().getAfastamento()).collect(Collectors.toList()),  
         verbaPeriodicaList.stream().filter(item -> !item.getVerba().getAfastamento()).collect(Collectors.toList()),  
         verbaPeriodicaContexto);  
} else {  
   calculoVerba.calcularVerbasDependentesDoComplemento(servidorCalculoCtx, moedaValorCtx, listaParamAdicionalTempo, paramEventoCalculo,  
         rotinaCalculo, listaTipoVerba, verbaEventualList, verbaPeriodicaList, verbaPeriodicaContexto);  
}

Corte de teto

Para o cálculo da verba do corte de teto é chamado o método calcularCorteTetoSalarial(). Este método verifica se existe parâmetro para o cálculo. Se não houver uma mensagem é exibida no log. Se houver, calcula o corte de teto (não pode receber acima do teto). O valor excedente é desconsiderado. Em seguida, adiciona no contexto.

VerbaAutomatica corteTetoVerba = new VerbaAutomatica(rotinaPorVerbaIdMap.get(paramEventoCalculo.getCorteTeto().getId()), 0d);  
  
calculoVerba.calcularVerbaAdicionarAoContexto(servidorCalculoCtx, servidorCalculoCtx.getTotalDiasAReceber(), corteTetoVerba,  
      moedaValorCtx, OperacaoTipoEnum.INCLUSAO_OU_SUBSTITUICAO, verbaPeriodicaContexto);

Proporcionalidade admissão

Se o total de dias a receber for menor que 30 e o servidor possuir a categoria salarial mensal, é chamado o método calcularProporcionalidade(), adiciona um novo valor para a verba a receber de acordo com a quantidade de dias trabalhado e adiciona ao contexto.

Afastamento

Esse cálculo é feito pelo método calcularAfastamento(), que irá buscar todos os afastamentos do servidor para o determinado mês de referência.

Proporcionalidade periódicas admissão

Cálculo da proporcionalidade das verbas periódicas será feito em caso de mês fora da admissão. A verba é percorrida e se o valor for zerado não faz a proporção. O novo valor da verba será a divisão do valor atual pela multiplicação do total de dias a receber vezes a quantidade de verbas

novoValorVerba = valorVerba.dividirPor(servidorCalculoCtx.getTotalDiasAReceber()).multiplicarPor(quantidade);

O novo valor será sobrescrito no contexto.

Proporcionalidade unidade de trabalho

Utiliza o método calcularProporcionalidadeUnidadeTrabalho() para calcular os valores das verbas baseados na quantidade total "contratada" do mês em relação a quantidade lançada. Esse método inicia algumas variáveis:

  • quantidade contratada de Unidade de Trabalho no mês
  • quantidade de dias afastados
  • Calcula o total de dias de trabalho no mês sem Afastamento
  • quantidade de Unidade de Trabalho lançada para o servidor no mês Se a unidade de trabalho estiver nula, os dados do calculo são zerados e uma mensagem é exibida no log. Em seguida o método inicia cálculo para verificar se o total lançado é proporcional aos dias trabalhados
Fracao quantidadeIdealMesEncontrada = quantidadeTotalMes;  
quantidadeIdealMesEncontrada = quantidadeIdealMesEncontrada.dividirPor(totalDiasMes);

Multiplica pelos dias trabalhados para encontrar quanto ele deveria ter trabalhado no mês

quantidadeIdealMesEncontrada = quantidadeIdealMesEncontrada.multiplicarPor(totalDiasReceber);

Fator de multiplicação de verbas que não fazem parte do afastamento é calculado sobre o mês todo

Fracao fatorMultiplicacaoSemAfastamento = new Fracao(1);  
if (quantidadeTotalMes.doubleValor() != 0) {  
   fatorMultiplicacaoSemAfastamento = unidadeTrabalhoQuantidade;  
   fatorMultiplicacaoSemAfastamento = fatorMultiplicacaoSemAfastamento.dividirPor(quantidadeTotalMes);  
}

Fator de multiplicação de verbas que fazem parte do afastamento é calculado sobre os dias sem afastamento

Fracao fatorMultiplicacaoComAfastamento = unidadeTrabalhoQuantidade;  
if (quantidadeIdealMesEncontrada.doubleValor() > 0) {  
   fatorMultiplicacaoComAfastamento = fatorMultiplicacaoComAfastamento.dividirPor(quantidadeIdealMesEncontrada);  
} else {  
   fatorMultiplicacaoComAfastamento = new Fracao(1);  
}

Se a quantidade que ele deveria ter trabalhado for diferente da quantidade lançada, é necessário calcular os valores de todas as verbas baseadas nesta quantidade. Se estiver marcado para ser proporcional, faz o cálculo

Fracao novoValorVerbaCtx;  
Fracao valorOriginalVerbaCtx;  
// se estiver marcado para ser proporcional, faz o calculo  
if (verbaValorCtx.buscarVerbaPorVerbaCodigo(verbaCodigoCtx).getProporcional()) {  
   novoValorVerbaCtx = verbaValorCtx.buscarValorPorVerbaCodigo(verbaCodigoCtx);  
   valorOriginalVerbaCtx = verbaValorCtx.buscarValorPorVerbaCodigo(verbaCodigoCtx);  
   if (calculoAfastamento.ehVerbaAgregada(verbaCodigoCtx)) {  
      novoValorVerbaCtx = novoValorVerbaCtx.multiplicarPor(fatorMultiplicacaoComAfastamento);  
   } else {  
      novoValorVerbaCtx = novoValorVerbaCtx.multiplicarPor(fatorMultiplicacaoSemAfastamento);  
   }  
   verbaValorCtx.adicionarAoContexto(verbaValorCtx.buscarVerbaPorVerbaCodigo(verbaCodigoCtx), novoValorVerbaCtx,  
         unidadeTrabalho.getQuantidade());  
   this.calculoVerba.alterarBases(servidorCalculoCtx, verbaValorCtx.buscarVerbaPorVerbaCodigo(verbaCodigoCtx),  
         novoValorVerbaCtx.doubleValor(), valorOriginalVerbaCtx.doubleValor());  
}

No fim, adiciona ao contexto as verbas.

Comissionado

Para calcular diferença do período comissionado ou efetivo dentro do mês, é utilizado o método calcularDiferenca(), que calcula os dias do vencimento baseado no salário do período com outro método calcularDiferençaSalario(), que verifica qual categoria salarial o servidor se encontra e calcula a diferença salarial:

  • Se for mensal: divide por 30 o salário e multiplica o resultado pelo total de dias a receber.
  • Se for unidade de trabalho busca verbas que não dependem do complemento salario, verbas eventuais que não dependem do complemento salarial definidas dentro do período do mês referência, periódicas que não dependem do complemento salarial definidas dentro do período do mês referência, verbas de adicional tempo e adiciona numa lista de verbas para então calculá-las Ao fim do cálculo, é adicionado no contexto a verba de vencimento. e os valores são agrupados.

Pensão judicial não líquida

O método calcularPensaoJudicialNaoLiquida() é usado. Ele carrega lista de pensões a serem pagas e se essa lista estiver vazia, retorna de volta para execução normal. Caso não esteja vazia, verifica se as rotinas de adiantamento décimo terceiro, folha complementar e férias estão marcadas e realiza o filtro em cada uma. A lista de pensão é entao percorrida para o cálculo. Se for parcela final, busca o valor do adiantamento e o insere numa variável. Em seguida verifica se a fórmula da pensão apresenta variáveis, ou códigos de verba os quais a pensão será calculada descontando IRRF e INSS:

if (!ehPensaoLiquida(itemPensaoJudicial.getFormula(), paramPrevidencia, paramEventoCalculo)) {  
   if (verbaValorCtx.buscarQuantidadePorVerbaCodigo(verbaPensao.getCodigoVerba()) == null) {  
      quantidadePensao = 1.0;  
   } else {  
      quantidadePensao = verbaValorCtx.buscarQuantidadePorVerbaCodigo(verbaPensao.getCodigoVerba()) + 1;  
   }

O cálculo então é feito baseado na fórmula:

valorPensao = calculoVerba.buscarValorResultadoFormula(servidorCalculoCtx, itemPensaoJudicial, moedaValorCtx);  
valorPensao = valorPensao.subtrairPor(valorAdiantado);

Em seguida os valores são validados e adicionados ao contexto.

Verbas não acumuladoras

As verbas eventuais e periódicas são carregadas em uma lista e dessa lista são separadas e removidas as consignações para calcular as verbas dependentes do complemento. Esse calculo é feito com uma lista de verbas dependente de complemento e em seguida recupera do contexto verbas que não dependem do inss, irrf e do salário líquido.

FGTS

O calculo do FGST é feito com o método calcularFGTS, que verifica se o FGTS base é maior que zero e então calcula listando os parâmetros de FGTS e multiplicando pelo valor base do cálculo do FGTS, depois de confirmar que os parâmetros não estão nulos

ParamFgts paramFgts = paramFgtsBO.buscarTodos(servidorEfetivo.getVinculoEmpregaticioId(), servidorEfetivo.getClassificacaoFuncionalId());  
  
if (paramFgts == null) {  
   throw new RestricaoNegocioExcecao(  
         String.format("Não há parametrização de FGTS para o vínculo empregatício: [%s][%s] e classificação funcional: [%s][%s]",  
               servidorEfetivo.getVinculoEmpregaticioId(), servidorEfetivo.getVinculoEmpregaticioDescricao(),  
               servidorEfetivo.getVinculoEmpregaticioId(), servidorEfetivo.getVinculoEmpregaticioDescricao()));  
}  
  
Fracao valorFgts = new Fracao(paramFgts.getPercentualCalculo());  
valorFgts = valorFgts.multiplicarPor(calculoCtx.getFgtsBaseCalculoValor());

IRRF

Para o cálculo do irrf, é feita antes uma verificação se o servidor processa o irrf e se o laudo moléstia é nula. Se a condição for satisfeita, o método calcularIrrf() é chamado. Vale lembrar que ao calcular o irrf, caso haja uma verba ou calcule sobre um salário líquido, deverá haver um recálculo de irrf. O método separa em variáveis o contexto de cálculo e a idade do servidor e busca a quantidade de dependentes de um servidor.

CalculoContexto calculoCtx = servidorCalculoCtx.getCalculoCtx();  
Integer idade = servidorCalculoCtx.getServidorEfetivo()  
      .getIdade(MesReferenciaUtil.retornarUltimoDiaMes(servidorCalculoCtx.getServidorEfetivo().getMesReferencia()));
Long dependenteQuantidade = servidorMensalBO.buscarQuantidadeDependenteIrrf(servidorCalculoCtx.getServidorEfetivo().getServidor().getId(),  
      servidorCalculoCtx.getServidorEfetivo().getMesReferencia());

Em seguida, verifica se o valor da verba base do irrf é maior que 0 e realiza duas buscas na lista do financeiro mensal para adicionar em uma variável:

  • buscarBaseIrrf():
Fracao baseIrrf = new Fracao(0d);  
for (FinanceiroMensal item : financeiroList) {  
   baseIrrf = baseIrrf.somarCom(new Fracao(item.getIrrfFolhaBase()));  
}  
return baseIrrf.doubleValor();
  • buscarValorIrrf():
Fracao valorIrrf = new Fracao(0d);  
for (FinanceiroMensal item : financeiroList) {  
  valorIrrf = valorIrrf.somarCom(new Fracao(item.getIrrfFolhaValor()));  
}  
return valorIrrf.doubleValor();  

Em seguida usa essas variáveis para calcular o irrf com o método calcularIrrf(). Nesse método se a verba for nula, o sistema lança uma exceção informando que não há parametrização. O método busca o parâmetro do irrf de acordo com o vínculo e classificação do servidor:

ParamIrrf paramIrrf = paramIrrfBO.buscarTodos(servidorEfetivo.getVinculoEmpregaticioId(), servidorEfetivo.getClassificacaoFuncionalId(),  
      0L);

e se este for nulo, lança uma exceção. Em seguida busca os índices de irrf de acordo com a tabela parametrizada e os coloca em uma lista:

List<IndiceIrrf> listaIndiceIrrf = indiceIrrfBO.buscarTodosPorIdTabelaIndicePorIdEmpresaAndMesReferencia(  
      paramIrrf.getTabelaIndice().getId(), servidorCalculoCtx.getServidorEfetivo().getEmpresaId(), servidorEfetivo.getMesReferencia());

se essa lista estiver vazia, o sistema lança uma exceção; Em seguida as deduções de dependente são feitas:

Fracao baseCalculoIrrf = new Fracao(calculoCtx.getIrrfBaseCalculoVerbaValor());  
baseCalculoIrrf = baseCalculoIrrf.somarCom(new Fracao(valorBaseOutraRotina));  
Fracao deducaoDependente = new Fracao(listaIndiceIrrf.get(0).getDeducaoDependente());  
deducaoDependente = deducaoDependente.multiplicarPor(dependenteQuantidade);  
baseCalculoIrrf = baseCalculoIrrf.subtrairPor(deducaoDependente);  
deducaoTotal = deducaoTotal.somarCom(deducaoDependente);

em seguida verifica se a Classificação Funcional é Pensionista ou Aposentado, zera as bases de IR caso esteja negativa e verifica se o servidor ou o pensionista tem 65 anos ou mais para fazer a dedução:

if (NumericoUtil.menorIgual(baseCalculoIrrf.doubleValor(), zeroFracao.doubleValor())) {  
   baseCalculoIrrf = zeroFracao;  
}

if (idade > 64) {  
   baseCalculoIrrf = baseCalculoIrrf.subtrairPor(new Fracao(listaIndiceIrrf.get(0).getDeducaoAposentado065()));  
   deducaoTotal = deducaoTotal.somarCom(new Fracao(listaIndiceIrrf.get(0).getDeducaoAposentado065()));  
}

Percorre a tabela de previdência para encontrar a faixa de previdência e fazer a dedução do valor do irrf

Fracao parcelaDeducao = zeroFracao;  
Fracao aliquota = zeroFracao;  
for (IndiceIrrf itemIrrf : listaIndiceIrrf) {  
   if (baseCalculoIrrf.doubleValor() >= itemIrrf.getBaseCalculo()) {  
      parcelaDeducao = new Fracao(itemIrrf.getParcelaDeducao());  
      aliquota = new Fracao(itemIrrf.getAliquota());  
      aliquota = aliquota.dividirPor(100);  
      } else {  
      break;  
   }  
}  
deducaoTotal = deducaoTotal.somarCom(parcelaDeducao);  
Fracao valorIrrf = baseCalculoIrrf.multiplicarPor(aliquota);  
valorIrrf = valorIrrf.subtrairPor(parcelaDeducao);  
valorIrrf = valorIrrf.subtrairPor(new Fracao(valorIrrfOutraRotina));

As bases do valor irrf antigo são alteradas e acumuladas para definir os novos valores de base de cálculo, valor da alíquota, valor do irrf, dedução e quantidade de dependentes ao contexto:

calculoCtx.setIrrfBaseCalculoValor(baseCalculoIrrf.doubleValor());  
calculoCtx.setIrrfAliquotaValor(aliquota.multiplicarPor(100).doubleValor());  
calculoCtx.setIrrfValor(NumericoUtil.arredondarDouble(valorIrrf.doubleValor(), 2));  
calculoCtx.setIrrfDeducaoValor(deducaoTotal.doubleValor());  
calculoCtx.setIrrfDependenteQuantidade(dependenteQuantidade);

verbaValorCtx.adicionarAoContexto(verbaIrrf, valorIrrf, aliquota.multiplicarPor(100).doubleValor());
Adequar alíquota IRRF

Visto que o contexto do possui um id de lotação, teoricamente seria possível buscar as matrículas de um servidor buscando seu CPF utilizando a própria matrícula.

A seguinte consulta pode ser usada de base para iniciar essa adequação:

 SELECT
	PF.CPF,
	S.ID_LOTACAO_CONTRATACAO,
	COUNT(PF.CPF)
FROM
	GOVERNA.RH.SERVIDOR S
JOIN GOVERNA.ADMIN.PESSOA_FISICA PF ON
	S.ID_PESSOA = PF.ID
GROUP BY
	PF.CPF,
	S.ID_LOTACAO_CONTRATACAO
HAVING
	COUNT(PF.CPF) > 1
;

Averbações líquidas

De início, a lista de averbação líquida é montada e em seguida as consignações são separadas e removidas para então realizar o cálculo de averbação já explicado anteriormente com o método calcularVerbasAverbacao().

Pensão judicial líquida

Calcula primeiro as consignações, adicionando cada averbação em uma lista, sendo elas verbas eventuais e periódicas e em seguida é calculado utilizando o método calcularVerbasConsignacao() que verifica se as listas de verbas eventuais e periódicas não estão vazias e realiza o cálculo de cada.

Recalcular IRRF

O irrf é recalculado da mesma maneira vista anteriormente, com a diferença de que é feita antes uma verificação se o servidor tem pensão judicial líquida ou verba líquida

Salário abono família

Para este cálculo é verificado se o parâmetro de abono salário família não está nulo e se o servidor processa esse abono. Case a condição seja verdadeira, o método calcularSalarioAbonoFamilia() é chamado. Ele verifica se a base de cálculo é maior que zero e realiza o cálculo buscando os índices de abono/salário família de acordo com a tabela parametrizada e os adiciona numa lista. Em seguida verifica se o tipo é Abono ou Salário Família para saber qual base será utilizada no cálculo e busca a quantidade de dependentes. Percorre a tabela de abono/salário família para encontrar a faixa correspondente:

Fracao valorSalarioFamilia = zeroFracao;  
for (IndiceSalarioFamilia itemSalarioFamilia : listaIndiceSalarioFamilia) {  
   if (baseCalculoAbonoSalarioFamilia <= itemSalarioFamilia.getBaseCalculo()) {  
      if (itemSalarioFamilia.getValorDependente() != 0) {  
         valorSalarioFamilia = new Fracao(itemSalarioFamilia.getValorDependente());  
         valorSalarioFamilia = valorSalarioFamilia.multiplicarPor(quantidadeDependente);  
         break;  
      } else {  
         valorSalarioFamilia = new Fracao(baseCalculoAbonoSalarioFamilia);  
         valorSalarioFamilia = valorSalarioFamilia.multiplicarPor(new Fracao(itemSalarioFamilia.getPercentualDependente()));  
         valorSalarioFamilia = valorSalarioFamilia.multiplicarPor(quantidadeDependente);  
         break;  
      }  
   }}
if (servidorEfetivo.getAdmissaoData().after(MesReferenciaUtil.retornarPrimeiroDiaMes(servidorEfetivo.getMesReferencia()))) {  
   Integer totalDiasMes = 30  
         - (DateUtil.calcularDiferencaDias(MesReferenciaUtil.retornarPrimeiroDiaMes(servidorEfetivo.getMesReferencia()),  
               servidorEfetivo.getAdmissaoData()) - 1);  
   valorSalarioFamilia = valorSalarioFamilia.dividirPor(30);  
   valorSalarioFamilia = valorSalarioFamilia.multiplicarPor(totalDiasMes);  
}

Se o valor do salário família for maior que 0, adiciona o valor ao contexto.

Complemento de saldo negativo

Se o valor de desconto for maior que o valor do salário bruto o método calcularComplementoSaldoNegativo() é chamado. Ele verifica novamente se o valor do desconto é maior que o valor do salário bruto o método e faz a dedução:

if (calculoCtx.getDescontoValor() > calculoCtx.getSalarioBrutoValor()) {  
   Fracao valorComplemento = new Fracao(calculoCtx.getDescontoValor());  
   valorComplemento = valorComplemento.subtrairPor(new Fracao(calculoCtx.getSalarioBrutoValor()));  
   valorComplemento = valorComplemento.abs();  
   verbaValorCtx.adicionarAoContexto(verbaComplemento, valorComplemento, 1d);  
   this.calculoVerba.acumularValores(servidorCalculoCtx, verbaComplemento, valorComplemento);  
}

Fim do cálculo

Finalmente o cálculo termina, retornando o contexto de cálculo do método calcularPagamentoFolha(), verificando se existem erros no relatório, enviando a mensagem de sucesso para o log e definindo a execução como falsa.