This is an automated email from the ASF dual-hosted git repository.

adamsaghy pushed a commit to branch develop
in repository https://gitbox.apache.org/repos/asf/fineract.git


The following commit(s) were added to refs/heads/develop by this push:
     new 5ced30b86 FINERACT-1724: Create accrual entry for specific due date 
charge even on disbursement date
5ced30b86 is described below

commit 5ced30b864d39b95f02c8588f05224ee4d050419
Author: Adam Saghy <[email protected]>
AuthorDate: Fri Mar 10 20:12:06 2023 +0100

    FINERACT-1724: Create accrual entry for specific due date charge even on 
disbursement date
---
 .../LoanAccrualWritePlatformServiceImpl.java       |   3 +-
 .../service/LoanReadPlatformServiceImpl.java       |   3 +-
 .../LoanChargeSpecificDueDateTest.java             |  44 ++++++++-
 .../integrationtests/SchedulerJobsTestResults.java | 101 +++++++++++++++++++--
 4 files changed, 141 insertions(+), 10 deletions(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
index f0c46d89c..bc7e40694 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/service/LoanAccrualWritePlatformServiceImpl.java
@@ -375,7 +375,8 @@ public class LoanAccrualWritePlatformServiceImpl implements 
LoanAccrualWritePlat
                         }
                     }
                 }
