diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java index b889ad638b7..fab798cdf4a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanReadPlatformServiceImpl.java @@ -2125,11 +2125,21 @@ private static final class RepaymentTransactionTemplateMapper implements RowMapp public String schema() { // TODO: investigate whether it can be refactored to be more efficient - return " GREATEST(loan_transaction.transaction_date, ls.dueDate) as transactionDate," - + " coalesce(ls.principal_amount, 0) - coalesce(ls.principal_writtenoff_derived, 0) - coalesce(ls.principal_completed_derived, 0) as principalDue," - + " coalesce(ls.interest_amount, 0) - coalesce(ls.interest_completed_derived, 0) - coalesce(ls.interest_waived_derived, 0) - coalesce(ls.interest_writtenoff_derived, 0) as interestDue," - + " coalesce(ls.fee_charges_amount, 0) - coalesce(ls.fee_charges_completed_derived, 0) - coalesce(ls.fee_charges_writtenoff_derived, 0) - coalesce(ls.fee_charges_waived_derived, 0) as feeDue," - + " coalesce(ls.penalty_charges_amount, 0) - coalesce(ls.penalty_charges_completed_derived, 0) - coalesce(ls.penalty_charges_writtenoff_derived, 0) - coalesce(ls.penalty_charges_waived_derived, 0) as penaltyDue," + final String principalDueExpression = "coalesce(ls.principal_amount, 0) - coalesce(ls.principal_writtenoff_derived, 0) " + + "- coalesce(ls.principal_completed_derived, 0)"; + final String interestDueExpression = "coalesce(ls.interest_amount, 0) - coalesce(ls.interest_completed_derived, 0) " + + "- coalesce(ls.interest_waived_derived, 0) - coalesce(ls.interest_writtenoff_derived, 0)"; + final String feeDueExpression = "coalesce(ls.fee_charges_amount, 0) - coalesce(ls.fee_charges_completed_derived, 0) " + + "- coalesce(ls.fee_charges_writtenoff_derived, 0) - coalesce(ls.fee_charges_waived_derived, 0)"; + final String penaltyDueExpression = "coalesce(ls.penalty_charges_amount, 0) " + + "- coalesce(ls.penalty_charges_completed_derived, 0) - coalesce(ls.penalty_charges_writtenoff_derived, 0) " + + "- coalesce(ls.penalty_charges_waived_derived, 0)"; + final String totalDueExpression = principalDueExpression + " + " + interestDueExpression + " + " + feeDueExpression + " + " + + penaltyDueExpression; + + return " GREATEST(loan_transaction.transaction_date, ls.dueDate) as transactionDate," + " " + principalDueExpression + + " as principalDue," + " " + interestDueExpression + " as interestDue," + " " + feeDueExpression + " as feeDue," + + " " + penaltyDueExpression + " as penaltyDue," + " l.currency_code as currencyCode," + " l.currency_digits as currencyDigits," + " l.currency_multiplesof as inMultiplesOf," + " l.net_disbursal_amount as netDisbursalAmount," + " rc." + sqlGenerator.escape("name") + " as currencyName," + " rc.display_symbol as currencyDisplaySymbol," @@ -2137,8 +2147,8 @@ public String schema() { + sqlGenerator.escape("code") + " = l.currency_code" + " JOIN m_loan_repayment_schedule ls ON ls.loan_id = l.id" + " LEFT JOIN (" + " select tr.loan_id, max(tr.transaction_date) as transaction_date" + " from m_loan_transaction tr" + " where tr.transaction_type_enum in (?,?)" + " AND tr.is_reversed = false" + " group by tr.loan_id" - + " ) loan_transaction ON loan_transaction.loan_id = l.id" + " WHERE l.id = ?" + " ORDER BY ls.installment" - + " LIMIT 1"; + + " ) loan_transaction ON loan_transaction.loan_id = l.id" + " WHERE l.id = ?" + " ORDER BY CASE WHEN (" + + totalDueExpression + ") > 0 THEN 0 ELSE 1 END, ls.installment" + " LIMIT 1"; } @Override diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java index 44b674fcb6e..86bac58c042 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/AdvancedPaymentAllocationLoanRepaymentScheduleTest.java @@ -445,6 +445,35 @@ public void uc4() { }); } + @Test + public void repaymentTemplateReturnsNextUnpaidInstallmentAmount() { + runAt("15 February 2023", () -> { + final PostLoansResponse loanResponse = applyForLoanApplication(client.getClientId(), commonLoanProductId, + BigDecimal.valueOf(500.0), 45, 15, 3, BigDecimal.ZERO, "01 January 2023", "01 January 2023"); + + loanTransactionHelper.approveLoan(loanResponse.getLoanId(), + new PostLoansLoanIdRequest().approvedLoanAmount(BigDecimal.valueOf(500)).dateFormat(DATETIME_PATTERN) + .approvedOnDate("01 January 2023").locale("en")); + + loanTransactionHelper.disburseLoan(loanResponse.getLoanId(), + new PostLoansLoanIdRequest().actualDisbursementDate("01 January 2023").dateFormat(DATETIME_PATTERN) + .transactionAmount(BigDecimal.valueOf(500.00)).locale("en")); + + loanTransactionHelper.makeLoanRepayment(loanResponse.getLoanId(), new PostLoansLoanIdTransactionsRequest() + .dateFormat(DATETIME_PATTERN).transactionDate("16 January 2023").locale("en").transactionAmount(125.0)); + + final GetLoansLoanIdTransactionsTemplateResponse transactionTemplate = loanTransactionHelper + .retrieveTransactionTemplate(loanResponse.getLoanId(), "repayment", DATETIME_PATTERN, "16 January 2023", LOCALE); + + assertNotNull(transactionTemplate); + assertEquals(125.0, transactionTemplate.getAmount()); + assertEquals(125.0, transactionTemplate.getPrincipalPortion()); + assertEquals(0.0, transactionTemplate.getInterestPortion()); + assertEquals(0.0, transactionTemplate.getFeeChargesPortion()); + assertEquals(0.0, transactionTemplate.getPenaltyChargesPortion()); + }); + } + // UC5: Refund past due // ADVANCED_PAYMENT_ALLOCATION_STRATEGY // 1. Disburse the loan