FINERACT-1971: Fix for infinite recursive loop in CalendarUtils when tenant and system TZ is different

This commit is contained in:
Arnold Galovics
2023-11-08 14:35:25 +01:00
committed by Arnold Gálovics
parent 9efe4f7ed5
commit f681ee9ca5
16 changed files with 593 additions and 25 deletions
@@ -61,7 +61,7 @@ public class ApplicationCurrency extends AbstractPersistableCustom {
currency.displaySymbol);
}
private ApplicationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMultiplesOf,
public ApplicationCurrency(final String code, final String name, final int decimalPlaces, final Integer inMultiplesOf,
final String nameCode, final String displaySymbol) {
this.code = code;
this.name = name;
@@ -38,11 +38,11 @@ public class MoneyHelper {
@Autowired
private ConfigurationDomainService configurationDomainService;
@PostConstruct
// This is a hack, but fixing this is not trivial, because some @Entity
// domain classes use this helper
@PostConstruct
@SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
public void someFunction() {
public void initialize() {
staticConfigurationDomainService = configurationDomainService;
}
@@ -25,11 +25,12 @@ import lombok.Getter;
@Getter
public enum RepaymentRescheduleType {
INVALID(0, "RepaymentRescheduleType.invalid"), SAME_DAY(1, "RepaymentRescheduleType.same.day"), MOVE_TO_NEXT_WORKING_DAY(2,
"RepaymentRescheduleType.move.to.next.working.day"), MOVE_TO_NEXT_REPAYMENT_MEETING_DAY(3,
"RepaymentRescheduleType.move.to.next.repayment.meeting.day"), MOVE_TO_PREVIOUS_WORKING_DAY(4,
"RepaymentRescheduleType.move.to.previous.working.day"), MOVE_TO_NEXT_MEETING_DAY(5,
"RepaymentRescheduleType.move.to.next.meeting.day");
INVALID(0, "RepaymentRescheduleType.invalid"), //
SAME_DAY(1, "RepaymentRescheduleType.same.day"), //
MOVE_TO_NEXT_WORKING_DAY(2, "RepaymentRescheduleType.move.to.next.working.day"), //
MOVE_TO_NEXT_REPAYMENT_MEETING_DAY(3, "RepaymentRescheduleType.move.to.next.repayment.meeting.day"), //
MOVE_TO_PREVIOUS_WORKING_DAY(4, "RepaymentRescheduleType.move.to.previous.working.day"), //
MOVE_TO_NEXT_MEETING_DAY(5, "RepaymentRescheduleType.move.to.next.meeting.day"); //
private final Integer value;
private final String code;
@@ -51,7 +51,7 @@ public class WorkingDays extends AbstractPersistableCustom {
}
protected WorkingDays(final String recurrence, final Integer repaymentReschedulingType, final Boolean extendTermForDailyRepayments,
public WorkingDays(final String recurrence, final Integer repaymentReschedulingType, final Boolean extendTermForDailyRepayments,
final Boolean extendTermForRepaymentsOnHolidays) {
this.recurrence = recurrence;
this.repaymentReschedulingType = repaymentReschedulingType;
@@ -33,7 +33,7 @@ public final class WorkingDaysUtil {
public static LocalDate getOffSetDateIfNonWorkingDay(final LocalDate date, final LocalDate nextMeetingDate,
final WorkingDays workingDays) {
// If date is not a non working day then return date.
// If date is a working day then return date.
if (isWorkingDay(workingDays, date)) {
return date;
}
@@ -32,6 +32,7 @@ import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import lombok.extern.slf4j.Slf4j;
import net.fortuna.ical4j.model.Date;
import net.fortuna.ical4j.model.DateList;
import net.fortuna.ical4j.model.DateTime;
@@ -53,19 +54,18 @@ import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType;
import org.apache.fineract.portfolio.calendar.domain.CalendarWeekDaysType;
import org.apache.fineract.portfolio.common.domain.NthDayType;
import org.apache.fineract.portfolio.common.domain.PeriodFrequencyType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Slf4j
public final class CalendarUtils {
public static final String FLOATING_TIMEZONE_PROPERTY_KEY = "net.fortuna.ical4j.timezone.date.floating";
private CalendarUtils() {
}
private static final Logger LOG = LoggerFactory.getLogger(CalendarUtils.class);
static {
System.setProperty("net.fortuna.ical4j.timezone.date.floating", "true");
System.setProperty(FLOATING_TIMEZONE_PROPERTY_KEY, "true");
}
public static LocalDateTime getNextRecurringDate(final String recurringRule, final LocalDateTime seedDate,
@@ -119,7 +119,7 @@ public final class CalendarUtils {
try {
formattedDate = new Date(seedDateStr, DateUtils.DEFAULT_DATETIME_FORMAT);
} catch (final ParseException e) {
LOG.error("Invalid date: {}", seedDateStr, e);
log.error("Invalid date: {}", seedDateStr, e);
}
return formattedDate;
}
@@ -170,15 +170,15 @@ public final class CalendarUtils {
numberOfDays);
}
private static Collection<LocalDate> convertToLocalDateList(final DateList dates, final LocalDate seedDate,
static Collection<LocalDate> convertToLocalDateList(final DateList dates, final LocalDate seedDate,
final PeriodFrequencyType frequencyType, boolean isSkippMeetingOnFirstDay, final Integer numberOfDays) {
final Collection<LocalDate> recurringDates = new ArrayList<>();
for (@SuppressWarnings("rawtypes")
final Iterator iterator = dates.iterator(); iterator.hasNext();) {
final Date date = (Date) iterator.next();
recurringDates.add((LocalDate) adjustDate(LocalDate.ofInstant(date.toInstant(), DateUtils.getDateTimeZoneOfTenant()), seedDate,
frequencyType));
for (final Date date : dates) {
LocalDateTime dateTimeInProperTz = getLocalDateTimeFromICal4JDate(date);
ZoneId tenantZoneId = DateUtils.getDateTimeZoneOfTenant();
recurringDates.add((LocalDate) adjustDate(dateTimeInProperTz.atZone(tenantZoneId).toLocalDate(), seedDate, frequencyType));
}
if (isSkippMeetingOnFirstDay) {
@@ -188,6 +188,12 @@ public final class CalendarUtils {
return recurringDates;
}
private static LocalDateTime getLocalDateTimeFromICal4JDate(Date date) {
java.util.Calendar cal = java.util.Calendar.getInstance();
cal.setTime(date);
return LocalDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId());
}
private static Collection<LocalDate> skipMeetingOnFirstdayOfMonth(final Collection<LocalDate> recurringDates,
final Integer numberOfDays) {
final Collection<LocalDate> adjustedRecurringDates = new ArrayList<>();
@@ -220,10 +226,10 @@ public final class CalendarUtils {
return recur;
} catch (final ParseException e) {
// TODO Auto-generated catch block
LOG.error("Problem occurred in getICalRecur function", e);
log.error("Problem occurred in getICalRecur function", e);
} catch (final ValidationException e) {
// TODO Auto-generated catch block
LOG.error("Problem occurred in getICalRecur function", e);
log.error("Problem occurred in getICalRecur function", e);
}
return null;