Skip to content

Commit 8d65284

Browse files
mariiaKraievskaadamsaghy
authored andcommitted
FINERACT-2017: Fix apply holidays to loans job
1 parent aff009c commit 8d65284

4 files changed

Lines changed: 97 additions & 9 deletions

File tree

fineract-core/src/main/java/org/apache/fineract/infrastructure/core/service/DateUtils.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,24 @@ public static String format(LocalDateTime dateTime, String format, Locale locale
352352
return dateTime == null ? null : dateTime.format(getDateTimeFormatter(format, locale));
353353
}
354354

355+
/**
356+
* Checks if a specific date falls within a given range (inclusive).
357+
*
358+
* @param targetDate
359+
* the date to be checked
360+
* @param startDate
361+
* the start date of the range
362+
* @param endDate
363+
* the end date of the range
364+
* @return true if targetDate is within range or equal to start/end dates, otherwise false
365+
*/
366+
public static boolean isDateWithinRange(LocalDate targetDate, LocalDate startDate, LocalDate endDate) {
367+
if (targetDate == null || startDate == null || endDate == null) {
368+
throw new IllegalArgumentException("Dates must not be null");
369+
}
370+
return !targetDate.isBefore(startDate) && !targetDate.isAfter(endDate);
371+
}
372+
355373
@NotNull
356374
private static DateTimeFormatter getDateFormatter(String format, Locale locale) {
357375
DateTimeFormatter formatter = DEFAULT_DATE_FORMATTER;

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/jobs/applyholidaystoloans/ApplyHolidaysToLoansTasklet.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
package org.apache.fineract.portfolio.loanaccount.jobs.applyholidaystoloans;
2020

21+
import static org.apache.fineract.infrastructure.core.service.DateUtils.isDateWithinRange;
22+
2123
import java.time.LocalDate;
2224
import java.util.ArrayList;
2325
import java.util.Arrays;
@@ -129,13 +131,13 @@ private void adjustRepaymentSchedules(Loan loan, Holiday holiday, LocalDate adju
129131
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
130132
}
131133

132-
if (!DateUtils.isBefore(oldDueDate, holiday.getFromDate())) {
134+
if (isDateWithinRange(oldDueDate, holiday.getFromDate(), holiday.getToDate())) {
133135
// FIXME: AA do we need to apply non-working days.
134136
// Assuming holiday's repayment reschedule to date cannot be
135137
// created on a non-working day.
136138

137-
adjustedRescheduleToDate = scheduledDateGenerator.generateNextRepaymentDate(adjustedRescheduleToDate, loanApplicationTerms,
138-
false);
139+
adjustedRescheduleToDate = scheduledDateGenerator.generateNextRepaymentDateWhenHolidayApply(adjustedRescheduleToDate,
140+
loanApplicationTerms);
139141
loanRepaymentScheduleInstallment.updateDueDate(adjustedRescheduleToDate);
140142
}
141143
tmpFromDate = loanRepaymentScheduleInstallment.getDueDate();

fineract-provider/src/main/java/org/apache/fineract/portfolio/loanaccount/loanschedule/domain/DefaultScheduledDateGenerator.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,4 +358,42 @@ public LocalDate generateNextScheduleDateStartingFromDisburseDateOrRescheduleDat
358358
}
359359
return adjustedDate;
360360
}
361+
362+
public LocalDate generateNextRepaymentDateWhenHolidayApply(final LocalDate lastRepaymentDate,
363+
final LoanApplicationTerms loanApplicationTerms) {
364+
LocalDate seedDate;
365+
String reccuringString;
366+
Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
367+
LocalDate dueRepaymentPeriodDate = lastRepaymentDate;
368+
dueRepaymentPeriodDate = (LocalDate) CalendarUtils.adjustDate(dueRepaymentPeriodDate, loanApplicationTerms.getSeedDate(),
369+
loanApplicationTerms.getRepaymentPeriodFrequencyType());
370+
if (currentCalendar != null) {
371+
// If we have currentCalendar object, this means there is a
372+
// calendar associated with
373+
// the loan, and we should use it in order to calculate next
374+
// repayment
375+
376+
CalendarHistory calendarHistory = null;
377+
CalendarHistoryDataWrapper calendarHistoryDataWrapper = loanApplicationTerms.getCalendarHistoryDataWrapper();
378+
if (calendarHistoryDataWrapper != null) {
379+
calendarHistory = loanApplicationTerms.getCalendarHistoryDataWrapper().getCalendarHistory(dueRepaymentPeriodDate);
380+
}
381+
382+
// get the start date from the calendar history
383+
if (calendarHistory == null) {
384+
seedDate = currentCalendar.getStartDateLocalDate();
385+
reccuringString = currentCalendar.getRecurrence();
386+
} else {
387+
seedDate = calendarHistory.getStartDate();
388+
reccuringString = calendarHistory.getRecurrence();
389+
}
390+
391+
dueRepaymentPeriodDate = CalendarUtils.getNextRepaymentMeetingDate(reccuringString, seedDate, lastRepaymentDate,
392+
loanApplicationTerms.getRepaymentEvery(),
393+
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(loanApplicationTerms.getLoanTermPeriodFrequencyType()),
394+
loanApplicationTerms.isSkipRepaymentOnFirstDayofMonth(), loanApplicationTerms.getNumberOfdays());
395+
}
396+
397+
return dueRepaymentPeriodDate;
398+
}
361399
}

