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 8d6528408 FINERACT-2017: Fix apply holidays to loans job
8d6528408 is described below

commit 8d6528408df6ca9431295c87c5320a754ff48279
Author: mariiaKraievska <[email protected]>
AuthorDate: Tue Dec 19 00:07:00 2023 +0200

    FINERACT-2017: Fix apply holidays to loans job
---
 .../infrastructure/core/service/DateUtils.java     | 18 ++++++++++
 .../ApplyHolidaysToLoansTasklet.java               |  8 +++--
 .../domain/DefaultScheduledDateGenerator.java      | 38 ++++++++++++++++++++
 .../integrationtests/SchedulerJobsTestResults.java | 42 ++++++++++++++++++----
 4 files changed, 97 insertions(+), 9 deletions(-)

diff --git 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
index d706e8fd5..c8a9ccb6f 100644
--- 
a/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
+++ 
b/fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java
@@ -352,6 +352,24 @@ public final class DateUtils {
         return dateTime == null ? null : 
dateTime.format(getDateTimeFormatter(format, locale));
     }
 
+    /**
+     * Checks if a specific date falls within a given range (inclusive).
+     *
+     * @param targetDate
+     *            the date to be checked
+     * @param startDate
+     *            the start date of the range
+     * @param endDate
+     *            the end date of the range
+     * @return true if targetDate is within range or equal to start/end dates, 
otherwise false
+     */
+    public static boolean isDateWithinRange(LocalDate targetDate, LocalDate 
startDate, LocalDate endDate) {
+        if (targetDate == null || startDate == null || endDate == null) {
+            throw new IllegalArgumentException("Dates must not be null");
+        }
+        return !targetDate.isBefore(startDate) && !targetDate.isAfter(endDate);
+    }
+
     @NotNull
     private static DateTimeFormatter getDateFormatter(String format, Locale 
locale) {
         DateTimeFormatter formatter = DEFAULT_DATE_FORMATTER;
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 cf77e23c6..67bbeeac6 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
@@ -18,6 +18,8 @@
  */
 package org.apache.fineract.portfolio.loanaccount.jobs.applyholidaystoloans;
 
+import static 
org.apache.fineract.infrastructure.core.service.DateUtils.isDateWithinRange;
+
 import java.time.LocalDate;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -129,13 +131,13 @@ public class ApplyHolidaysToLoansTasklet implements 
Tasklet {
                 loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
             }
 
-            if (!DateUtils.isBefore(oldDueDate, holiday.getFromDate())) {
+            if (isDateWithinRange(oldDueDate, holiday.getFromDate(), 
holiday.getToDate())) {
                 // 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);
+                adjustedRescheduleToDate = 
scheduledDateGenerator.generateNextRepaymentDateWhenHolidayApply(adjustedRescheduleToDate,
+                        loanApplicationTerms);
                 
loanRepaymentScheduleInstallment.updateDueDate(adjustedRescheduleToDate);
             }
             tmpFromDate = loanRepaymentScheduleInstallment.getDueDate();
diff --git 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
index 72e529af0..5108bff37 100644
--- 
a/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
+++ 
b/fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java
@@ -358,4 +358,42 @@ public class DefaultScheduledDateGenerator implements 
ScheduledDateGenerator {
         }
         return adjustedDate;
     }
+
+    public LocalDate generateNextRepaymentDateWhenHolidayApply(final LocalDate 
lastRepaymentDate,
+            final LoanApplicationTerms loanApplicationTerms) {
+        LocalDate seedDate;
+        String reccuringString;
+        Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
+        LocalDate dueRepaymentPeriodDate = lastRepaymentDate;
+        dueRepaymentPeriodDate = (LocalDate) 
CalendarUtils.adjustDate(dueRepaymentPeriodDate, 
loanApplicationTerms.getSeedDate(),
+                loanApplicationTerms.getRepaymentPeriodFrequencyType());
+        if (currentCalendar != null) {
+            // If we have currentCalendar object, this means there is a
+            // calendar associated with
+            // the loan, and we should use it in order to calculate next
+            // repayment
+
+            CalendarHistory calendarHistory = null;
+            CalendarHistoryDataWrapper calendarHistoryDataWrapper = 
loanApplicationTerms.getCalendarHistoryDataWrapper();
+            if (calendarHistoryDataWrapper != null) {
+                calendarHistory = 
loanApplicationTerms.getCalendarHistoryDataWrapper().getCalendarHistory(dueRepaymentPeriodDate);
+            }
+
+            // get the start date from the calendar history
+            if (calendarHistory == null) {
+                seedDate = currentCalendar.getStartDateLocalDate();
+                reccuringString = currentCalendar.getRecurrence();
+            } else {
+                seedDate = calendarHistory.getStartDate();
+                reccuringString = calendarHistory.getRecurrence();
+            }
+
+            dueRepaymentPeriodDate = 
CalendarUtils.getNextRepaymentMeetingDate(reccuringString, seedDate, 
lastRepaymentDate,
+                    loanApplicationTerms.getRepaymentEvery(),
+                    
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(loanApplicationTerms.getLoanTermPeriodFrequencyType()),
+                    loanApplicationTerms.isSkipRepaymentOnFirstDayofMonth(), 
loanApplicationTerms.getNumberOfdays());
+        }
+
+        return dueRepaymentPeriodDate;
+    }
 }
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 bb8d478b7..eb410a05d 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
@@ -44,9 +44,11 @@ import java.time.temporal.ChronoUnit;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
+import java.util.Objects;
 import java.util.TimeZone;
 import org.apache.fineract.client.models.BusinessDateRequest;
 import 