-            } else if (loanCharge.getDueDate().isAfter(startDate) && 
!loanCharge.getDueDate().isAfter(endDate)) {
+            } else if (((accrualData.getInstallmentNumber() == 1 && 
loanCharge.getDueDate().isEqual(startDate))
+                    || loanCharge.getDueDate().isAfter(startDate)) && 
!loanCharge.getDueDate().isAfter(endDate)) {
                 chargeAmount = loanCharge.getAmount();
                 if (loanCharge.getAmountUnrecognized() != null) {
                     chargeAmount = 
chargeAmount.subtract(loanCharge.getAmountUnrecognized());
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 f5113daa3..98d5e3280 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
@@ -1778,7 +1778,8 @@ public class LoanReadPlatformServiceImpl implements 
LoanReadPlatformService {
                 .append(" or (ls.penalty_charges_amount <> 
COALESCE(ls.accrual_penalty_charges_derived, 0))")
                 .append(" or (ls.interest_amount <> 
COALESCE(ls.accrual_interest_derived, 0)))")
                 .append(" and loan.loan_status_id=:active and 
mpl.accounting_type=:type and (loan.closedon_date <= :tillDate or 
loan.closedon_date is null)")
-                .append(" and loan.is_npa=false and (ls.duedate <= :tillDate 
or (ls.duedate > :tillDate and ls.fromdate < :tillDate))) ");
+                .append(" and loan.is_npa=false and (ls.duedate <= :tillDate 
or (ls.duedate > :tillDate and ls.fromdate < :tillDate)")
+                .append(" or (ls.installment = 1 and ls.fromdate = 
:tillDate))) ");
         Map<String, Object> paramMap = new HashMap<>(5);
         if (organisationStartDate != null) {
             sqlBuilder.append(" and ls.duedate > :organisationStartDate ");
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
index 02393b488..c4c65a50a 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/LoanChargeSpecificDueDateTest.java
@@ -87,7 +87,8 @@ public class LoanChargeSpecificDueDateTest {
 
         // Client and Loan account creation
         final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, "01 January 2012");
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProduct(loanTransactionHelper, null);
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProductWithPeriodicAccrual(loanTransactionHelper,
+                null);
         assertNotNull(getLoanProductsProductResponse);
 
         // Older date to have more than one overdue installment
@@ -120,6 +121,25 @@ public class LoanChargeSpecificDueDateTest {
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
         validateLoanAccount(getLoansLoanIdResponse, 
Double.valueOf(principalAmount), Double.valueOf("10.00"), false);
 
+        // Run Accruals
+        log.info("Running Periodic Accrual for date {}", transactionDate);
+        
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(operationDate);
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+
+        final Long transactionId = 
loanTransactionHelper.evaluateLastLoanTransactionData(getLoansLoanIdResponse,
+                "loanTransactionType.accrual", operationDate, 
Double.valueOf("10.00"));
+        assertNotNull(transactionId);
+        log.info("transactionId {}", transactionId);
+
+        final GetJournalEntriesTransactionIdResponse journalEntriesResponse = 
journalEntryHelper.getJournalEntries("L" + transactionId);
+        assertNotNull(journalEntriesResponse);
+        final List<JournalEntryTransactionItem> journalEntries = 
journalEntriesResponse.getPageItems();
+        assertEquals(2, journalEntries.size());
+        assertEquals(10, journalEntries.get(0).getAmount());
+        assertEquals(10, journalEntries.get(1).getAmount());
+        assertEquals(transactionDate, 
journalEntries.get(0).getTransactionDate());
+        assertEquals(transactionDate, 
journalEntries.get(1).getTransactionDate());
+
         // Make a full repayment to close the Loan
         Float amount = Float.valueOf("1010.00");
         PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
@@ -140,7 +160,8 @@ public class LoanChargeSpecificDueDateTest {
 
         // Client and Loan account creation
         final Integer clientId = ClientHelper.createClient(this.requestSpec, 
this.responseSpec, "01 January 2012");
-        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProduct(loanTransactionHelper, null);
+        final GetLoanProductsProductIdResponse getLoanProductsProductResponse 
= createLoanProductWithPeriodicAccrual(loanTransactionHelper,
+                null);
         assertNotNull(getLoanProductsProductResponse);
 
         // Older date to have more than one overdue installment
@@ -174,6 +195,25 @@ public class LoanChargeSpecificDueDateTest {
         getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
         validateLoanAccount(getLoansLoanIdResponse, 
Double.valueOf(principalAmount), Double.valueOf("10.00"), true);
 
+        // Run Accruals
+        log.info("Running Periodic Accrual for date {}", transactionDate);
+        
periodicAccrualAccountingHelper.runPeriodicAccrualAccounting(operationDate);
+        getLoansLoanIdResponse = loanTransactionHelper.getLoan(requestSpec, 
responseSpec, loanId);
+
+        final Long transactionId = 
loanTransactionHelper.evaluateLastLoanTransactionData(getLoansLoanIdResponse,
+                "loanTransactionType.accrual", operationDate, 
Double.valueOf("10.00"));
+        assertNotNull(transactionId);
+        log.info("transactionId {}", transactionId);
+
+        final GetJournalEntriesTransactionIdResponse journalEntriesResponse = 
journalEntryHelper.getJournalEntries("L" + transactionId);
+        assertNotNull(journalEntriesResponse);
+        final List<JournalEntryTransactionItem> journalEntries = 
journalEntriesResponse.getPageItems();
+        assertEquals(2, journalEntries.size());
+        assertEquals(10, journalEntries.get(0).getAmount());
+        assertEquals(10, journalEntries.get(1).getAmount());
+        assertEquals(transactionDate, 
journalEntries.get(0).getTransactionDate());
+        assertEquals(transactionDate, 
journalEntries.get(1).getTransactionDate());
+
         // Make a full repayment to close the Loan
         Float amount = Float.valueOf("1010.00");
         PostLoansLoanIdTransactionsResponse loanIdTransactionsResponse = 
loanTransactionHelper.makeLoanRepayment(operationDate, amount,
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
index 42785222d..1a7502c9c 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java
@@ -19,6 +19,8 @@
 package org.apache.fineract.integrationtests;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import com.google.common.truth.Truth;
 import io.restassured.builder.RequestSpecBuilder;
@@ -48,7 +50,9 @@ import java.util.Locale;
 import java.util.Map;
 import java.util.TimeZone;
 import org.apache.fineract.client.models.BusinessDateRequest;
+import 
org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
 import org.apache.fineract.client.models.GetLoansLoanIdResponse;
+import org.apache.fineract.client.models.JournalEntryTransactionItem;
 import org.apache.fineract.client.models.PostClientsResponse;
 import org.apache.fineract.client.models.PostLoansLoanIdTransactionsRequest;
 import org.apache.fineract.client.models.PutJobsJobIDRequest;
@@ -661,11 +665,15 @@ public class SchedulerJobsTestResults {
                 
ChargesHelper.getLoanOverdueFeeJSONWithCalculationTypePercentage("1"));
         Assertions.assertNotNull(overdueFeeChargeId);
 
+        Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+        Assertions.assertNotNull(fee);
+
         final Integer loanProductID = 
createLoanProduct(overdueFeeChargeId.toString());
         Assertions.assertNotNull(loanProductID);
         List<Integer> loanIDs = new ArrayList<>();
         HashMap loanStatusHashMap;
-        for (int i = 0; i < 10; i++) {
+        for (int i = 0; i < 3; i++) {
             final Integer loanID = 
applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, 
"10 January 2020");
 
             Assertions.assertNotNull(loanID);
@@ -682,6 +690,8 @@ public class SchedulerJobsTestResults {
             LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
             loanIDs.add(loanID);
 
+            this.loanTransactionHelper.addChargesForLoan(loanID,
+                    
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee),
 "02 March 2020", "10", null));
         }
 
         String jobName = "Loan COB";
@@ -690,18 +700,82 @@ public class SchedulerJobsTestResults {
             List<HashMap> repaymentScheduleDataAfter = 
this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec,
                     loanId);
 
+            Assertions.assertEquals(10.00f, (Float) 
repaymentScheduleDataAfter.get(1).get("feeChargesDue"),
+                    "Verifying From Fee Charges due for first Repayment after 
Successful completion of Scheduler Job");
             Assertions.assertEquals(39.39f, (Float) 
repaymentScheduleDataAfter.get(1).get("penaltyChargesDue"),
-                    "Verifying From Penalty Charges due fot first Repayment 
after Successful completion of Scheduler Job");
+                    "Verifying From Penalty Charges due for first Repayment 
after Successful completion of Scheduler Job");
             Assertions.assertEquals(39.39f, (Float) 
repaymentScheduleDataAfter.get(2).get("penaltyChargesDue"),
-                    "Verifying From Penalty Charges due fot first Repayment 
after Successful completion of Scheduler Job");
+                    "Verifying From Penalty Charges due for first Repayment 
after Successful completion of Scheduler Job");
             Assertions.assertEquals(39.39f, (Float) 
repaymentScheduleDataAfter.get(3).get("penaltyChargesDue"),
-                    "Verifying From Penalty Charges due fot first Repayment 
after Successful completion of Scheduler Job");
+                    "Verifying From Penalty Charges due for first Repayment 
after Successful completion of Scheduler Job");
             Assertions.assertEquals(39.39f, (Float) 
repaymentScheduleDataAfter.get(4).get("penaltyChargesDue"),
-                    "Verifying From Penalty Charges due fot first Repayment 
after Successful completion of Scheduler Job");
+                    "Verifying From Penalty Charges due for first Repayment 
after Successful completion of Scheduler Job");
 
         }
     }
 
+    @Test
+    public void testLoanCOBJobOutcomeWhileAddingFeeOnDisbursementDate() {
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
+        BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, 
BusinessDateType.COB_DATE, LocalDate.of(2020, 3, 2));
+        this.savingsAccountHelper = new SavingsAccountHelper(requestSpec, 
responseSpec);
+        this.loanTransactionHelper = new LoanTransactionHelper(requestSpec, 
responseSpec);
+
+        final Integer clientID = ClientHelper.createClient(requestSpec, 
responseSpec);
+        Assertions.assertNotNull(clientID);
+
+        Integer fee = ChargesHelper.createCharges(requestSpec, responseSpec,
+                
ChargesHelper.getLoanSpecifiedDueDateJSON(ChargesHelper.CHARGE_CALCULATION_TYPE_FLAT,
 "10", false));
+        Assertions.assertNotNull(fee);
+
+        final Integer loanProductID = 
createLoanProductWithPeriodicAccrual(null);
+        Assertions.assertNotNull(loanProductID);
+
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "10 January 2020");
+
+        Assertions.assertNotNull(loanID);
+
+        HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
+        LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+        loanStatusHashMap = this.loanTransactionHelper.approveLoan("01 March 
2020", loanID);
+        LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+
+        String loanDetails = 
this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
+        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("02 March 2020", 
loanID,
+                
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+        LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+        this.loanTransactionHelper.addChargesForLoan(loanID,
+                
LoanTransactionHelper.getSpecifiedDueDateChargesForLoanAsJSON(String.valueOf(fee),
 "02 March 2020", "10", null));
+
+        String jobName = "Loan COB";
+        this.schedulerJobHelper.executeAndAwaitJob(jobName);
+
+        List<HashMap> repaymentScheduleDataAfter = 
this.loanTransactionHelper.getLoanRepaymentSchedule(requestSpec, responseSpec, 
loanID);
+
+        Assertions.assertEquals(10.00f, (Float) 
repaymentScheduleDataAfter.get(1).get("feeChargesDue"),
+                "Verifying From Fee Charges due for first Repayment after 
Successful completion of Scheduler Job");
+
+        GetLoansLoanIdResponse getLoansLoanIdResponse = 
loanTransactionHelper.getLoan(requestSpec, responseSpec, loanID);
+        // First accrual transaction
+        
assertTrue(getLoansLoanIdResponse.getTransactions().get(1).getType().getAccrual());
+        assertEquals(10.0f, 
getLoansLoanIdResponse.getTransactions().get(1).getFeeChargesPortion());
+        assertEquals(LocalDate.of(2020, 3, 2), 
getLoansLoanIdResponse.getTransactions().get(1).getDate());
+        Long transactionId = 
getLoansLoanIdResponse.getTransactions().get(1).getId();
+
+        final GetJournalEntriesTransactionIdResponse journalEntriesResponse = 
journalEntryHelper.getJournalEntries("L" + transactionId);
+        assertNotNull(journalEntriesResponse);
+        final List<JournalEntryTransactionItem> journalEntries = 
journalEntriesResponse.getPageItems();
+        assertEquals(2, journalEntries.size());
+        assertEquals(10, journalEntries.get(0).getAmount());
+        assertEquals(10, journalEntries.get(1).getAmount());
+        assertEquals(LocalDate.of(2020, 3, 2), 
journalEntries.get(1).getTransactionDate());
+        assertEquals(LocalDate.of(2020, 3, 2), 
journalEntries.get(0).getTransactionDate());
+
+        GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.FALSE);
+    }
+
     @Test
     public void testLoanCOBRunsOnlyOnLoansOneDayBehind() {
         GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, 
responseSpec, Boolean.TRUE);
@@ -934,7 +1008,7 @@ public class SchedulerJobsTestResults {
         String JobName = "Update Non Performing Assets";
         this.schedulerJobHelper.executeAndAwaitJob(JobName);
         final Boolean isNPAAfter = (Boolean) 
this.loanTransactionHelper.getLoanDetail(requestSpec, responseSpec, loanID, 
"isNPA");
-        Assertions.assertTrue(isNPAAfter);
+        assertTrue(isNPAAfter);
     }
 
     @Test
@@ -1090,6 +1164,21 @@ public class SchedulerJobsTestResults {
         return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
     }
 
+    private Integer createLoanProductWithPeriodicAccrual(final String 
chargeId) {
+        final Account assetAccount = this.accountHelper.createAssetAccount();
+        final Account assetFeeAndPenaltyAccount = 
this.accountHelper.createAssetAccount();
+        final Account incomeAccount = this.accountHelper.createIncomeAccount();
+        final Account expenseAccount = 
this.accountHelper.createExpenseAccount();
+        final Account overpaymentAccount = 
this.accountHelper.createLiabilityAccount();
+
+        final String loanProductJSON = new 
LoanProductTestBuilder().withPrincipal("15,000.00").withNumberOfRepayments("4")
+                
.withRepaymentAfterEvery("1").withRepaymentTypeAsMonth().withinterestRatePerPeriod("1")
+                .withAccountingRulePeriodicAccrual(new Account[] { 
assetAccount, incomeAccount, expenseAccount, overpaymentAccount })
+                
.withInterestRateFrequencyTypeAsMonths().withAmortizationTypeAsEqualInstallments().withInterestTypeAsDecliningBalance()
+                
.withFeeAndPenaltyAssetAccount(assetFeeAndPenaltyAccount).build(chargeId);
+        return this.loanTransactionHelper.getLoanProductId(loanProductJSON);
+    }
+
     private void addCollaterals(List<HashMap> collaterals, Integer 
collateralId, BigDecimal quantity) {
         collaterals.add(collaterals(collateralId, quantity));
     }

Reply via email to