FINERACT-1981: fix pay-off for TILL_REST_FREQUENCY_DATE

This commit is contained in:
Kristof Jozsa
2024-09-18 09:33:00 +02:00
committed by Adam Saghy
parent e0071715d2
commit c511385946
3 changed files with 33 additions and 9 deletions
@@ -512,6 +512,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
Money penaltyPortionOfTransaction = Money.zero(currency); Money penaltyPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) { if (transactionAmountRemaining.isZero()) {
// checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
return penaltyPortionOfTransaction; return penaltyPortionOfTransaction;
} }
@@ -538,6 +539,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency(); final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money feePortionOfTransaction = Money.zero(currency); Money feePortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) { if (transactionAmountRemaining.isZero()) {
// checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
return feePortionOfTransaction; return feePortionOfTransaction;
} }
final Money feeChargesDue = getFeeChargesOutstanding(currency); final Money feeChargesDue = getFeeChargesOutstanding(currency);
@@ -563,6 +565,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency(); final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
Money interestPortionOfTransaction = Money.zero(currency); Money interestPortionOfTransaction = Money.zero(currency);
if (transactionAmountRemaining.isZero()) { if (transactionAmountRemaining.isZero()) {
// checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
return interestPortionOfTransaction; return interestPortionOfTransaction;
} }
final Money interestDue = getInterestOutstanding(currency); final Money interestDue = getInterestOutstanding(currency);
@@ -588,6 +591,7 @@ public class LoanRepaymentScheduleInstallment extends AbstractAuditableWithUTCDa
final MonetaryCurrency currency = transactionAmount.getCurrency(); final MonetaryCurrency currency = transactionAmount.getCurrency();
Money principalPortionOfTransaction = Money.zero(currency); Money principalPortionOfTransaction = Money.zero(currency);
if (transactionAmount.isZero()) { if (transactionAmount.isZero()) {
checkIfRepaymentPeriodObligationsAreMet(transactionDate, currency);
return principalPortionOfTransaction; return principalPortionOfTransaction;
} }
final Money principalDue = getPrincipalOutstanding(currency); final Money principalDue = getPrincipalOutstanding(currency);
@@ -26,6 +26,8 @@ import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.FE
import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.INTEREST; import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.INTEREST;
import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.PENALTY; import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.PENALTY;
import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.PRINCIPAL; import static org.apache.fineract.portfolio.loanproduct.domain.AllocationType.PRINCIPAL;
import static org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy.TILL_PRE_CLOSURE_DATE;
import static org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy.TILL_REST_FREQUENCY_DATE;
import static org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTransactionType.DEFAULT; import static org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationTransactionType.DEFAULT;
import java.math.BigDecimal; import java.math.BigDecimal;
@@ -85,6 +87,7 @@ import org.apache.fineract.portfolio.loanproduct.domain.AllocationType;
import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType; import org.apache.fineract.portfolio.loanproduct.domain.CreditAllocationTransactionType;
import org.apache.fineract.portfolio.loanproduct.domain.DueType; import org.apache.fineract.portfolio.loanproduct.domain.DueType;
import org.apache.fineract.portfolio.loanproduct.domain.FutureInstallmentAllocationRule; import org.apache.fineract.portfolio.loanproduct.domain.FutureInstallmentAllocationRule;
import org.apache.fineract.portfolio.loanproduct.domain.LoanPreClosureInterestCalculationStrategy;
import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail; import org.apache.fineract.portfolio.loanproduct.domain.LoanProductRelatedDetail;
import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType; import org.apache.fineract.portfolio.loanproduct.domain.PaymentAllocationType;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -1231,10 +1234,21 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
if (transactionCtx instanceof ProgressiveTransactionCtx ctx && loan.isInterestBearing() if (transactionCtx instanceof ProgressiveTransactionCtx ctx && loan.isInterestBearing()
&& loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled()) { && loan.getLoanProductRelatedDetail().isInterestRecalculationEnabled()) {
ProgressiveLoanInterestScheduleModel model = ctx.getModel(); ProgressiveLoanInterestScheduleModel model = ctx.getModel();
LocalDate transactionDate = loanTransaction.getTransactionDate(); LoanPreClosureInterestCalculationStrategy strategy = loanTransaction.getLoan().getLoanProduct()
LocalDate payDate = inAdvanceInstallment.getFromDate().isAfter(transactionDate) .preCloseInterestCalculationStrategy();
? inAdvanceInstallment.getFromDate()
: transactionDate; LocalDate payDate = switch (strategy) {
case TILL_PRE_CLOSURE_DATE -> {
LocalDate transactionDate = loanTransaction.getTransactionDate();
yield inAdvanceInstallment.getFromDate().isAfter(transactionDate)
? inAdvanceInstallment.getFromDate()
: transactionDate;
}
case TILL_REST_FREQUENCY_DATE -> inAdvanceInstallment.getDueDate();
case NONE ->
throw new IllegalStateException("Unexpected PreClosureInterestCalculationStrategy: NONE");
};
ProgressiveLoanInterestRepaymentModel payableDetails = emiCalculator ProgressiveLoanInterestRepaymentModel payableDetails = emiCalculator
.getPayableDetails(model, inAdvanceInstallment.getDueDate(), payDate).orElseThrow(); .getPayableDetails(model, inAdvanceInstallment.getDueDate(), payDate).orElseThrow();
@@ -1253,13 +1267,19 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
switch (paymentAllocationType) { switch (paymentAllocationType) {
case IN_ADVANCE_PRINCIPAL -> { case IN_ADVANCE_PRINCIPAL -> {
emiCalculator.addBalanceCorrection(model, payDate, Money balance = switch (strategy) {
payableDetails.getOutstandingBalance().multipliedBy(-1)); case TILL_PRE_CLOSURE_DATE -> payableDetails.getOutstandingBalance();
case TILL_REST_FREQUENCY_DATE -> payableDetails.getRemainingBalance();
default -> throw new IllegalStateException();
};
emiCalculator.addBalanceCorrection(model, payDate, balance.multipliedBy(-1));
emiCalculator.addBalanceCorrection(model, payDate, emiCalculator.addBalanceCorrection(model, payDate,
payableDetails.getPrincipalDue().minus(paidPortion)); payableDetails.getPrincipalDue().minus(paidPortion));
} }
case IN_ADVANCE_INTEREST -> emiCalculator.addBalanceCorrection(model, payDate, case IN_ADVANCE_INTEREST -> {
payableDetails.getInterestDue().minus(paidPortion)); emiCalculator.addBalanceCorrection(model, payDate,
payableDetails.getInterestDue().minus(paidPortion));
}
default -> { default -> {
} }
} }
@@ -251,7 +251,7 @@ public class ProgressiveLoanScheduleGenerator implements LoanScheduleGenerator {
case TILL_PRE_CLOSURE_DATE -> onDate; case TILL_PRE_CLOSURE_DATE -> onDate;
case TILL_REST_FREQUENCY_DATE -> // find due date of current installment case TILL_REST_FREQUENCY_DATE -> // find due date of current installment
installments.stream().filter(it -> it.getFromDate().isBefore(onDate) && it.getDueDate().isAfter(onDate)).findFirst() installments.stream().filter(it -> it.getFromDate().isBefore(onDate) && it.getDueDate().isAfter(onDate)).findFirst()
.orElseThrow(() -> new IllegalStateException("No installment found for transaction date: " + onDate)).getDueDate(); .orElse(installments.get(0)).getDueDate();
case NONE -> throw new IllegalStateException("Unexpected PreClosureInterestCalculationStrategy: NONE"); case NONE -> throw new IllegalStateException("Unexpected PreClosureInterestCalculationStrategy: NONE");
}; };