FINERACT-1981: Remove hardcoded MoneyHelper dependency during progressive loan

This commit is contained in:
Andrii Kulminskyi
2024-10-22 18:45:28 +02:00
committed by Adam Saghy
parent 806ee69798
commit a9ecdf97a4
31 changed files with 536 additions and 355 deletions
@@ -1,459 +0,0 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import java.time.LocalDate;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import lombok.extern.slf4j.Slf4j;
import net.fortuna.ical4j.model.Recur;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.holiday.service.HolidayUtil;
import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO;
import org.apache.fineract.organisation.workingdays.domain.RepaymentRescheduleType;
import org.apache.fineract.organisation.workingdays.service.WorkingDaysUtil;
import org.apache.fineract.portfolio.calendar.data.CalendarHistoryDataWrapper;
import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.apache.fineract.portfolio.loanaccount.data.HolidayDetailDTO;
import org.apache.fineract.portfolio.loanaccount.data.LoanTermVariationsData;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class DefaultScheduledDateGenerator implements ScheduledDateGenerator {
@Override
public List<LoanScheduleModelRepaymentPeriod> generateRepaymentPeriods(final LocalDate scheduleStartDate,
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
final Money zeroAmount = Money.zero(loanApplicationTerms.getCurrency());
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
final ArrayList<LoanScheduleModelRepaymentPeriod> repaymentPeriods = new ArrayList<>(numberOfRepayments);
LocalDate lastRepaymentDate = scheduleStartDate;
LocalDate nextRepaymentDate;
boolean isFirstRepayment = true;
for (int repaymentPeriodNumber = 1; repaymentPeriodNumber <= numberOfRepayments; repaymentPeriodNumber++) {
nextRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment);
if (repaymentPeriodNumber == numberOfRepayments) { // last period
if (loanApplicationTerms.getFixedLength() != null
&& loanApplicationTerms.calculateMaxDateForFixedLength().compareTo(nextRepaymentDate) != 0) {
nextRepaymentDate = loanApplicationTerms.calculateMaxDateForFixedLength();
}
nextRepaymentDate = adjustRepaymentDate(nextRepaymentDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
}
nextRepaymentDate = applyLoanTermVariations(loanApplicationTerms, nextRepaymentDate);
repaymentPeriods.add(LoanScheduleModelRepaymentPeriod.repayment(repaymentPeriodNumber, lastRepaymentDate, nextRepaymentDate,
zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, zeroAmount, false));
lastRepaymentDate = nextRepaymentDate;
isFirstRepayment = false;
}
return repaymentPeriods;
}
private LocalDate applyLoanTermVariations(final LoanApplicationTerms loanApplicationTerms, final LocalDate scheduledDueDate) {
LocalDate modifiedScheduledDueDate = scheduledDueDate;
// due date changes should be applied only for that dueDate
if (loanApplicationTerms.getLoanTermVariations().hasDueDateVariation(scheduledDueDate)) {
LoanTermVariationsData loanTermVariationsData = loanApplicationTerms.getLoanTermVariations().nextDueDateVariation();
if (DateUtils.isEqual(modifiedScheduledDueDate, loanTermVariationsData.getTermVariationApplicableFrom())) {
modifiedScheduledDueDate = loanTermVariationsData.getDateValue();
loanApplicationTerms.updateVariationDays(DateUtils.getDifferenceInDays(scheduledDueDate, modifiedScheduledDueDate));
}
}
return modifiedScheduledDueDate;
}
@Override
public LocalDate getLastRepaymentDate(final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
final int numberOfRepayments = loanApplicationTerms.getNumberOfRepayments();
LocalDate lastRepaymentDate = loanApplicationTerms.getRepaymentStartDate();
boolean isFirstRepayment = true;
for (int repaymentPeriod = 1; repaymentPeriod <= numberOfRepayments; repaymentPeriod++) {
lastRepaymentDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment);
isFirstRepayment = false;
}
if (loanApplicationTerms.getFixedLength() != null
&& loanApplicationTerms.calculateMaxDateForFixedLength().compareTo(lastRepaymentDate) != 0) {
lastRepaymentDate = loanApplicationTerms.calculateMaxDateForFixedLength();
}
lastRepaymentDate = adjustRepaymentDate(lastRepaymentDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
return lastRepaymentDate;
}
@Override
public LocalDate generateNextRepaymentDate(final LocalDate lastRepaymentDate, final LoanApplicationTerms loanApplicationTerms,
boolean isFirstRepayment) {
final LocalDate firstRepaymentPeriodDate = loanApplicationTerms.getCalculatedRepaymentsStartingFromLocalDate();
LocalDate dueRepaymentPeriodDate = null;
if (isFirstRepayment && firstRepaymentPeriodDate != null) {
dueRepaymentPeriodDate = firstRepaymentPeriodDate;
} else {
LocalDate seedDate = null;
String reccuringString = null;
Calendar currentCalendar = loanApplicationTerms.getLoanCalendar();
dueRepaymentPeriodDate = getRepaymentPeriodDate(loanApplicationTerms.getRepaymentPeriodFrequencyType(),
loanApplicationTerms.getRepaymentEvery(), 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;
}
@Override
public LocalDate generateNextRepaymentDate(LocalDate lastRepaymentDate, LoanApplicationTerms loanApplicationTerms,
final boolean isFirstRepayment, final Integer periodNumber) {
LocalDate dueRepaymentPeriodDate = generateNextRepaymentDate(lastRepaymentDate, loanApplicationTerms, isFirstRepayment);
// Fixed Length validation only for Last Installment
if (loanApplicationTerms.isLastPeriod(periodNumber)) {
final LocalDate maxDateForFixedLength = loanApplicationTerms.calculateMaxDateForFixedLength();
if (maxDateForFixedLength != null && dueRepaymentPeriodDate.compareTo(maxDateForFixedLength) != 0) {
dueRepaymentPeriodDate = maxDateForFixedLength;
}
}
return dueRepaymentPeriodDate;
}
@Override
public AdjustedDateDetailsDTO adjustRepaymentDate(final LocalDate dueRepaymentPeriodDate,
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
final LocalDate adjustedDate = dueRepaymentPeriodDate;
return getAdjustedDateDetailsDTO(dueRepaymentPeriodDate, loanApplicationTerms, holidayDetailDTO, adjustedDate);
}
private AdjustedDateDetailsDTO getAdjustedDateDetailsDTO(final LocalDate dueRepaymentPeriodDate,
final LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO, final LocalDate adjustedDate) {
final boolean isFirstRepayment = false;
final LocalDate nextRepaymentPeriodDueDate = generateNextRepaymentDate(adjustedDate, loanApplicationTerms, isFirstRepayment);
final AdjustedDateDetailsDTO newAdjustedDateDetailsDTO = new AdjustedDateDetailsDTO(adjustedDate, dueRepaymentPeriodDate,
nextRepaymentPeriodDueDate);
return recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(newAdjustedDateDetailsDTO,
loanApplicationTerms, holidayDetailDTO, isFirstRepayment);
}
/**
* Recursively checking non working days and holidays and working days exemption to generate next repayment period
* date Base on the configuration
*
* @param adjustedDateDetailsDTO
* @param loanApplicationTerms
* @param holidayDetailDTO
* @param isFirstRepayment
* @return
*/
private AdjustedDateDetailsDTO recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(
final AdjustedDateDetailsDTO adjustedDateDetailsDTO, final LoanApplicationTerms loanApplicationTerms,
final HolidayDetailDTO holidayDetailDTO, final boolean isFirstRepayment) {
final Recur recur = CalendarUtils.getICalRecur(holidayDetailDTO.getWorkingDays().getRecurrence());
final boolean isSevenDaysWeek = (recur.getDayList().size() == 7); // 7 Seven days in the week
// If Workings days are not seven day week
if (!isSevenDaysWeek) {
checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
isFirstRepayment);
}
// Check Holidays If applied
checkAndUpdateWorkingDayIfRepaymentDateIsHolidayDay(adjustedDateDetailsDTO, holidayDetailDTO, loanApplicationTerms,
isFirstRepayment);
/**
* Check Changed Schedule Date is holiday or is not a working day Then re-call this method to get the non
* holiday and working day
*/
if ((holidayDetailDTO.isHolidayEnabled() && HolidayUtil.getApplicableHoliday(adjustedDateDetailsDTO.getChangedScheduleDate(),
holidayDetailDTO.getHolidays()) != null)
|| WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate())) {
recursivelyCheckNonWorkingDaysAndHolidaysAndWorkingDaysExemptionToGenerateNextRepaymentPeriodDate(adjustedDateDetailsDTO,
loanApplicationTerms, holidayDetailDTO, isFirstRepayment);
}
return adjustedDateDetailsDTO;
}
/**
* This method to check and update the working day if repayment date is holiday
*
* @param adjustedDateDetailsDTO
* @param holidayDetailDTO
* @param loanApplicationTerms
* @param isFirstRepayment
*/
private void checkAndUpdateWorkingDayIfRepaymentDateIsHolidayDay(final AdjustedDateDetailsDTO adjustedDateDetailsDTO,
final HolidayDetailDTO holidayDetailDTO, final LoanApplicationTerms loanApplicationTerms, final boolean isFirstRepayment) {
if (holidayDetailDTO.isHolidayEnabled()) {
Holiday applicableHolidayForNewAdjustedDate = null;
while ((applicableHolidayForNewAdjustedDate = HolidayUtil.getApplicableHoliday(adjustedDateDetailsDTO.getChangedScheduleDate(),
holidayDetailDTO.getHolidays())) != null) {
if (applicableHolidayForNewAdjustedDate.getReScheduleType().isResheduleToNextRepaymentDate()) {
LocalDate nextRepaymentPeriodDueDate = adjustedDateDetailsDTO.getChangedActualRepaymentDate();
while (!DateUtils.isAfter(nextRepaymentPeriodDueDate, adjustedDateDetailsDTO.getChangedScheduleDate())) {
nextRepaymentPeriodDueDate = generateNextRepaymentDate(nextRepaymentPeriodDueDate, loanApplicationTerms,
isFirstRepayment);
}
adjustedDateDetailsDTO.setChangedScheduleDate(nextRepaymentPeriodDueDate);
adjustedDateDetailsDTO.setNextRepaymentPeriodDueDate(nextRepaymentPeriodDueDate);
adjustedDateDetailsDTO.setChangedActualRepaymentDate(adjustedDateDetailsDTO.getChangedScheduleDate());
} else {
HolidayUtil.updateRepaymentRescheduleDateToWorkingDayIfItIsHoliday(adjustedDateDetailsDTO,
applicableHolidayForNewAdjustedDate);
}
}
}
}
/**
* This method to check and update the working day if repayment date is non working day
*
* @param adjustedDateDetailsDTO
* @param holidayDetailDTO
* @param isFirstRepayment
* @param loanApplicationTerms
*/
private void checkAndUpdateWorkingDayIfRepaymentDateIsNonWorkingDay(final AdjustedDateDetailsDTO adjustedDateDetailsDTO,
final HolidayDetailDTO holidayDetailDTO, final LoanApplicationTerms loanApplicationTerms, final boolean isFirstRepayment) {
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), adjustedDateDetailsDTO.getChangedScheduleDate())) {
final RepaymentRescheduleType repaymentRescheduleType = WorkingDaysUtil
.getRepaymentRescheduleType(holidayDetailDTO.getWorkingDays());
if (repaymentRescheduleType.isMoveToNextRepaymentDay()) {
LocalDate nextRepaymentPeriodDueDate = adjustedDateDetailsDTO.getNextRepaymentPeriodDueDate();
while (WorkingDaysUtil.isNonWorkingDay(holidayDetailDTO.getWorkingDays(), nextRepaymentPeriodDueDate)
|| DateUtils.isAfter(adjustedDateDetailsDTO.getChangedScheduleDate(), nextRepaymentPeriodDueDate)) {
nextRepaymentPeriodDueDate = generateNextRepaymentDate(nextRepaymentPeriodDueDate, loanApplicationTerms,
isFirstRepayment);
}
adjustedDateDetailsDTO.setNextRepaymentPeriodDueDate(nextRepaymentPeriodDueDate);
}
WorkingDaysUtil.updateWorkingDayIfRepaymentDateIsNonWorkingDay(adjustedDateDetailsDTO, holidayDetailDTO.getWorkingDays());
}
}
@Override
public LocalDate getRepaymentPeriodDate(final PeriodFrequencyType frequency, final int repaidEvery, final LocalDate startDate) {
LocalDate dueRepaymentPeriodDate = startDate;
switch (frequency) {
case DAYS:
dueRepaymentPeriodDate = startDate.plusDays(repaidEvery);
break;
case WEEKS:
dueRepaymentPeriodDate = startDate.plusWeeks(repaidEvery);
break;
case MONTHS:
dueRepaymentPeriodDate = startDate.plusMonths(repaidEvery);
break;
case YEARS:
dueRepaymentPeriodDate = startDate.plusYears(repaidEvery);
break;
case INVALID:
break;
case WHOLE_TERM:
log.error("TODO Implement getRepaymentPeriodDate for WHOLE_TERM");
break;
}
return dueRepaymentPeriodDate;
}
@Override
public Boolean isDateFallsInSchedule(final PeriodFrequencyType frequency, final int repaidEvery, final LocalDate startDate,
final LocalDate date) {
boolean isScheduledDate = false;
switch (frequency) {
case DAYS:
int diff = Math.toIntExact(ChronoUnit.DAYS.between(startDate, date));
isScheduledDate = (diff % repaidEvery) == 0;
break;
case WEEKS:
int weekDiff = Math.toIntExact(ChronoUnit.WEEKS.between(startDate, date));
isScheduledDate = (weekDiff % repaidEvery) == 0;
if (isScheduledDate) {
LocalDate modifiedDate = startDate.plusWeeks(weekDiff);
isScheduledDate = DateUtils.isEqual(modifiedDate, date);
}
break;
case MONTHS:
int monthDiff = Math.toIntExact(ChronoUnit.MONTHS.between(startDate, date));
isScheduledDate = (monthDiff % repaidEvery) == 0;
if (isScheduledDate) {
LocalDate modifiedDate = startDate.plusMonths(monthDiff);
isScheduledDate = DateUtils.isEqual(modifiedDate, date);
}
break;
case YEARS:
int yearDiff = Math.toIntExact(ChronoUnit.YEARS.between(startDate, date));
isScheduledDate = (yearDiff % repaidEvery) == 0;
if (isScheduledDate) {
LocalDate modifiedDate = startDate.plusYears(yearDiff);
isScheduledDate = DateUtils.isEqual(modifiedDate, date);
}
break;
case INVALID:
break;
case WHOLE_TERM:
log.error("TODO Implement isDateFallsInSchedule for WHOLE_TERM");
break;
}
return isScheduledDate;
}
@Override
public LocalDate idealDisbursementDateBasedOnFirstRepaymentDate(final PeriodFrequencyType repaymentPeriodFrequencyType,
final int repaidEvery, final LocalDate firstRepaymentDate, final Calendar loanCalendar, final HolidayDetailDTO holidayDetailDTO,
final LoanApplicationTerms loanApplicationTerms) {
LocalDate idealDisbursementDate = null;
switch (repaymentPeriodFrequencyType) {
case DAYS:
idealDisbursementDate = firstRepaymentDate.minusDays(repaidEvery);
break;
case WEEKS:
idealDisbursementDate = firstRepaymentDate.minusWeeks(repaidEvery);
break;
case MONTHS:
if (loanCalendar == null) {
idealDisbursementDate = firstRepaymentDate.minusMonths(repaidEvery);
} else {
idealDisbursementDate = CalendarUtils.getNewRepaymentMeetingDate(loanCalendar.getRecurrence(),
firstRepaymentDate.minusMonths(repaidEvery), firstRepaymentDate.minusMonths(repaidEvery), repaidEvery,
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(repaymentPeriodFrequencyType),
holidayDetailDTO.getWorkingDays(), loanApplicationTerms.isSkipRepaymentOnFirstDayofMonth(),
loanApplicationTerms.getNumberOfdays());
}
break;
case YEARS:
idealDisbursementDate = firstRepaymentDate.minusYears(repaidEvery);
break;
case INVALID:
break;
case WHOLE_TERM:
log.error("TODO Implement repaymentPeriodFrequencyType for WHOLE_TERM");
break;
}
return idealDisbursementDate;
}
@Override
public LocalDate generateNextScheduleDateStartingFromDisburseDate(LocalDate lastRepaymentDate,
LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
LocalDate generatedDate = loanApplicationTerms.getExpectedDisbursementDate();
boolean isFirstRepayment = true;
while (!DateUtils.isAfter(generatedDate, lastRepaymentDate)) {
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
isFirstRepayment = false;
}
generatedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
return generatedDate;
}
@Override
public LocalDate generateNextScheduleDateStartingFromDisburseDateOrRescheduleDate(LocalDate lastRepaymentDate,
LoanApplicationTerms loanApplicationTerms, final HolidayDetailDTO holidayDetailDTO) {
LocalDate generatedDate = loanApplicationTerms.getExpectedDisbursementDate();
boolean isFirstRepayment = true;
if (loanApplicationTerms.getNewScheduledDueDateStart() != null) {
generatedDate = loanApplicationTerms.getNewScheduledDueDateStart();
isFirstRepayment = false;
}
LocalDate adjustedDate = generatedDate;
while (!DateUtils.isAfter(adjustedDate, lastRepaymentDate)) {
generatedDate = generateNextRepaymentDate(generatedDate, loanApplicationTerms, isFirstRepayment);
adjustedDate = adjustRepaymentDate(generatedDate, loanApplicationTerms, holidayDetailDTO).getChangedScheduleDate();
isFirstRepayment = false;
}
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;
}
}
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -88,6 +89,7 @@ public class CheckLoanRepaymentOverdueBusinessStepTest {
@BeforeAll
public static void init() {
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
}
@@ -29,6 +29,7 @@ import static org.mockito.Mockito.when;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -116,8 +117,12 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
@Mock
private AvroDateTimeMapper mapper;
private MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
@BeforeEach
public void setUp() {
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
ThreadLocalContextUtil
@@ -127,6 +132,7 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
@AfterEach
public void tearDown() {
ThreadLocalContextUtil.reset();
moneyHelper.close();
}
@Test
@@ -141,7 +147,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
LoanAccountData loanAccountData = mock(LoanAccountData.class);
CollectionData delinquentData = mock(CollectionData.class);
MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1);
MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
String delinquentDateAsStr = "2022-12-01";
LocalDate delinquentDate = LocalDate.parse(delinquentDateAsStr);
@@ -168,8 +173,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
when(loanChargeReadPlatformService.retrieveLoanCharges(anyLong())).thenAnswer(a -> repaymentScheduleInstallments.get(0)
.getInstallmentCharges().stream().map(c -> c.getLoanCharge().toData()).collect(Collectors.toList()));
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
// when
LoanAccountDelinquencyRangeDataV1 data = (LoanAccountDelinquencyRangeDataV1) serializer.toAvroDTO(event);
@@ -190,8 +193,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
assertEquals(0, data.getAmount().getFeeAmount().compareTo(new BigDecimal("5.0")));
assertEquals(0, data.getAmount().getPenaltyAmount().compareTo(new BigDecimal("50.0")));
assertEquals(delinquentDateAsStr, data.getDelinquentDate());
moneyHelper.close();
}
@Test
@@ -206,7 +207,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
LoanAccountData loanAccountData = mock(LoanAccountData.class);
CollectionData delinquentData = mock(CollectionData.class);
MonetaryCurrency loanCurrency = new MonetaryCurrency("CODE", 1, 1);
MockedStatic<MoneyHelper> moneyHelper = Mockito.mockStatic(MoneyHelper.class);
String delinquentDateAsStr = "2022-12-01";
LocalDate delinquentDate = LocalDate.parse(delinquentDateAsStr);
when(loanForProcessing.getId()).thenReturn(1L);
@@ -263,8 +263,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
when(loanForProcessing.getLoanCharges()).thenAnswer(a -> repaymentScheduleInstallments.get(0).getInstallmentCharges().stream()
.map(c -> c.getLoanCharge()).collect(Collectors.toList()));
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
// when
LoanAccountDelinquencyRangeDataV1 data = (LoanAccountDelinquencyRangeDataV1) serializer.toAvroDTO(event);
@@ -314,7 +312,6 @@ public class LoanAccountDelinquencyRangeEventSerializerTest {
assertEquals(0, installmentDelinquencyBucketDataV1_2.getAmount().getFeeAmount().compareTo(new BigDecimal("0.0")));
assertEquals(0, installmentDelinquencyBucketDataV1_2.getAmount().getPenaltyAmount().compareTo(new BigDecimal("0.0")));
assertEquals(0, installmentDelinquencyBucketDataV1_2.getCharges().size());
moneyHelper.close();
}
@Test
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -85,7 +86,8 @@ public class LoanRepaymentBusinessEventSerializerTest {
ThreadLocalContextUtil.setActionContext(ActionContext.DEFAULT);
ThreadLocalContextUtil
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
}
@AfterEach
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -104,7 +105,8 @@ public class LoanDelinquencyDomainServiceTest {
currency = new MonetaryCurrency("USD", 2, null);
moneyHelperStatic = Mockito.mockStatic(MoneyHelper.class);
moneyHelperStatic.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
moneyHelperStatic.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
moneyHelperStatic.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
}
@AfterEach
@@ -25,6 +25,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanStatusChangedBusinessEvent;
@@ -55,7 +56,8 @@ class DefaultLoanLifecycleStateMachineTest {
public void setUp() {
moneyHelperStatic = Mockito.mockStatic(MoneyHelper.class);
moneyHelperStatic.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
moneyHelperStatic.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
moneyHelperStatic.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
underTest = new DefaultLoanLifecycleStateMachine(businessEventNotifierService);
}
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.refEq;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.HashMap;
@@ -87,6 +88,7 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
@BeforeAll
public static void init() {
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
}
@@ -22,6 +22,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.refEq;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.HashMap;
@@ -88,6 +89,7 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
@BeforeAll
public static void init() {
MONEY_HELPER.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.HALF_EVEN));
MONEY_HELPER.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.HALF_EVEN);
}
@@ -37,6 +37,8 @@ import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
@@ -82,6 +84,7 @@ public class DefaultScheduledDateGeneratorTest {
public void test_generateRepaymentPeriods() {
// given
HolidayDetailDTO holidayDetailDTO = createHolidayDTO();
MathContext mathContext = new MathContext(12, RoundingMode.HALF_EVEN);
ApplicationCurrency dollarCurrency = new ApplicationCurrency("USD", "US Dollar", 2, 0, "currency.USD", "$");
Money principalAmount = Money.of(fromApplicationCurrency(dollarCurrency), BigDecimal.valueOf(100));
@@ -98,8 +101,8 @@ public class DefaultScheduledDateGeneratorTest {
submittedOnDate, CUMULATIVE, LoanScheduleProcessingType.HORIZONTAL, null, false, null);
// when
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(expectedDisbursementDate, loanApplicationTerms,
holidayDetailDTO);
List<? extends LoanScheduleModelPeriod> result = underTest.generateRepaymentPeriods(mathContext, expectedDisbursementDate,
loanApplicationTerms, holidayDetailDTO);
// then
assertThat(result.size()).isEqualTo(4);
@@ -22,6 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import java.math.BigDecimal;
import java.math.MathContext;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.ZoneId;
@@ -64,7 +65,8 @@ public class LoanCalculateRepaymentPastDueServiceTest {
ThreadLocalContextUtil
.setBusinessDates(new HashMap<>(Map.of(BusinessDateType.BUSINESS_DATE, LocalDate.now(ZoneId.systemDefault()))));
underTest = new LoanCalculateRepaymentPastDueService();
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
}
@AfterEach
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import java.math.MathContext;
import java.math.RoundingMode;
import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanBalanceChangedBusinessEvent;
@@ -75,7 +76,8 @@ public class LoanDownPaymentHandlerServiceImplTest {
@BeforeEach
public void setUp() {
underTest = new LoanDownPaymentHandlerServiceImpl(loanTransactionRepository, businessEventNotifierService);
moneyHelper.when(() -> MoneyHelper.getRoundingMode()).thenReturn(RoundingMode.UP);
moneyHelper.when(MoneyHelper::getMathContext).thenReturn(new MathContext(12, RoundingMode.UP));
moneyHelper.when(MoneyHelper::getRoundingMode).thenReturn(RoundingMode.UP);
}
@AfterEach