diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java index 48a13bed630..f54f5186d7a 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/LoanAccountDomainServiceJpa.java @@ -688,6 +688,10 @@ private void updateLoanTransaction(final Long loanTransactionId, final LoanTrans @Override public LoanTransaction creditBalanceRefund(final Loan loan, final LocalDate transactionDate, final BigDecimal transactionAmount, final String noteText, final ExternalId externalId, PaymentDetail paymentDetail) { + if (transactionDate.isAfter(DateUtils.getBusinessLocalDate())) { + throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.in.the.future", + "Loan: " + loan.getId() + ", Credit Balance Refund transaction cannot be created for the future.", loan.getId()); + } if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) { throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " + loan.getId() diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java index 64d8ca86d52..486737f9139 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/BaseLoanIntegrationTest.java @@ -271,7 +271,7 @@ protected void disburseLoan(Long loanId, BigDecimal amount, String date) { .transactionAmount(amount).locale("en")); } - protected void verifyJournalEntries(Long loanId, JournalEntry... entries) { + protected void verifyJournalEntries(Long loanId, Journal... entries) { GetJournalEntriesTransactionIdResponse journalEntriesForLoan = journalEntryHelper.getJournalEntriesForLoan(loanId); Assertions.assertEquals(entries.length, journalEntriesForLoan.getPageItems().size()); Arrays.stream(entries).forEach(journalEntry -> { @@ -405,8 +405,8 @@ protected void updateBusinessDate(String date) { new BusinessDateRequest().type(BUSINESS_DATE.getName()).date(date).dateFormat(DATETIME_PATTERN).locale("en")); } - protected JournalEntry journalEntry(double principalAmount, Account account, String type) { - return new JournalEntry(principalAmount, account, type); + protected Journal journalEntry(double principalAmount, Account account, String type) { + return new Journal(principalAmount, account, type); } protected Transaction transaction(double principalAmount, String type, String date) { @@ -454,7 +454,7 @@ public static class TransactionExt { @ToString @AllArgsConstructor - public static class JournalEntry { + public static class Journal { Double amount; Account account; diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java index c43cdf2ce24..9a61b658ca7 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest.java @@ -62,7 +62,7 @@ @SuppressWarnings({ "rawtypes", "unchecked" }) @ExtendWith(LoanTestLifecycleExtension.class) @Slf4j -public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest { +public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest extends BaseLoanIntegrationTest { private ResponseSpecification responseSpec; private ResponseSpecification responseSpec403; @@ -251,7 +251,56 @@ public void fullRefundChangesStatusToClosedObligationMetTest(LoanProductTestBuil totalOverpaidAtEnd = floatZero; } assertEquals(totalOverpaidAtEnd, floatZero); + } + + @ParameterizedTest + @MethodSource("loanProductFactory") + public void refundAcceptedOnTheCurrentBusinessDate(LoanProductTestBuilder loanProductTestBuilder) { + runAt("09 January 2022", () -> { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC, loanProductTestBuilder); + HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment + LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); + + final Float totalOverpaid = (Float) this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, + disbursedLoanID, "totalOverpaid"); + + final String creditBalanceRefundDate = "09 January 2022"; + final String externalId = null; + loanTransactionHelper.creditBalanceRefund(creditBalanceRefundDate, totalOverpaid, externalId, disbursedLoanID, null); + loanStatusHashMap = (HashMap) this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, disbursedLoanID, + "status"); + LoanStatusChecker.verifyLoanAccountIsClosed(loanStatusHashMap); + + final Float floatZero = 0.0f; + Float totalOverpaidAtEnd = (Float) this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, + disbursedLoanID, "totalOverpaid"); + if (totalOverpaidAtEnd == null) { + totalOverpaidAtEnd = floatZero; + } + assertEquals(totalOverpaidAtEnd, floatZero); + }); + } + + @ParameterizedTest + @MethodSource("loanProductFactory") + public void refundCannotBeDuneForFutureDate(LoanProductTestBuilder loanProductTestBuilder) { + runAt("06 January 2022", () -> { + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC, loanProductTestBuilder); + HashMap loanStatusHashMap = makeRepayment("06 January 2022", 20000.00f); // overpayment + LoanStatusChecker.verifyLoanAccountIsOverPaid(loanStatusHashMap); + + final Float totalOverpaid = (Float) this.loanTransactionHelper.getLoanDetail(this.requestSpec, this.responseSpec, + disbursedLoanID, "totalOverpaid"); + + final String creditBalanceRefundDate = "09 January 2022"; + final String externalId = null; + + ArrayList cbrErrors = (ArrayList) loanTransactionHelperValidationError.creditBalanceRefund( + creditBalanceRefundDate, totalOverpaid, externalId, disbursedLoanID, CommonConstants.RESPONSE_ERROR); + assertEquals("error.msg.transaction.date.cannot.be.in.the.future", + cbrErrors.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); + }); } @ParameterizedTest