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 50eefd2ee FINERACT-2031: Fix reschedule to next repayment date. 
Schedule should shift all the remaining installments to next repayment date as 
per the loan frequency.
50eefd2ee is described below

commit 50eefd2ee54463d085a8bc4b0cac4979f0bba2a2
Author: 2vinodhkumar <[email protected]>
AuthorDate: Wed Dec 27 17:01:37 2023 +0530

    FINERACT-2031: Fix reschedule to next repayment date. Schedule should shift 
all the remaining installments to next repayment date as per the loan frequency.
    
    FINERACT-2031: Fix reschedule to next repayment date. Schedule should shift 
all the remaining installments to next repayment date as per the loan frequency.
    
    FINERACT-2031: Fix reschedule to next repayment date. Schedule should shift 
all the remaining installments to next repayment date as per the loan frequency.
    
    FINERACT-2031: Fix reschedule to next repayment date. Schedule should shift 
all the remaining installments to next repayment date as per the loan frequency.
    
    FINERACT-2031: Fix reschedule to next repayment date. Schedule should shift 
all the remaining installments to next repayment date as per the loan frequency.
---
 .../ApplyHolidaysToLoansTasklet.java               |  39 +++++-
 .../integrationtests/SchedulerJobsTestResults.java | 136 +++++++++++++++++++++
 .../integrationtests/common/HolidayHelper.java     |  23 ++++
 3 files changed, 197 insertions(+), 1 deletion(-)

diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/applyholidaystoloans/ApplyHolidaysToLoansTasklet.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/applyholidaystoloans/ApplyHolidaysToLoansTasklet.java
index 3c3d2bd5c..690004ada 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/applyholidaystoloans/ApplyHolidaysToLoansTasklet.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/applyholidaystoloans/ApplyHolidaysToLoansTasklet.java
@@ -96,6 +96,7 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
 
     public void applyHolidayToRepaymentScheduleDates(Loan loan, Holiday 
holiday) {
         LocalDate adjustedRescheduleToDate = null;
+        boolean isResheduleToNextRepaymentDate = 
holiday.getReScheduleType().isResheduleToNextRepaymentDate();
         if (holiday.getReScheduleType().isResheduleToNextRepaymentDate()) {
             adjustedRescheduleToDate = getNextRepaymentDate(loan, holiday);
         } else {
@@ -103,7 +104,11 @@ public class ApplyHolidaysToLoansTasklet implements 
Tasklet {
         }
 
         if (isRepaymentScheduleAdjustmentNeeded(adjustedRescheduleToDate)) {
-            adjustRepaymentSchedules(loan, holiday, adjustedRescheduleToDate);
+            if (isResheduleToNextRepaymentDate) {
+                adjustAllRepaymentSchedules(loan, holiday, 
adjustedRescheduleToDate);
+            } else {
+                adjustRepaymentSchedules(loan, holiday, 
adjustedRescheduleToDate);
+            }
             businessEventNotifierService.notifyPostBusinessEvent(new 
LoanRescheduledDueHolidayBusinessEvent(loan));
         }
     }
@@ -144,6 +149,38 @@ public class ApplyHolidaysToLoansTasklet implements 
Tasklet {
         }
     }
 