org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
@@ -299,7 +301,7 @@ public class SchedulerJobsTestResults {
         final Integer loanProductID = createLoanProduct(null);
         Assertions.assertNotNull(loanProductID);
 
-        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "10 January 2013");
+        final Integer loanID = applyForLoanApplication(clientID.toString(), 
loanProductID.toString(), null, "01 January 2013");
         Assertions.assertNotNull(loanID);
 
         HashMap loanStatusHashMap = 
LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -329,21 +331,49 @@ public class SchedulerJobsTestResults {
 
         if (!enabled) {
             enabled = true;
-            configId = 
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, 
responseSpec, configId, enabled);
+            
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, 
responseSpec, configId, enabled);
         }
 
         holidayId = HolidayHelper.activateHolidays(requestSpec, responseSpec, 
holidayId.toString());
         Assertions.assertNotNull(holidayId);
 
+        HashMap holidayData = HolidayHelper.getHolidayById(requestSpec, 
responseSpec, holidayId.toString());
+        ArrayList<Integer> repaymentsRescheduledDate = (ArrayList<Integer>) 
holidayData.get("repaymentsRescheduledTo");
+
+        // Loan Repayment Schedule Before Apply Holidays To Loans
+        LinkedHashMap repaymentScheduleHashMap = 
JsonPath.from(loanDetails).get("repaymentSchedule");
+        ArrayList<LinkedHashMap> periods = (ArrayList<LinkedHashMap>) 
repaymentScheduleHashMap.get("periods");
+
+        for (LinkedHashMap period : periods) {
+            ArrayList<Integer> fromDate = (ArrayList<Integer>) 
period.get("fromDate");
+            if (fromDate != null && Objects.equals(fromDate.get(1), 
repaymentsRescheduledDate.get(1))) {
+                Assertions.assertNotEquals(repaymentsRescheduledDate.get(2), 
fromDate.get(2),
+                        "Verifying Repayment Rescheduled Day before Running 
Apply Holidays to Loans Scheduler Job");
+            }
+        }
+
         String JobName = "Apply Holidays To Loans";
 
         this.schedulerJobHelper.executeAndAwaitJob(JobName);
 
-        HashMap holidayData = HolidayHelper.getHolidayById(requestSpec, 
responseSpec, holidayId.toString());
-        ArrayList<Integer> repaymentsRescheduledDate = (ArrayList<Integer>) 
holidayData.get("repaymentsRescheduledTo");
+        // Loan Repayment Schedule After Apply Holidays To Loans
+        loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, 
responseSpec, loanID);
+        repaymentScheduleHashMap = 
JsonPath.from(loanDetails).get("repaymentSchedule");
+        periods = (ArrayList<LinkedHashMap>) 
repaymentScheduleHashMap.get("periods");
+        ArrayList<Integer> dateToApplyHolidays = null;
+
+        for (LinkedHashMap period : periods) {
+            ArrayList<Integer> fromDate = (ArrayList<Integer>) 
period.get("fromDate");
+            if (fromDate != null && Objects.equals(fromDate.get(1), 
repaymentsRescheduledDate.get(1))) {
+                dateToApplyHolidays = fromDate;
+            }
+        }
 
-        Assertions.assertEquals(repaymentsRescheduledDate, 
repaymentsRescheduledDate,
-                "Verifying Repayment Rescheduled Date after Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertNotNull(dateToApplyHolidays);
+        Assertions.assertEquals(repaymentsRescheduledDate.get(0), 
dateToApplyHolidays.get(0),
+                "Verifying Repayment Rescheduled Year after Running Apply 
Holidays to Loans Scheduler Job");
+        Assertions.assertEquals(repaymentsRescheduledDate.get(2), 
dateToApplyHolidays.get(2),
+                "Verifying Repayment Rescheduled Day after Running Apply 
Holidays to Loans Scheduler Job");
     }
 
     @Test

Reply via email to