integration-tests/src/test/java/org/apache/fineract/integrationtests/SchedulerJobsTestResults.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,11 @@
4444
import java.util.ArrayList;
4545
import java.util.Calendar;
4646
import java.util.HashMap;
47+
import java.util.LinkedHashMap;
4748
import java.util.List;
4849
import java.util.Locale;
4950
import java.util.Map;
51+
import java.util.Objects;
5052
import java.util.TimeZone;
5153
import org.apache.fineract.client.models.BusinessDateRequest;
5254
import org.apache.fineract.client.models.GetJournalEntriesTransactionIdResponse;
@@ -299,7 +301,7 @@ public void testApplyHolidaysToLoansJobOutcome() throws InterruptedException {
299301
final Integer loanProductID = createLoanProduct(null);
300302
Assertions.assertNotNull(loanProductID);
301303

302-
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "10 January 2013");
304+
final Integer loanID = applyForLoanApplication(clientID.toString(), loanProductID.toString(), null, "01 January 2013");
303305
Assertions.assertNotNull(loanID);
304306

305307
HashMap loanStatusHashMap = LoanStatusChecker.getStatusOfLoan(requestSpec, responseSpec, loanID);
@@ -329,21 +331,49 @@ public void testApplyHolidaysToLoansJobOutcome() throws InterruptedException {
329331

330332
if (!enabled) {
331333
enabled = true;
332-
configId = GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, configId, enabled);
334+
GlobalConfigurationHelper.updateEnabledFlagForGlobalConfiguration(requestSpec, responseSpec, configId, enabled);
333335
}
334336

335337
holidayId = HolidayHelper.activateHolidays(requestSpec, responseSpec, holidayId.toString());
336338
Assertions.assertNotNull(holidayId);
337339

340+
HashMap holidayData = HolidayHelper.getHolidayById(requestSpec, responseSpec, holidayId.toString());
341+
ArrayList<Integer> repaymentsRescheduledDate = (ArrayList<Integer>) holidayData.get("repaymentsRescheduledTo");
342+
343+
// Loan Repayment Schedule Before Apply Holidays To Loans
344+
LinkedHashMap repaymentScheduleHashMap = JsonPath.from(loanDetails).get("repaymentSchedule");
345+
ArrayList<LinkedHashMap> periods = (ArrayList<LinkedHashMap>) repaymentScheduleHashMap.get("periods");
346+
347+
for (LinkedHashMap period : periods) {
348+
ArrayList<Integer> fromDate = (ArrayList<Integer>) period.get("fromDate");
349+
if (fromDate != null && Objects.equals(fromDate.get(1), repaymentsRescheduledDate.get(1))) {
350+
Assertions.assertNotEquals(repaymentsRescheduledDate.get(2), fromDate.get(2),
351+
"Verifying Repayment Rescheduled Day before Running Apply Holidays to Loans Scheduler Job");
352+
}
353+
}
354+
338355
String JobName = "Apply Holidays To Loans";
339356

340357
this.schedulerJobHelper.executeAndAwaitJob(JobName);
341358

342-
HashMap holidayData = HolidayHelper.getHolidayById(requestSpec, responseSpec, holidayId.toString());
343-
ArrayList<Integer> repaymentsRescheduledDate = (ArrayList<Integer>) holidayData.get("repaymentsRescheduledTo");
359+
// Loan Repayment Schedule After Apply Holidays To Loans
360+
loanDetails = this.loanTransactionHelper.getLoanDetails(requestSpec, responseSpec, loanID);
361+
repaymentScheduleHashMap = JsonPath.from(loanDetails).get("repaymentSchedule");
362+
periods = (ArrayList<LinkedHashMap>) repaymentScheduleHashMap.get("periods");
363+
ArrayList<Integer> dateToApplyHolidays = null;
364+
365+
for (LinkedHashMap period : periods) {
366+
ArrayList<Integer> fromDate = (ArrayList<Integer>) period.get("fromDate");
367+
if (fromDate != null && Objects.equals(fromDate.get(1), repaymentsRescheduledDate.get(1))) {
368+
dateToApplyHolidays = fromDate;
369+
}
370+
}
344371

345-
Assertions.assertEquals(repaymentsRescheduledDate, repaymentsRescheduledDate,
346-
"Verifying Repayment Rescheduled Date after Running Apply Holidays to Loans Scheduler Job");
372+
Assertions.assertNotNull(dateToApplyHolidays);
373+
Assertions.assertEquals(repaymentsRescheduledDate.get(0), dateToApplyHolidays.get(0),
374+
"Verifying Repayment Rescheduled Year after Running Apply Holidays to Loans Scheduler Job");
375+
Assertions.assertEquals(repaymentsRescheduledDate.get(2), dateToApplyHolidays.get(2),
376+
"Verifying Repayment Rescheduled Day after Running Apply Holidays to Loans Scheduler Job");
347377
}
348378

349379
@Test

0 commit comments

Comments
 (0)