+    private void adjustAllRepaymentSchedules(Loan loan, Holiday holiday, 
LocalDate adjustedRescheduleToDate) {
+        final DefaultScheduledDateGenerator scheduledDateGenerator = new 
DefaultScheduledDateGenerator();
+        ScheduleGeneratorDTO scheduleGeneratorDTO = 
loanUtilService.buildScheduleGeneratorDTO(loan, holiday.getFromDate());
+        final LoanApplicationTerms loanApplicationTerms = 
loan.constructLoanApplicationTerms(scheduleGeneratorDTO);
+
+        // first repayment's from date is same as disbursement date.
+        LocalDate tmpFromDate = loan.getDisbursementDate();
+
+        // Loop through all loanRepayments
+        List<LoanRepaymentScheduleInstallment> installments = 
loan.getRepaymentScheduleInstallments();
+        for (final LoanRepaymentScheduleInstallment 
loanRepaymentScheduleInstallment : installments) {
+            final LocalDate oldDueDate = 
loanRepaymentScheduleInstallment.getDueDate();
+
+            // update from date if it's not same as previous installment's due
+            // date.
+            if (!DateUtils.isEqual(tmpFromDate, 
loanRepaymentScheduleInstallment.getFromDate())) {
+                loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
+            }
+
+            if (!DateUtils.isBefore(oldDueDate, holiday.getFromDate())) {
+                // FIXME: AA do we need to apply non-working days.
+                // Assuming holiday's repayment reschedule to date cannot be
+                // created on a non-working day.
+
+                adjustedRescheduleToDate = 
scheduledDateGenerator.generateNextRepaymentDate(adjustedRescheduleToDate, 
loanApplicationTerms,
+                        false);
+                
loanRepaymentScheduleInstallment.updateDueDate(adjustedRescheduleToDate);
+            }
+            tmpFromDate = loanRepaymentScheduleInstallment.getDueDate();
+        }
+    }
+
     private LocalDate getNextRepaymentDate(Loan loan, Holiday holiday) {
         LocalDate adjustedRescheduleToDate = null;
         final LocalDate rescheduleToDate = holiday.getToDate();
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 f8ec562bd..166019e81 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
@@ -402,6 +402,142 @@ public class SchedulerJobsTestResults {
         Assertions.assertEquals(fromDateDayBefore, fromDateDayAfter, message);
     }
 
+    @Test
+    public void testApplyType1HolidaysToLoansJobOutcome() throws 
InterruptedException {
+        this.loanTransactionHelper = new LoanTransactionHelper(requestSpec, 
responseSpec);
+
+        final Integer clientID = ClientHelper.createClient(requestSpec, 
responseSpec);
+        Assertions.assertNotNull(clientID);
+
+        Integer holidayId = HolidayHelper.createTyoe1Holidays(requestSpec, 
responseSpec);
+        Assertions.assertNotNull(holidayId);
+
+        final Integer loanProductID = createLoanProduct(null);
+        Assertions.assertNotNull(loanProductID);
+
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "04 January 2024");
+        Assertions.assertNotNull(loanID);
+
+        HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
+        LoanStatusChecker.verifyLoanIsPending(loanStatusHashMap);
+
+        loanStatusHashMap = this.loanTransactionHelper.approveLoan("04 January 
2024", loanID);
+        LoanStatusChecker.verifyLoanIsApproved(loanStatusHashMap);
+
+        String loanDetails = 
this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
+        loanStatusHashMap = 
this.loanTransactionHelper.disburseLoanWithNetDisbursalAmount("04 January 
2024", loanID,
+                
JsonPath.from(loanDetails).get("netDisbursalAmount").toString());
+        LoanStatusChecker.verifyLoanIsActive(loanStatusHashMap);
+
+        // Retrieving All Global Configuration details
+        final ArrayList<HashMap> globalConfig = 
GlobalConfigurationHelper.getAllGlobalConfigurations(requestSpec, responseSpec);
+        Assertions.assertNotNull(globalConfig);
+
+        // Updating Value for reschedule-repayments-on-holidays Global
+        // Configuration
+        Integer configId = (Integer) globalConfig.get(3).get("id");
+        Assertions.assertNotNull(configId);
+
+        HashMap configData = 
GlobalConfigurationHelper.getGlobalConfigurationById(requestSpec, responseSpec, 
configId.toString());
+        Assertions.assertNotNull(configData);
+
+        Boolean enabled = (Boolean) globalConfig.get(3).get("enabled");
+
+        if (!enabled) {
+            enabled = true;
+            
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, 
responseSpec, configId, enabled);
+        }
+
+        holidayId = HolidayHelper.activateHolidays(requestSpec, responseSpec, 
holidayId.toString());
+        Assertions.assertNotNull(holidayId);
+
+        HashMap holidayData = HolidayHelper.getHolidayById(requestSpec, 
responseSpec, holidayId.toString());
+
+        LinkedHashMap repaymentScheduleHashMap = 
JsonPath.from(loanDetails).get("repaymentSchedule");
+        ArrayList<LinkedHashMap> periods = (ArrayList<LinkedHashMap>) 
repaymentScheduleHashMap.get("periods");
+        String JobName = "Apply Holidays To Loans";
+
+        this.schedulerJobHelper.executeAndAwaitJob(JobName);
+
+        // Loan Repayment Schedule After Apply Holidays To Loans
+        loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, 
responseSpec, loanID);
+        repaymentScheduleHashMap = 
JsonPath.from(loanDetails).get("repaymentSchedule");
+        ArrayList<LinkedHashMap> periodsAfterRescheduleApplied = 
(ArrayList<LinkedHashMap>) repaymentScheduleHashMap.get("periods");
+
+        ArrayList<Integer> fromDateValues = (ArrayList<Integer>) 
periods.get(1).get("fromDate");
+        LocalDate fromDate = LocalDate.of(fromDateValues.get(0), 
fromDateValues.get(1), fromDateValues.get(2));
+        ArrayList<Integer> dueDateValues = (ArrayList<Integer>) 
periods.get(1).get("dueDate");
+        LocalDate dueDate = LocalDate.of(dueDateValues.get(0), 
dueDateValues.get(1), dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 1, 4), fromDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 2, 4), dueDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+
+        fromDateValues = (ArrayList<Integer>) periods.get(2).get("fromDate");
+        fromDate = LocalDate.of(fromDateValues.get(0), fromDateValues.get(1), 
fromDateValues.get(2));
+        dueDateValues = (ArrayList<Integer>) periods.get(2).get("dueDate");
+        dueDate = LocalDate.of(dueDateValues.get(0), dueDateValues.get(1), 
dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 2, 4), fromDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 3, 4), dueDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+
+        fromDateValues = (ArrayList<Integer>) periods.get(3).get("fromDate");
+        fromDate = LocalDate.of(fromDateValues.get(0), fromDateValues.get(1), 
fromDateValues.get(2));
+        dueDateValues = (ArrayList<Integer>) periods.get(3).get("dueDate");
+        dueDate = LocalDate.of(dueDateValues.get(0), dueDateValues.get(1), 
dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 3, 4), fromDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 4, 4), dueDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+
+        fromDateValues = (ArrayList<Integer>) periods.get(4).get("fromDate");
+        fromDate = LocalDate.of(fromDateValues.get(0), fromDateValues.get(1), 
fromDateValues.get(2));
+        dueDateValues = (ArrayList<Integer>) periods.get(4).get("dueDate");
+        dueDate = LocalDate.of(dueDateValues.get(0), dueDateValues.get(1), 
dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 4, 4), fromDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 5, 4), dueDate,
+                "Verifying Repayment Rescheduled Date before Running Apply 
Holidays to Loans Scheduler Job");
+
+        fromDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(1).get("fromDate");
+        fromDate = LocalDate.of(fromDateValues.get(0), fromDateValues.get(1), 
fromDateValues.get(2));
+        dueDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(1).get("dueDate");
+        dueDate = LocalDate.of(dueDateValues.get(0), dueDateValues.get(1), 
dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 1, 4), fromDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 2, 4), dueDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+
+        fromDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(2).get("fromDate");
+        fromDate = LocalDate.of(fromDateValues.get(0), fromDateValues.get(1), 
fromDateValues.get(2));
+        dueDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(2).get("dueDate");
+        dueDate = LocalDate.of(dueDateValues.get(0), dueDateValues.get(1), 
dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 2, 4), fromDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 3, 4), dueDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+
+        fromDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(3).get("fromDate");
+        fromDate = LocalDate.of(fromDateValues.get(0), fromDateValues.get(1), 
fromDateValues.get(2));
+        dueDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(3).get("dueDate");
+        dueDate = LocalDate.of(dueDateValues.get(0), dueDateValues.get(1), 
dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 3, 4), fromDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 5, 4), dueDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+
+        fromDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(4).get("fromDate");
+        fromDate = LocalDate.of(fromDateValues.get(0), fromDateValues.get(1), 
fromDateValues.get(2));
+        dueDateValues = (ArrayList<Integer>) 
periodsAfterRescheduleApplied.get(4).get("dueDate");
+        dueDate = LocalDate.of(dueDateValues.get(0), dueDateValues.get(1), 
dueDateValues.get(2));
+        Assertions.assertEquals(LocalDate.of(2024, 5, 4), fromDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(LocalDate.of(2024, 6, 4), dueDate,
+                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+
+    }
+
     @Test
     public void testApplyDueFeeChargesForSavingsJobOutcome() throws 
InterruptedException {
         this.savingsAccountHelper = new SavingsAccountHelper(requestSpec, 
responseSpec);
diff --git 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/HolidayHelper.java
 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/HolidayHelper.java
index 32f9a08e4..163057505 100644
--- 
a/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/HolidayHelper.java
+++ 
b/integration-tests/src/test/java/org/apache/fineract/integrationtests/common/HolidayHelper.java
@@ -64,6 +64,25 @@ public class HolidayHelper {
         return HolidayCreateJson;
     }
 
+    public static String getCreateType1HolidayDataAsJSON() {
+        final HashMap<String, Object> map = new HashMap<>();
+        List<HashMap<String, String>> offices = new ArrayList<HashMap<String, 
String>>();
+        HashMap<String, String> officeMap = new HashMap<>();
+        officeMap.put("officeId", OFFICE_ID);
+        offices.add(officeMap);
+
+        map.put("offices", offices);
+        map.put("locale", "en");
+        map.put("dateFormat", "dd MMMM yyyy");
+        map.put("name", Utils.uniqueRandomStringGenerator("HOLIDAY_", 5));
+        map.put("fromDate", "04 April 2024");
+        map.put("toDate", "04 April 2024");
+        map.put("reschedulingType", 1);
+        String HolidayCreateJson = new Gson().toJson(map);
+        LOG.info("{}", HolidayCreateJson);
+        return HolidayCreateJson;
+    }
+
     public static String getActivateHolidayDataAsJSON() {
         final HashMap<String, String> map = new HashMap<>();
         String activateHoliday = new Gson().toJson(map);
@@ -75,6 +94,10 @@ public class HolidayHelper {
         return Utils.performServerPost(requestSpec, responseSpec, 
CREATE_HOLIDAY_URL, getCreateHolidayDataAsJSON(), "resourceId");
     }
 
+    public static Integer createTyoe1Holidays(final RequestSpecification 
requestSpec, final ResponseSpecification responseSpec) {
+        return Utils.performServerPost(requestSpec, responseSpec, 
CREATE_HOLIDAY_URL, getCreateType1HolidayDataAsJSON(), "resourceId");
+    }
+
     public static Integer activateHolidays(final RequestSpecification 
requestSpec, final ResponseSpecification responseSpec,
             final String holidayID) {
         final String ACTIVATE_HOLIDAY_URL = HOLIDAYS_URL + "/" + holidayID + 
"?command=activate&" + Utils.TENANT_IDENTIFIER;

Reply via email to