From 0487bece0b02a00a70ed03d3ba1e727d41e16e7c Mon Sep 17 00:00:00 2001 From: John Woodlock Date: Fri, 8 Jul 2022 01:27:56 +0100 Subject: [PATCH] FINERACT-1661 Allow merchant_issued and payout refunds when fullyPaid/Overpaid --- .../portfolio/loanaccount/domain/Loan.java | 20 ++++++++- ...RefundandRepaymentTypeIntegrationTest.java | 43 ++++++++++++++++--- .../common/loans/LoanTransactionHelper.java | 8 ++-- 3 files changed, 60 insertions(+), 11 deletions(-) diff --git a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java index f8ef7d7d2..642b69b1e 100644 --- a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java +++ b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/domain/Loan.java @@ -3098,7 +3098,7 @@ public class Loan extends AbstractPersistableCustom { if (!isHolidayValidationDone) { holidayDetailDTO = scheduleGeneratorDTO.getHolidayDetailDTO(); } - validateAccountStatus(event); + validateRepaymentTypeAccountStatus(repaymentTransaction, event); validateActivityNotBeforeClientOrGroupTransferDate(event, repaymentTransaction.getTransactionDate()); validateActivityNotBeforeLastTransactionDate(event, repaymentTransaction.getTransactionDate()); if (!isHolidayValidationDone) { @@ -3116,6 +3116,24 @@ public class Loan extends AbstractPersistableCustom { return changedTransactionDetail; } + private void validateRepaymentTypeAccountStatus(LoanTransaction repaymentTransaction, LoanEvent event) { + if (repaymentTransaction.isGoodwillCredit() || repaymentTransaction.isMerchantIssuedRefund() + || repaymentTransaction.isPayoutRefund()) { + + if (!(isOpen() || isClosedObligationsMet() || isOverPaid())) { + final List dataValidationErrors = new ArrayList<>(); + final String defaultUserMessage = "Loan must be Active, Fully Paid or Overpaid"; + final ApiParameterError error = ApiParameterError.generalError("error.msg.loan.must.be.active.fully.paid.or.overpaid", + defaultUserMessage); + dataValidationErrors.add(error); + throw new PlatformApiDataValidationException(dataValidationErrors); + } + } else { + validateAccountStatus(event); + } + + } + public void makeChargePayment(final Long chargeId, final LoanLifecycleStateMachine loanLifecycleStateMachine, final List existingTransactionIds, final List existingReversedTransactionIds, final HolidayDetailDTO holidayDetailDTO, final LoanTransaction paymentTransaction, final Integer installmentNumber) { 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 ebc94ded9..7e5a9748e 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,6 +62,7 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest { private Account incomeAccount; private Account expenseAccount; private Account overpaymentAccount; + private static final String REPAYMENT = "repayment"; private static final String MERCHANT_ISSUED_REFUND = "merchantIssuedRefund"; private static final String PAYOUT_REFUND = "payoutRefund"; private static final String GOODWILL_CREDIT = "goodwillCredit"; @@ -343,8 +344,8 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest { } private void verifyRepaymentTransactionTypeMatches(final String repaymentTransactionType) { - HashMap loanStatusHashMap = this.loanTransactionHelper.makeRepaymentTypePayment(repaymentTransactionType, "06 January 2022", - 200.00f, this.disbursedLoanID); + HashMap loanStatusHashMap = (HashMap) this.loanTransactionHelper.makeRepaymentTypePayment(repaymentTransactionType, + "06 January 2022", 200.00f, this.disbursedLoanID, ""); Integer newTransactionId = (Integer) loanStatusHashMap.get("resourceId"); loanStatusHashMap = this.loanTransactionHelper.getLoanTransactionDetails(this.disbursedLoanID, newTransactionId); @@ -353,6 +354,36 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest { Assertions.assertTrue(Boolean.TRUE.equals(isTypeCorrect), "Not " + repaymentTransactionType); } + @Test + public void repaymentTransactionTypeWhenPaidTest() { + verifyRepaymentTransactionTypeWhenPaid(MERCHANT_ISSUED_REFUND); + verifyRepaymentTransactionTypeWhenPaid(PAYOUT_REFUND); + verifyRepaymentTransactionTypeWhenPaid(GOODWILL_CREDIT); + verifyRepaymentTransactionTypeWhenPaid(REPAYMENT); + + } + + private void verifyRepaymentTransactionTypeWhenPaid(final String repaymentTransactionType) { + + disburseLoanOfAccountingRule(ACCRUAL_PERIODIC); + // Overpay loan + Integer resourceId = (Integer) this.loanTransactionHelper.makeRepaymentTypePayment(REPAYMENT, "06 January 2022", 13000.00f, + this.disbursedLoanID, "resourceId"); + Assertions.assertNotNull(resourceId); + + if (repaymentTransactionType.equalsIgnoreCase(REPAYMENT)) { + ArrayList errors = (ArrayList) this.loanTransactionHelperValidationError.makeRepaymentTypePayment( + repaymentTransactionType, "06 January 2022", 1.00f, this.disbursedLoanID, CommonConstants.RESPONSE_ERROR); + + assertEquals("error.msg.loan.repayment.or.waiver.account.is.not.active", + errors.get(0).get(CommonConstants.RESPONSE_ERROR_MESSAGE_CODE)); + } else { + resourceId = (Integer) this.loanTransactionHelper.makeRepaymentTypePayment(repaymentTransactionType, "06 January 2022", 1.00f, + this.disbursedLoanID, "resourceId"); + Assertions.assertNotNull(resourceId); + } + } + @Test public void newGoodwillCreditCreatesCorrectJournalEntriesForPeriodicAccrualsTest() { @@ -366,8 +397,8 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest { final Float overpaidAmount = 159.00f; final Float goodwillAmount = totalOutstanding + overpaidAmount; final String goodwillDate = "09 January 2022"; - HashMap loanStatusHashMap = this.loanTransactionHelper.makeRepaymentTypePayment(GOODWILL_CREDIT, goodwillDate, goodwillAmount, - this.disbursedLoanID); + HashMap loanStatusHashMap = (HashMap) this.loanTransactionHelper.makeRepaymentTypePayment(GOODWILL_CREDIT, goodwillDate, + goodwillAmount, this.disbursedLoanID, ""); // only a single credit for principal and interest as test sets up same GL account for both (summed up) this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, goodwillDate, @@ -392,8 +423,8 @@ public class ClientLoanCreditBalanceRefundandRepaymentTypeIntegrationTest { final Float overpaidAmount = 159.00f; final Float goodwillAmount = totalOutstanding + overpaidAmount; final String goodwillDate = "09 January 2022"; - HashMap loanStatusHashMap = this.loanTransactionHelper.makeRepaymentTypePayment(GOODWILL_CREDIT, goodwillDate, goodwillAmount, - this.disbursedLoanID); + HashMap loanStatusHashMap = (HashMap) this.loanTransactionHelper.makeRepaymentTypePayment(GOODWILL_CREDIT, goodwillDate, + goodwillAmount, this.disbursedLoanID, ""); this.journalEntryHelper.checkJournalEntryForAssetAccount(assetAccount, goodwillDate, new JournalEntry(principalOutstanding, JournalEntry.TransactionType.CREDIT)); diff --git a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java index b0a0121ed..4b88a2aa8 100644 --- a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java +++ b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/loans/LoanTransactionHelper.java @@ -349,10 +349,10 @@ public class LoanTransactionHelper { getCreditBalanceRefundBodyAsJSON(date, amountToBePaid, externalId), jsonAttributeToGetback); } - public HashMap makeRepaymentTypePayment(final String repaymentTypeCommand, final String date, final Float amountToBePaid, - final Integer loanID) { - return (HashMap) performLoanTransaction(createLoanTransactionURL(repaymentTypeCommand, loanID), - getRepaymentBodyAsJSON(date, amountToBePaid), ""); + public Object makeRepaymentTypePayment(final String repaymentTypeCommand, final String date, final Float amountToBePaid, + final Integer loanID, String jsonAttributeToGetback) { + return performLoanTransaction(createLoanTransactionURL(repaymentTypeCommand, loanID), getRepaymentBodyAsJSON(date, amountToBePaid), + jsonAttributeToGetback); } public HashMap makeRepayment(final String date, final Float amountToBePaid, final Integer loanID) {