From 71d2e51051397b23140c7b22f54f0db9f98287e2 Mon Sep 17 00:00:00 2001 From: Peter Bagrij Date: Mon, 30 Oct 2023 16:52:19 +0100 Subject: [PATCH] FINERACT-1971: Fixing obligation met on date when adding charge after maturity --- ...RepaymentScheduleTransactionProcessor.java | 7 ++ ...playWithAdvancedPaymentAllocationTest.java | 65 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java index bfea29cbc..f93012fda 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/transactionprocessor/AbstractLoanRepaymentScheduleTransactionProcessor.java @@ -388,6 +388,13 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen if (lastInstallment.isAdditional() && lastInstallment.getDue(currency).isZero()) { installments.remove(lastInstallment); } + // TODO: rewrite and handle it at the proper place when disbursement handling got fixed + for (LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : installments) { + if (loanRepaymentScheduleInstallment.getTotalOutstanding(currency).isGreaterThanZero()) { + loanRepaymentScheduleInstallment.updateObligationMet(false); + loanRepaymentScheduleInstallment.updateObligationMetOnDate(null); + } + } } private void recalculateCreditTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction, diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeReveseReplayWithAdvancedPaymentAllocationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeReveseReplayWithAdvancedPaymentAllocationTest.java index 0334570cb..9ef638155 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeReveseReplayWithAdvancedPaymentAllocationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanAccountChargeReveseReplayWithAdvancedPaymentAllocationTest.java @@ -23,6 +23,7 @@ import static java.lang.Boolean.TRUE; import static org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType.BUSINESS_DATE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import io.restassured.builder.RequestSpecBuilder; import io.restassured.builder.ResponseSpecBuilder; @@ -277,6 +278,70 @@ public class LoanAccountChargeReveseReplayWithAdvancedPaymentAllocationTest { }); } + @Test + public void testObligationMetDateIsNotMetOnExtraInstallment() { + runAt("1 September 2022", () -> { + String loanExternalIdStr = UUID.randomUUID().toString(); + final Integer loanProductID = createLoanProductWithPeriodicAccrualAccounting(true); + final Integer clientId = clientHelper.createClient(ClientHelper.defaultClientCreationRequest()).getClientId().intValue(); + final Integer loanId = createLoanAccount(clientId, loanProductID, loanExternalIdStr, true, "1 September 2022", + "1 September 2022"); + + // make a repayment on 3rd od Sept + updateBusinessDate("3 September 2022"); + final PostLoansLoanIdTransactionsResponse repaymentTransaction = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("3 September 2022").locale("en") + .transactionAmount(100.0)); + + // apply charges on 4th of Sept backdated to 2nd of Sept 2022 + updateBusinessDate("4 September 2022"); + Integer feeCharge = ChargesHelper.createCharges(requestSpec, responseSpec, + ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "10", false)); + + LocalDate targetDate = LocalDate.of(2022, 9, 2); + final String feeCharge1AddedDate = DATE_FORMATTER.format(targetDate); + Integer feeLoanChargeId = loanTransactionHelper.addChargesForLoan(loanId, + LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(feeCharge), feeCharge1AddedDate, "10")); + + // apply penalty + Integer penalty = ChargesHelper.createCharges(requestSpec, responseSpec, + ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "20", true)); + + final String penaltyCharge1AddedDate = DATE_FORMATTER.format(targetDate); + + Integer penalty1LoanChargeId = this.loanTransactionHelper.addChargesForLoan(loanId, + LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(penalty), penaltyCharge1AddedDate, "20")); + + // make a full repayment of 10th of September + updateBusinessDate("10 September 2022"); + PostLoansLoanIdTransactionsResponse fullRepayment = loanTransactionHelper.makeLoanRepayment(loanExternalIdStr, + new PostLoansLoanIdTransactionsRequest().dateFormat("dd MMMM yyyy").transactionDate("10 September 2022").locale("en") + .transactionAmount(930.0)); + + GetLoansLoanIdResponse loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); + assertNotNull(loanDetails.getRepaymentSchedule()); + assertNotNull(loanDetails.getRepaymentSchedule().getPeriods()); + assertEquals(2, loanDetails.getRepaymentSchedule().getPeriods().size()); + assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPenaltyChargesOutstanding()); + assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getFeeChargesOutstanding()); + assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getPrincipalOutstanding()); + assertEquals(0.0, loanDetails.getRepaymentSchedule().getPeriods().get(1).getTotalOutstandingForPeriod()); + + // adding an extra charge after maturity + updateBusinessDate("11 October 2022"); + Integer snoozeFee = ChargesHelper.createCharges(requestSpec, responseSpec, + ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT, "30.0", false)); + loanTransactionHelper.addChargesForLoan(loanId, + LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(snoozeFee), "11 October 2022", "30.0")); + + loanDetails = this.loanTransactionHelper.getLoanDetails((long) loanId); + assertNotNull(loanDetails.getRepaymentSchedule()); + assertNotNull(loanDetails.getRepaymentSchedule().getPeriods()); + assertEquals(3, loanDetails.getRepaymentSchedule().getPeriods().size()); // extra instalment is created + assertNull(loanDetails.getRepaymentSchedule().getPeriods().get(2).getObligationsMetOnDate()); // not repayed + }); + } + private Integer createLoanAccount(final Integer clientID, final Integer loanProductID, final String externalId, final boolean advancedPaymentStrategy, String approveDate, String disbursementDate) {