FINERACT-1981: fix progressive accruals on late repayment

This commit is contained in:
adam.magyari
2025-02-04 11:47:52 +01:00
committed by Adam Saghy
parent 0b9687d2aa
commit 157a492bcc
5 changed files with 220 additions and 7 deletions
@@ -0,0 +1,32 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.data;
import java.math.BigDecimal;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class AccrualBalances {
private BigDecimal interestPortion = BigDecimal.ZERO;
private BigDecimal feePortion = BigDecimal.ZERO;
private BigDecimal penaltyPortion = BigDecimal.ZERO;
}
@@ -56,6 +56,7 @@ import org.apache.fineract.infrastructure.event.business.service.BusinessEventNo
import org.apache.fineract.infrastructure.jobs.exception.JobExecutionException;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.data.AccrualBalances;
import org.apache.fineract.portfolio.loanaccount.data.AccrualChargeData;
import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodData;
import org.apache.fineract.portfolio.loanaccount.data.AccrualPeriodsData;
@@ -698,13 +699,52 @@ public class LoanAccrualsProcessingServiceImpl implements LoanAccrualsProcessing
LoanRepaymentScheduleInstallment lastInstallment = loan.getLastLoanRepaymentScheduleInstallment();
LocalDate lastDueDate = lastInstallment.getDueDate();
if (isProgressiveAccrual(loan)) {
AccrualPeriodsData accrualPeriods = calculateAccrualAmounts(loan, lastDueDate, true);
for (AccrualPeriodData period : accrualPeriods.getPeriods()) {
Money interestAccrued = period.getTransactionAccrued();
Money feeAccrued = period.getFeeTransactionAccrued();
Money penaltyAccrued = period.getPenaltyTransactionAccrued();
LoanRepaymentScheduleInstallment installment = loan.fetchRepaymentScheduleInstallment(period.getInstallmentNumber());
installment.updateAccrualPortion(interestAccrued, feeAccrued, penaltyAccrued);
AccrualBalances accrualBalances = new AccrualBalances();
accrualTransactions.forEach(lt -> {
switch (lt.getTypeOf()) {
case ACCRUAL -> {
accrualBalances.setFeePortion(MathUtil.add(accrualBalances.getFeePortion(), lt.getFeeChargesPortion()));
accrualBalances.setPenaltyPortion(MathUtil.add(accrualBalances.getPenaltyPortion(), lt.getPenaltyChargesPortion()));
accrualBalances.setInterestPortion(MathUtil.add(accrualBalances.getInterestPortion(), lt.getInterestPortion()));
}
case ACCRUAL_ADJUSTMENT -> {
accrualBalances.setFeePortion(MathUtil.subtract(accrualBalances.getFeePortion(), lt.getFeeChargesPortion()));
accrualBalances
.setPenaltyPortion(MathUtil.subtract(accrualBalances.getPenaltyPortion(), lt.getPenaltyChargesPortion()));
accrualBalances
.setInterestPortion(MathUtil.subtract(accrualBalances.getInterestPortion(), lt.getInterestPortion()));
}
default -> throw new IllegalStateException("Unexpected value: " + lt.getTypeOf());
}
});
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
BigDecimal maximumAccruableInterest = MathUtil.nullToZero(installment.getInterestCharged());
BigDecimal maximumAccruableFee = MathUtil.nullToZero(installment.getFeeChargesCharged());
BigDecimal maximumAccruablePenalty = MathUtil.nullToZero(installment.getPenaltyCharges());
if (MathUtil.isLessThanOrEqualTo(maximumAccruableInterest, accrualBalances.getInterestPortion())) {
installment.setInterestAccrued(maximumAccruableInterest);
accrualBalances.setInterestPortion(accrualBalances.getInterestPortion().subtract(maximumAccruableInterest));
} else {
installment.setInterestAccrued(accrualBalances.getInterestPortion());
accrualBalances.setInterestPortion(BigDecimal.ZERO);
}
if (MathUtil.isLessThanOrEqualTo(maximumAccruableFee, accrualBalances.getFeePortion())) {
installment.setFeeAccrued(maximumAccruableFee);
accrualBalances.setFeePortion(accrualBalances.getFeePortion().subtract(maximumAccruableFee));
} else {
installment.setFeeAccrued(accrualBalances.getFeePortion());
accrualBalances.setFeePortion(BigDecimal.ZERO);
}
if (MathUtil.isLessThanOrEqualTo(maximumAccruablePenalty, accrualBalances.getPenaltyPortion())) {
installment.setPenaltyAccrued(maximumAccruablePenalty);
accrualBalances.setPenaltyPortion(accrualBalances.getPenaltyPortion().subtract(maximumAccruablePenalty));
} else {
installment.setPenaltyAccrued(accrualBalances.getPenaltyPortion());
accrualBalances.setPenaltyPortion(BigDecimal.ZERO);
}
}
} else {
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();