FINERACT-1983: Date handling improvements - general

This commit is contained in:
jmarta
2023-10-25 22:41:23 +02:00
committed by Adam Saghy
parent 71afd6dead
commit e602fa6b3c
147 changed files with 797 additions and 1028 deletions
@@ -66,15 +66,14 @@ public class GLClosureWritePlatformServiceJpaRepositoryImpl implements GLClosure
final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId); final Office office = this.officeRepositoryWrapper.findOneWithNotFoundDetection(officeId);
// TODO: Get Tenant specific date // TODO: Get Tenant specific date
// ensure closure date is not in the future // ensure closure date is not in the future
final LocalDate todaysDate = DateUtils.getBusinessLocalDate();
final LocalDate closureDate = command.localDateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue()); final LocalDate closureDate = command.localDateValueOfParameterNamed(GLClosureJsonInputParams.CLOSING_DATE.getValue());
if (closureDate.isAfter(todaysDate)) { if (DateUtils.isDateInTheFuture(closureDate)) {
throw new GLClosureInvalidException(GlClosureInvalidReason.FUTURE_DATE, closureDate); throw new GLClosureInvalidException(GlClosureInvalidReason.FUTURE_DATE, closureDate);
} }
// shouldn't be before an existing accounting closure // shouldn't be before an existing accounting closure
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(officeId); final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(officeId);
if (latestGLClosure != null) { if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().isAfter(closureDate)) { if (DateUtils.isAfter(latestGLClosure.getClosingDate(), closureDate)) {
throw new GLClosureInvalidException(GlClosureInvalidReason.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate()); throw new GLClosureInvalidException(GlClosureInvalidReason.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate());
} }
} }
@@ -122,7 +121,7 @@ public class GLClosureWritePlatformServiceJpaRepositoryImpl implements GLClosure
**/ **/
final LocalDate closureDate = glClosure.getClosingDate(); final LocalDate closureDate = glClosure.getClosingDate();
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(glClosure.getOffice().getId()); final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(glClosure.getOffice().getId());
if (latestGLClosure.getClosingDate().isAfter(closureDate)) { if (DateUtils.isAfter(latestGLClosure.getClosingDate(), closureDate)) {
throw new GLClosureInvalidDeleteException(latestGLClosure.getOffice().getId(), latestGLClosure.getOffice().getName(), throw new GLClosureInvalidDeleteException(latestGLClosure.getOffice().getId(), latestGLClosure.getOffice().getName(),
latestGLClosure.getClosingDate()); latestGLClosure.getClosingDate());
} }
@@ -30,6 +30,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@Entity @Entity
@Table(name = "m_trial_balance") @Table(name = "m_trial_balance")
@@ -70,9 +71,8 @@ public class TrialBalance extends AbstractPersistableCustom {
} }
TrialBalance other = (TrialBalance) obj; TrialBalance other = (TrialBalance) obj;
return Objects.equals(other.officeId, officeId) && Objects.equals(other.glAccountId, glAccountId) return Objects.equals(other.officeId, officeId) && Objects.equals(other.glAccountId, glAccountId)
&& Objects.equals(other.amount, amount) && other.entryDate.compareTo(entryDate) == 0 ? Boolean.TRUE && Objects.equals(other.amount, amount) && DateUtils.isEqual(other.entryDate, entryDate)
: Boolean.FALSE && other.transactionDate.compareTo(transactionDate) == 0 ? Boolean.TRUE && DateUtils.isEqual(other.transactionDate, transactionDate) && Objects.equals(other.closingBalance, closingBalance);
: Boolean.FALSE && Objects.equals(other.closingBalance, closingBalance);
} }
@Override @Override
@@ -133,7 +133,7 @@ public class CommandSource extends AbstractPersistableCustom {
this.subResourceId = subResourceId; this.subResourceId = subResourceId;
this.commandAsJson = commandSerializedAsJson; this.commandAsJson = commandSerializedAsJson;
this.maker = maker; this.maker = maker;
this.madeOnDate = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(); this.madeOnDate = DateUtils.getAuditOffsetDateTime();
this.status = status; this.status = status;
this.idempotencyKey = idempotencyKey; this.idempotencyKey = idempotencyKey;
} }
@@ -314,13 +314,13 @@ public class CommandSource extends AbstractPersistableCustom {
public void markAsChecked(final AppUser checker) { public void markAsChecked(final AppUser checker) {
this.checker = checker; this.checker = checker;
this.checkedOnDate = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(); this.checkedOnDate = DateUtils.getAuditOffsetDateTime();
this.status = CommandProcessingResultType.PROCESSED.getValue(); this.status = CommandProcessingResultType.PROCESSED.getValue();
} }
public void markAsRejected(final AppUser checker) { public void markAsRejected(final AppUser checker) {
this.checker = checker; this.checker = checker;
this.checkedOnDate = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(); this.checkedOnDate = DateUtils.getAuditOffsetDateTime();
this.status = CommandProcessingResultType.REJECTED.getValue(); this.status = CommandProcessingResultType.REJECTED.getValue();
} }
@@ -43,7 +43,7 @@ public class PurgeProcessedCommandsTasklet implements Tasklet {
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) {
try { try {
Long numberOfDaysForPurgeCriteria = configurationDomainService.retrieveProcessedCommandsPurgeDaysCriteria(); Long numberOfDaysForPurgeCriteria = configurationDomainService.retrieveProcessedCommandsPurgeDaysCriteria();
OffsetDateTime dateForPurgeCriteria = DateUtils.getOffsetDateTimeOfTenant().minusDays(numberOfDaysForPurgeCriteria); OffsetDateTime dateForPurgeCriteria = DateUtils.getAuditOffsetDateTime().minusDays(numberOfDaysForPurgeCriteria);
repository.deleteOlderEventsWithStatus(CommandProcessingResultType.PROCESSED.getValue(), dateForPurgeCriteria); repository.deleteOlderEventsWithStatus(CommandProcessingResultType.PROCESSED.getValue(), dateForPurgeCriteria);
} catch (Exception e) { } catch (Exception e) {
log.error("Error occurred while purging processed commands: ", e); log.error("Error occurred while purging processed commands: ", e);
@@ -19,7 +19,6 @@
package org.apache.fineract.infrastructure.businessdate.service; package org.apache.fineract.infrastructure.businessdate.service;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@@ -70,8 +69,7 @@ public class BusinessDateReadPlatformServiceImpl implements BusinessDateReadPlat
@Override @Override
public HashMap<BusinessDateType, LocalDate> getBusinessDates() { public HashMap<BusinessDateType, LocalDate> getBusinessDates() {
HashMap<BusinessDateType, LocalDate> businessDateMap = new HashMap<>(); HashMap<BusinessDateType, LocalDate> businessDateMap = new HashMap<>();
ZoneId zone = DateUtils.getDateTimeZoneOfTenant(); LocalDate tenantDate = DateUtils.getLocalDateOfTenant();
LocalDate tenantDate = LocalDate.now(zone);
businessDateMap.put(BusinessDateType.BUSINESS_DATE, tenantDate); businessDateMap.put(BusinessDateType.BUSINESS_DATE, tenantDate);
businessDateMap.put(BusinessDateType.COB_DATE, tenantDate); businessDateMap.put(BusinessDateType.COB_DATE, tenantDate);
if (configurationDomainService.isBusinessDateEnabled()) { if (configurationDomainService.isBusinessDateEnabled()) {
@@ -80,7 +78,6 @@ public class BusinessDateReadPlatformServiceImpl implements BusinessDateReadPlat
businessDateMap.put(BusinessDateType.valueOf(businessDateData.getType()), businessDateData.getDate()); businessDateMap.put(BusinessDateType.valueOf(businessDateData.getType()), businessDateData.getDate());
} }
} }
return businessDateMap; return businessDateMap;
} }
} }
@@ -132,15 +132,11 @@ public class BusinessDateWritePlatformServiceImpl implements BusinessDateWritePl
private void updateBusinessDate(BusinessDate businessDate, LocalDate newDate, Map<String, Object> changes) { private void updateBusinessDate(BusinessDate businessDate, LocalDate newDate, Map<String, Object> changes) {
LocalDate oldDate = businessDate.getDate(); LocalDate oldDate = businessDate.getDate();
if (!hasChange(oldDate, newDate)) { if (DateUtils.isEqual(oldDate, newDate)) {
return; return;
} }
businessDate.setDate(newDate); businessDate.setDate(newDate);
repository.save(businessDate); repository.save(businessDate);
changes.put(businessDate.getType().name(), newDate); changes.put(businessDate.getType().name(), newDate);
} }
private boolean hasChange(@NotNull LocalDate oldDate, @NotNull LocalDate date) {
return (date.isBefore(oldDate) || date.isAfter(oldDate));
}
} }
@@ -32,6 +32,7 @@ import net.fortuna.ical4j.model.property.RRule;
import net.fortuna.ical4j.validate.ValidationException; import net.fortuna.ical4j.validate.ValidationException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.quartz.CronExpression; import org.quartz.CronExpression;
import org.springframework.util.ObjectUtils; import org.springframework.util.ObjectUtils;
@@ -1045,7 +1046,7 @@ public class DataValidatorBuilder {
if (this.value != null && date != null) { if (this.value != null && date != null) {
final LocalDate dateVal = (LocalDate) this.value; final LocalDate dateVal = (LocalDate) this.value;
if (date.isAfter(dateVal)) { if (DateUtils.isAfter(date, dateVal)) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".is.less.than.date"); .append(this.parameter).append(".is.less.than.date");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter)
@@ -1065,7 +1066,7 @@ public class DataValidatorBuilder {
if (this.value != null && date != null) { if (this.value != null && date != null) {
final LocalDate dateVal = (LocalDate) this.value; final LocalDate dateVal = (LocalDate) this.value;
if (date.isBefore(dateVal)) { if (DateUtils.isBefore(date, dateVal)) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".is.greater.than.date"); .append(this.parameter).append(".is.greater.than.date");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter)
@@ -1085,7 +1086,7 @@ public class DataValidatorBuilder {
if (this.value != null && date != null) { if (this.value != null && date != null) {
final LocalDate dateVal = (LocalDate) this.value; final LocalDate dateVal = (LocalDate) this.value;
if (dateVal.isAfter(date)) { if (DateUtils.isAfter(dateVal, date)) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".is.greater.than.date"); .append(this.parameter).append(".is.greater.than.date");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter)
@@ -1105,7 +1106,7 @@ public class DataValidatorBuilder {
if (this.value != null && date != null) { if (this.value != null && date != null) {
final LocalDate dateVal = (LocalDate) this.value; final LocalDate dateVal = (LocalDate) this.value;
if (!dateVal.isEqual(date)) { if (!DateUtils.isEqual(dateVal, date)) {
final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".") final StringBuilder validationErrorCode = new StringBuilder("validation.msg.").append(this.resource).append(".")
.append(this.parameter).append(".is.not.equal.to.date"); .append(this.parameter).append(".is.not.equal.to.date");
final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter) final StringBuilder defaultEnglishMessage = new StringBuilder("The parameter `").append(this.parameter)
@@ -22,6 +22,7 @@ import java.time.LocalDate;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle; import org.apache.commons.lang3.builder.ToStringStyle;
import org.apache.fineract.infrastructure.core.service.DateUtils;
public class LocalDateInterval { public class LocalDateInterval {
@@ -66,11 +67,11 @@ public class LocalDateInterval {
} }
private boolean isBetweenInclusive(final LocalDate start, final LocalDate end, final LocalDate target) { private boolean isBetweenInclusive(final LocalDate start, final LocalDate end, final LocalDate target) {
return !target.isBefore(start) && !target.isAfter(end); return !DateUtils.isBefore(target, start) && !DateUtils.isAfter(target, end);
} }
public boolean fallsBefore(final LocalDate dateToCheck) { public boolean fallsBefore(final LocalDate dateToCheck) {
return this.endDate.isBefore(dateToCheck); return DateUtils.isBefore(this.endDate, dateToCheck);
} }
@Override @Override
@@ -70,10 +70,6 @@ public final class DateUtils {
return truncate == null ? now : now.truncatedTo(truncate); return truncate == null ? now : now.truncatedTo(truncate);
} }
public static OffsetDateTime getOffsetDateTimeOfTenantWithMostPrecision() {
return getOffsetDateTimeOfTenant(null);
}
public static OffsetDateTime getOffsetDateTimeOfTenant() { public static OffsetDateTime getOffsetDateTimeOfTenant() {
return getOffsetDateTimeOfTenant(null); return getOffsetDateTimeOfTenant(null);
} }
@@ -20,6 +20,7 @@ package org.apache.fineract.infrastructure.core.service.database;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import java.io.Serializable;
import java.sql.JDBCType; import java.sql.JDBCType;
import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException; import org.apache.fineract.infrastructure.core.exception.PlatformServiceUnavailableException;
@@ -272,7 +273,7 @@ public enum JdbcJavaType {
} }
@com.google.errorprone.annotations.Immutable @com.google.errorprone.annotations.Immutable
private static final class DialectType { private static final class DialectType implements Serializable {
@NotNull @NotNull
private final JDBCType jdbcType; private final JDBCType jdbcType;
@@ -99,7 +99,7 @@ public class SendAsynchronousEventsTasklet implements Tasklet {
} }
private void markEventsAsSent(List<Long> eventIds) { private void markEventsAsSent(List<Long> eventIds) {
OffsetDateTime sentAt = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(); OffsetDateTime sentAt = DateUtils.getAuditOffsetDateTime();
// Partitioning dataset to avoid exception: PreparedStatement can have at most 65,535 parameters // Partitioning dataset to avoid exception: PreparedStatement can have at most 65,535 parameters
List<List<Long>> partitions = Lists.partition(eventIds, 5_000); List<List<Long>> partitions = Lists.partition(eventIds, 5_000);
@@ -80,7 +80,7 @@ public class ExternalEvent extends AbstractPersistableCustom {
this.data = data; this.data = data;
this.idempotencyKey = idempotencyKey; this.idempotencyKey = idempotencyKey;
this.aggregateRootId = aggregateRootId; this.aggregateRootId = aggregateRootId;
this.createdAt = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(); this.createdAt = DateUtils.getAuditOffsetDateTime();
this.status = ExternalEventStatus.TO_BE_SENT; this.status = ExternalEventStatus.TO_BE_SENT;
this.businessDate = DateUtils.getBusinessLocalDate(); this.businessDate = DateUtils.getBusinessLocalDate();
} }
@@ -20,6 +20,7 @@ package org.apache.fineract.organisation.holiday.service;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.holiday.domain.Holiday; import org.apache.fineract.organisation.holiday.domain.Holiday;
import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO; import org.apache.fineract.organisation.workingdays.data.AdjustedDateDetailsDTO;
@@ -30,47 +31,29 @@ public final class HolidayUtil {
} }
public static LocalDate getRepaymentRescheduleDateToIfHoliday(LocalDate repaymentDate, final List<Holiday> holidays) { public static LocalDate getRepaymentRescheduleDateToIfHoliday(LocalDate repaymentDate, final List<Holiday> holidays) {
for (final Holiday holiday : holidays) { for (final Holiday holiday : holidays) {
if (repaymentDate.equals(holiday.getFromDate()) || repaymentDate.equals(holiday.getToDate()) if (isHoliday(repaymentDate, holiday)) {
|| (repaymentDate.isAfter(holiday.getFromDate()) && repaymentDate.isBefore(holiday.getToDate()))) { repaymentDate = holiday.getRepaymentsRescheduledTo();
repaymentDate = getRepaymentRescheduleDateIfHoliday(repaymentDate, holidays);
}
}
return repaymentDate;
}
private static LocalDate getRepaymentRescheduleDateIfHoliday(final LocalDate repaymentDate, final List<Holiday> holidays) {
for (final Holiday holiday : holidays) {
if (repaymentDate.equals(holiday.getFromDate()) || repaymentDate.equals(holiday.getToDate())
|| (repaymentDate.isAfter(holiday.getFromDate()) && repaymentDate.isBefore(holiday.getToDate()))) {
// should be take from holiday
return holiday.getRepaymentsRescheduledTo();
} }
} }
return repaymentDate; return repaymentDate;
} }
public static boolean isHoliday(final LocalDate date, final List<Holiday> holidays) { public static boolean isHoliday(final LocalDate date, final List<Holiday> holidays) {
for (final Holiday holiday : holidays) { return getApplicableHoliday(date, holidays) != null;
if (date.isEqual(holiday.getFromDate()) || date.isEqual(holiday.getToDate())
|| (date.isAfter(holiday.getFromDate()) && date.isBefore(holiday.getToDate()))) {
return true;
}
}
return false;
} }
public static Holiday getApplicableHoliday(final LocalDate repaymentDate, final List<Holiday> holidays) { public static Holiday getApplicableHoliday(final LocalDate repaymentDate, final List<Holiday> holidays) {
Holiday referedHoliday = null;
for (final Holiday holiday : holidays) { for (final Holiday holiday : holidays) {
if (!repaymentDate.isBefore(holiday.getFromDate()) && !repaymentDate.isAfter(holiday.getToDate())) { if (isHoliday(repaymentDate, holiday)) {
referedHoliday = holiday; return holiday;
} }
} }
return referedHoliday; return null;
}
public static boolean isHoliday(LocalDate date, Holiday holiday) {
return !DateUtils.isBefore(date, holiday.getFromDate()) && !DateUtils.isAfter(date, holiday.getToDate());
} }
public static void updateRepaymentRescheduleDateToWorkingDayIfItIsHoliday(final AdjustedDateDetailsDTO adjustedDateDetailsDTO, public static void updateRepaymentRescheduleDateToWorkingDayIfItIsHoliday(final AdjustedDateDetailsDTO adjustedDateDetailsDTO,
@@ -39,6 +39,7 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.organisation.office.exception.CannotUpdateOfficeWithParentOfficeSameAsSelf; import org.apache.fineract.organisation.office.exception.CannotUpdateOfficeWithParentOfficeSameAsSelf;
import org.apache.fineract.organisation.office.exception.RootOfficeParentCannotBeUpdated; import org.apache.fineract.organisation.office.exception.RootOfficeParentCannotBeUpdated;
@@ -155,11 +156,11 @@ public class Office extends AbstractPersistableCustom implements Serializable {
} }
public boolean isOpeningDateBefore(final LocalDate baseDate) { public boolean isOpeningDateBefore(final LocalDate baseDate) {
return getOpeningLocalDate().isBefore(baseDate); return DateUtils.isBefore(getOpeningLocalDate(), baseDate);
} }
public boolean isOpeningDateAfter(final LocalDate activationLocalDate) { public boolean isOpeningDateAfter(final LocalDate baseDate) {
return getOpeningLocalDate().isAfter(activationLocalDate); return DateUtils.isAfter(getOpeningLocalDate(), baseDate);
} }
public LocalDate getOpeningLocalDate() { public LocalDate getOpeningLocalDate() {
@@ -28,6 +28,7 @@ import lombok.Data;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType; import org.apache.fineract.portfolio.calendar.domain.CalendarFrequencyType;
import org.apache.fineract.portfolio.calendar.domain.CalendarRemindBy; import org.apache.fineract.portfolio.calendar.domain.CalendarRemindBy;
import org.apache.fineract.portfolio.calendar.domain.CalendarType; import org.apache.fineract.portfolio.calendar.domain.CalendarType;
@@ -377,12 +378,11 @@ public final class CalendarData implements Serializable {
} }
public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) { public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) {
return this.startDate != null && compareDate != null return this.startDate != null && compareDate != null && !DateUtils.isAfter(this.startDate, compareDate);
&& (this.startDate.isBefore(compareDate) || this.startDate.equals(compareDate));
} }
public boolean isEndDateAfterOrEqual(final LocalDate compareDate) { public boolean isEndDateAfterOrEqual(final LocalDate compareDate) {
return this.endDate != null && compareDate != null && (this.endDate.isAfter(compareDate) || this.endDate.isEqual(compareDate)); return this.endDate != null && compareDate != null && !DateUtils.isBefore(this.endDate, compareDate);
} }
public boolean isBetweenStartAndEndDate(final LocalDate compareDate) { public boolean isBetweenStartAndEndDate(final LocalDate compareDate) {
@@ -20,10 +20,10 @@ package org.apache.fineract.portfolio.calendar.data;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.calendar.domain.CalendarHistory; import org.apache.fineract.portfolio.calendar.domain.CalendarHistory;
public class CalendarHistoryDataWrapper { public class CalendarHistoryDataWrapper {
@@ -37,16 +37,16 @@ public class CalendarHistoryDataWrapper {
@Override @Override
public int compare(CalendarHistory calendarHistory1, CalendarHistory calendarHistory2) { public int compare(CalendarHistory calendarHistory1, CalendarHistory calendarHistory2) {
return calendarHistory1.getEndDateLocalDate().compareTo(calendarHistory2.getEndDateLocalDate()); return DateUtils.compare(calendarHistory1.getEndDate(), calendarHistory2.getEndDate());
} }
}; };
Collections.sort(this.calendarHistoryList, orderByDate); this.calendarHistoryList.sort(orderByDate);
} }
public CalendarHistory getCalendarHistory(final LocalDate dueRepaymentPeriodDate) { public CalendarHistory getCalendarHistory(final LocalDate dueRepaymentPeriodDate) {
CalendarHistory calendarHistory = null; CalendarHistory calendarHistory = null;
for (CalendarHistory history : this.calendarHistoryList) { for (CalendarHistory history : this.calendarHistoryList) {
if (history.getEndDateLocalDate().isAfter(dueRepaymentPeriodDate)) { if (DateUtils.isAfter(history.getEndDate(), dueRepaymentPeriodDate)) {
calendarHistory = history; calendarHistory = history;
break; break;
} }
@@ -175,23 +175,19 @@ public class Calendar extends AbstractAuditableWithUTCDateTimeCustom {
} }
public Map<String, Object> updateStartDateAndDerivedFeilds(final LocalDate newMeetingStartDate) { public Map<String, Object> updateStartDateAndDerivedFeilds(final LocalDate newMeetingStartDate) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(9); final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); final LocalDate currentDate = DateUtils.getLocalDateOfTenant();
if (DateUtils.isBefore(newMeetingStartDate, currentDate)) {
if (newMeetingStartDate.isBefore(currentDate)) {
final String defaultUserMessage = "New meeting effective from date cannot be in past"; final String defaultUserMessage = "New meeting effective from date cannot be in past";
throw new CalendarDateException("new.start.date.cannot.be.in.past", defaultUserMessage, newMeetingStartDate, throw new CalendarDateException("new.start.date.cannot.be.in.past", defaultUserMessage, newMeetingStartDate,
getStartDateLocalDate()); getStartDateLocalDate());
} else if (isStartDateAfter(newMeetingStartDate) && isStartDateBeforeOrEqual(currentDate)) { } else if (isStartDateAfter(newMeetingStartDate) && isStartDateBeforeOrEqual(currentDate)) {
// new meeting date should be on or after start date or current // new meeting date should be on or after start date or current date
// date
final String defaultUserMessage = "New meeting effective from date cannot be a date before existing meeting start date"; final String defaultUserMessage = "New meeting effective from date cannot be a date before existing meeting start date";
throw new CalendarDateException("new.start.date.before.existing.date", defaultUserMessage, newMeetingStartDate, throw new CalendarDateException("new.start.date.before.existing.date", defaultUserMessage, newMeetingStartDate,
getStartDateLocalDate()); getStartDateLocalDate());
} else { } else {
actualChanges.put(CalendarSupportedParameters.START_DATE.getValue(), newMeetingStartDate.toString()); actualChanges.put(CalendarSupportedParameters.START_DATE.getValue(), newMeetingStartDate.toString());
this.startDate = newMeetingStartDate; this.startDate = newMeetingStartDate;
@@ -200,33 +196,25 @@ public class Calendar extends AbstractAuditableWithUTCDateTimeCustom {
* recurring day and update it if it is changed. For weekly type is weekday and for monthly type it is day * recurring day and update it if it is changed. For weekly type is weekday and for monthly type it is day
* of the month * of the month
*/ */
CalendarFrequencyType calendarFrequencyType = CalendarUtils.getFrequency(this.recurrence); CalendarFrequencyType calendarFrequencyType = CalendarUtils.getFrequency(this.recurrence);
Integer interval = CalendarUtils.getInterval(this.recurrence); Integer interval = CalendarUtils.getInterval(this.recurrence);
Integer repeatsOnDay = null; Integer repeatsOnDay = null;
/* // Repeats on day, need to derive based on the start date
* Repeats on day, need to derive based on the start date
*/
if (calendarFrequencyType.isWeekly()) { if (calendarFrequencyType.isWeekly()) {
repeatsOnDay = newMeetingStartDate.get(ChronoField.DAY_OF_WEEK); repeatsOnDay = newMeetingStartDate.get(ChronoField.DAY_OF_WEEK);
} else if (calendarFrequencyType.isMonthly()) { } else if (calendarFrequencyType.isMonthly()) {
repeatsOnDay = newMeetingStartDate.getDayOfMonth(); repeatsOnDay = newMeetingStartDate.getDayOfMonth();
} }
// TODO cover other recurrence also // TODO cover other recurrence also
this.recurrence = constructRecurrence(calendarFrequencyType, interval, repeatsOnDay, null); this.recurrence = constructRecurrence(calendarFrequencyType, interval, repeatsOnDay, null);
} }
return actualChanges; return actualChanges;
} }
public Map<String, Object> update(final JsonCommand command, final Boolean areActiveEntitiesSynced) { public Map<String, Object> update(final JsonCommand command, final Boolean areActiveEntitiesSynced) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(9); final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
if (command.isChangeInStringParameterNamed(CalendarSupportedParameters.TITLE.getValue(), this.title)) { if (command.isChangeInStringParameterNamed(CalendarSupportedParameters.TITLE.getValue(), this.title)) {
@@ -234,13 +222,11 @@ public class Calendar extends AbstractAuditableWithUTCDateTimeCustom {
actualChanges.put(CalendarSupportedParameters.TITLE.getValue(), newValue); actualChanges.put(CalendarSupportedParameters.TITLE.getValue(), newValue);
this.title = StringUtils.defaultIfEmpty(newValue, null); this.title = StringUtils.defaultIfEmpty(newValue, null);
} }
if (command.isChangeInStringParameterNamed(CalendarSupportedParameters.DESCRIPTION.getValue(), this.description)) { if (command.isChangeInStringParameterNamed(CalendarSupportedParameters.DESCRIPTION.getValue(), this.description)) {
final String newValue = command.stringValueOfParameterNamed(CalendarSupportedParameters.DESCRIPTION.getValue()); final String newValue = command.stringValueOfParameterNamed(CalendarSupportedParameters.DESCRIPTION.getValue());
actualChanges.put(CalendarSupportedParameters.DESCRIPTION.getValue(), newValue); actualChanges.put(CalendarSupportedParameters.DESCRIPTION.getValue(), newValue);
this.description = StringUtils.defaultIfEmpty(newValue, null); this.description = StringUtils.defaultIfEmpty(newValue, null);
} }
if (command.isChangeInStringParameterNamed(CalendarSupportedParameters.LOCATION.getValue(), this.location)) { if (command.isChangeInStringParameterNamed(CalendarSupportedParameters.LOCATION.getValue(), this.location)) {
final String newValue = command.stringValueOfParameterNamed(CalendarSupportedParameters.LOCATION.getValue()); final String newValue = command.stringValueOfParameterNamed(CalendarSupportedParameters.LOCATION.getValue());
actualChanges.put(CalendarSupportedParameters.LOCATION.getValue(), newValue); actualChanges.put(CalendarSupportedParameters.LOCATION.getValue(), newValue);
@@ -251,12 +237,11 @@ public class Calendar extends AbstractAuditableWithUTCDateTimeCustom {
final String localeAsInput = command.locale(); final String localeAsInput = command.locale();
final String startDateParamName = CalendarSupportedParameters.START_DATE.getValue(); final String startDateParamName = CalendarSupportedParameters.START_DATE.getValue();
if (command.isChangeInLocalDateParameterNamed(startDateParamName, getStartDateLocalDate())) { if (command.isChangeInLocalDateParameterNamed(startDateParamName, getStartDateLocalDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(startDateParamName); final String valueAsInput = command.stringValueOfParameterNamed(startDateParamName);
final LocalDate newValue = command.localDateValueOfParameterNamed(startDateParamName); final LocalDate newValue = command.localDateValueOfParameterNamed(startDateParamName);
final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); final LocalDate currentDate = DateUtils.getLocalDateOfTenant();
if (newValue.isBefore(currentDate)) { if (DateUtils.isBefore(newValue, currentDate)) {
final String defaultUserMessage = "New meeting effective from date cannot be in past"; final String defaultUserMessage = "New meeting effective from date cannot be in past";
throw new CalendarDateException("new.start.date.cannot.be.in.past", defaultUserMessage, newValue, getStartDateLocalDate()); throw new CalendarDateException("new.start.date.cannot.be.in.past", defaultUserMessage, newValue, getStartDateLocalDate());
} else if (isStartDateAfter(newValue) && isStartDateBeforeOrEqual(currentDate)) { } else if (isStartDateAfter(newValue) && isStartDateBeforeOrEqual(currentDate)) {
@@ -472,24 +457,19 @@ public class Calendar extends AbstractAuditableWithUTCDateTimeCustom {
} }
public boolean isStartDateBefore(final LocalDate compareDate) { public boolean isStartDateBefore(final LocalDate compareDate) {
if (this.startDate != null && compareDate != null && getStartDateLocalDate().isBefore(compareDate)) { return startDate != null && DateUtils.isBefore(startDate, compareDate);
return true;
}
return false;
} }
public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) { public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) {
return this.startDate != null && compareDate != null return startDate != null && !DateUtils.isAfter(startDate, compareDate);
&& (getStartDateLocalDate().isBefore(compareDate) || getStartDateLocalDate().equals(compareDate));
} }
public boolean isStartDateAfter(final LocalDate compareDate) { public boolean isStartDateAfter(final LocalDate compareDate) {
return this.startDate != null && compareDate != null && getStartDateLocalDate().isAfter(compareDate); return compareDate != null && DateUtils.isAfter(startDate, compareDate);
} }
public boolean isEndDateAfterOrEqual(final LocalDate compareDate) { public boolean isEndDateAfterOrEqual(final LocalDate compareDate) {
return this.endDate != null && compareDate != null return compareDate != null && !DateUtils.isBefore(endDate, compareDate);
&& (getEndDateLocalDate().isAfter(compareDate) || getEndDateLocalDate().isEqual(compareDate));
} }
public boolean isBetweenStartAndEndDate(final LocalDate compareDate) { public boolean isBetweenStartAndEndDate(final LocalDate compareDate) {
@@ -585,7 +565,7 @@ public class Calendar extends AbstractAuditableWithUTCDateTimeCustom {
// validate with history details. // validate with history details.
for (CalendarHistory history : history()) { for (CalendarHistory history : history()) {
if (history.isBetweenStartAndEndDate(compareDate)) { if (history.isBetweenStartAndEndDate(compareDate)) {
return CalendarUtils.isValidRedurringDate(history.getRecurrence(), history.getStartDateLocalDate(), compareDate, return CalendarUtils.isValidRedurringDate(history.getRecurrence(), history.getStartDate(), compareDate,
isSkipRepaymentOnFirstMonth, numberOfDays); isSkipRepaymentOnFirstMonth, numberOfDays);
} }
} }
@@ -88,51 +88,63 @@ public enum CalendarEntityType {
} }
public static boolean isGroup(final Integer value) { public static boolean isGroup(final Integer value) {
return CalendarEntityType.GROUPS.value.equals(value) ? true : false; return CalendarEntityType.GROUPS.value.equals(value);
} }
public static boolean isGroup(final String name) { public static boolean isGroup(final String name) {
return CalendarEntityType.GROUPS.name().equalsIgnoreCase(name) ? true : false; return CalendarEntityType.GROUPS.name().equalsIgnoreCase(name);
} }
public static boolean isCenter(final Integer value) { public static boolean isCenter(final Integer value) {
return CalendarEntityType.CENTERS.value.equals(value) ? true : false; return CalendarEntityType.CENTERS.value.equals(value);
} }
public static boolean isCenter(final String name) { public static boolean isCenter(final String name) {
return CalendarEntityType.CENTERS.name().equalsIgnoreCase(name) ? true : false; return CalendarEntityType.CENTERS.name().equalsIgnoreCase(name);
} }
public static boolean isLoan(final Integer value) { public static boolean isLoan(final Integer value) {
return CalendarEntityType.LOANS.value.equals(value) ? true : false; return CalendarEntityType.LOANS.value.equals(value);
} }
public static boolean isLoan(final String name) { public static boolean isLoan(final String name) {
return CalendarEntityType.LOANS.name().equalsIgnoreCase(name) ? true : false; return CalendarEntityType.LOANS.name().equalsIgnoreCase(name);
} }
public static boolean isClient(final Integer value) { public static boolean isClient(final Integer value) {
return CalendarEntityType.CLIENTS.value.equals(value) ? true : false; return CalendarEntityType.CLIENTS.value.equals(value);
} }
public static boolean isClient(final String name) { public static boolean isClient(final String name) {
return CalendarEntityType.CLIENTS.name().equalsIgnoreCase(name) ? true : false; return CalendarEntityType.CLIENTS.name().equalsIgnoreCase(name);
}
public static boolean isSavings(final Integer value) {
return CalendarEntityType.SAVINGS.value.equals(value);
}
public static boolean isSavings(final String name) {
return CalendarEntityType.SAVINGS.name().equalsIgnoreCase(name);
} }
public boolean isCenter() { public boolean isCenter() {
return this.value.equals(CalendarEntityType.CENTERS.getValue()); return this == CENTERS;
} }
public boolean isGroup() { public boolean isGroup() {
return this.value.equals(CalendarEntityType.GROUPS.getValue()); return this == GROUPS;
} }
public boolean isLoan() { public boolean isLoan() {
return this.value.equals(CalendarEntityType.LOANS.getValue()); return this == LOANS;
} }
public boolean isClient() { public boolean isClient() {
return this.value.equals(CalendarEntityType.CLIENTS.getValue()); return this == CLIENTS;
}
public boolean isSavings() {
return this == SAVINGS;
} }
private static final Map<String, CalendarEntityType> entityNameToEnumMap = new HashMap<>(); private static final Map<String, CalendarEntityType> entityNameToEnumMap = new HashMap<>();
@@ -146,17 +158,4 @@ public enum CalendarEntityType {
public static CalendarEntityType getEntityType(String entityType) { public static CalendarEntityType getEntityType(String entityType) {
return entityNameToEnumMap.get(entityType.toLowerCase()); return entityNameToEnumMap.get(entityType.toLowerCase());
} }
public static boolean isSavings(final Integer value) {
return CalendarEntityType.SAVINGS.value.equals(value) ? true : false;
}
public static boolean isSavings(final String name) {
return CalendarEntityType.SAVINGS.name().equalsIgnoreCase(name) ? true : false;
}
public boolean isSavings() {
return this.value.equals(CalendarEntityType.SAVINGS.getValue());
}
} }
@@ -25,6 +25,7 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.time.LocalDate; import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@Entity @Entity
@Table(name = "m_calendar_history") @Table(name = "m_calendar_history")
@@ -95,26 +96,24 @@ public class CalendarHistory extends AbstractPersistableCustom {
return this.recurrence; return this.recurrence;
} }
public LocalDate getStartDateLocalDate() { public LocalDate getStartDate() {
return this.startDate; return this.startDate;
} }
public LocalDate getEndDateLocalDate() { public LocalDate getEndDate() {
return this.endDate; return this.endDate;
} }
public boolean isEndDateAfterOrEqual(final LocalDate compareDate) { public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) {
return this.endDate != null && compareDate != null return this.startDate != null && compareDate != null && !DateUtils.isAfter(this.startDate, compareDate);
&& (getEndDateLocalDate().isAfter(compareDate) || getEndDateLocalDate().isEqual(compareDate));
} }
public boolean isStartDateBeforeOrEqual(final LocalDate compareDate) { public boolean isEndDateAfterOrEqual(final LocalDate compareDate) {
return this.startDate != null && compareDate != null return this.endDate != null && compareDate != null && !DateUtils.isBefore(this.endDate, compareDate);
&& (getStartDateLocalDate().isBefore(compareDate) || getStartDateLocalDate().equals(compareDate));
} }
public boolean isBetweenStartAndEndDate(final LocalDate compareDate) { public boolean isBetweenStartAndEndDate(final LocalDate compareDate) {
return isStartDateBeforeOrEqual(compareDate) && (getEndDateLocalDate() == null || isEndDateAfterOrEqual(compareDate)); return isStartDateBeforeOrEqual(compareDate) && (getEndDate() == null || isEndDateAfterOrEqual(compareDate));
} }
public void updateEndDate(LocalDate historyCalEndDate) { public void updateEndDate(LocalDate historyCalEndDate) {
@@ -103,14 +103,14 @@ public final class CalendarUtils {
final DateTime periodStart = new DateTime(java.util.Date.from(startDate.atZone(ZoneId.systemDefault()).toInstant())); final DateTime periodStart = new DateTime(java.util.Date.from(startDate.atZone(ZoneId.systemDefault()).toInstant()));
final Date seed = convertToiCal4JCompatibleDate(seedDate); final Date seed = convertToiCal4JCompatibleDate(seedDate);
final Date nextRecDate = recur.getNextDate(seed, periodStart); final Date nextRecDate = recur.getNextDate(seed, periodStart);
return nextRecDate == null ? null : LocalDateTime.ofInstant(nextRecDate.toInstant(), DateUtils.getSystemZoneId()); return nextRecDate == null ? null : LocalDateTime.ofInstant(nextRecDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
} }
private static LocalDate getNextRecurringDate(final Recur recur, final LocalDate seedDate, final LocalDate startDate) { private static LocalDate getNextRecurringDate(final Recur recur, final LocalDate seedDate, final LocalDate startDate) {
final DateTime periodStart = new DateTime(java.util.Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant())); final DateTime periodStart = new DateTime(java.util.Date.from(startDate.atStartOfDay(ZoneId.systemDefault()).toInstant()));
final Date seed = convertToiCal4JCompatibleDate(seedDate.atStartOfDay()); final Date seed = convertToiCal4JCompatibleDate(seedDate.atStartOfDay());
final Date nextRecDate = recur.getNextDate(seed, periodStart); final Date nextRecDate = recur.getNextDate(seed, periodStart);
return nextRecDate == null ? null : LocalDate.ofInstant(nextRecDate.toInstant(), DateUtils.getSystemZoneId()); return nextRecDate == null ? null : LocalDate.ofInstant(nextRecDate.toInstant(), DateUtils.getDateTimeZoneOfTenant());
} }
private static Date convertToiCal4JCompatibleDate(final LocalDateTime inputDate) { private static Date convertToiCal4JCompatibleDate(final LocalDateTime inputDate) {
@@ -125,16 +125,16 @@ public final class CalendarUtils {
} }
public static Collection<LocalDate> getRecurringDates(final String recurringRule, final LocalDate seedDate, final LocalDate endDate) { public static Collection<LocalDate> getRecurringDates(final String recurringRule, final LocalDate seedDate, final LocalDate endDate) {
final LocalDate periodStartDate = DateUtils.getLocalDateOfTenant(); final LocalDate periodStartDate = DateUtils.getLocalDateOfTenant();
final LocalDate periodEndDate = (endDate == null) ? DateUtils.getLocalDateOfTenant().plusYears(5) : endDate; final LocalDate periodEndDate = endDate == null ? periodStartDate.plusYears(5) : endDate;
return getRecurringDates(recurringRule, seedDate, periodStartDate, periodEndDate); return getRecurringDates(recurringRule, seedDate, periodStartDate, periodEndDate);
} }
public static Collection<LocalDate> getRecurringDatesFrom(final String recurringRule, final LocalDate seedDate, public static Collection<LocalDate> getRecurringDatesFrom(final String recurringRule, final LocalDate seedDate,
final LocalDate startDate) { final LocalDate startDate) {
final LocalDate periodStartDate = (startDate == null) ? DateUtils.getLocalDateOfTenant() : startDate; LocalDate currentDate = DateUtils.getLocalDateOfTenant();
final LocalDate periodEndDate = DateUtils.getLocalDateOfTenant().plusYears(5); final LocalDate periodStartDate = startDate == null ? currentDate : startDate;
final LocalDate periodEndDate = currentDate.plusYears(5);
return getRecurringDates(recurringRule, seedDate, periodStartDate, periodEndDate); return getRecurringDates(recurringRule, seedDate, periodStartDate, periodEndDate);
} }
@@ -150,7 +150,6 @@ public final class CalendarUtils {
public static Collection<LocalDate> getRecurringDates(final String recurringRule, final LocalDate seedDate, public static Collection<LocalDate> getRecurringDates(final String recurringRule, final LocalDate seedDate,
final LocalDate periodStartDate, final LocalDate periodEndDate, final int maxCount, boolean isSkippMeetingOnFirstDay, final LocalDate periodStartDate, final LocalDate periodEndDate, final int maxCount, boolean isSkippMeetingOnFirstDay,
final Integer numberOfDays) { final Integer numberOfDays) {
final Recur recur = CalendarUtils.getICalRecur(recurringRule); final Recur recur = CalendarUtils.getICalRecur(recurringRule);
return getRecurringDates(recur, seedDate, periodStartDate, periodEndDate, maxCount, isSkippMeetingOnFirstDay, numberOfDays); return getRecurringDates(recur, seedDate, periodStartDate, periodEndDate, maxCount, isSkippMeetingOnFirstDay, numberOfDays);
@@ -173,7 +172,6 @@ public final class CalendarUtils {
private static Collection<LocalDate> convertToLocalDateList(final DateList dates, final LocalDate seedDate, private static Collection<LocalDate> convertToLocalDateList(final DateList dates, final LocalDate seedDate,
final PeriodFrequencyType frequencyType, boolean isSkippMeetingOnFirstDay, final Integer numberOfDays) { final PeriodFrequencyType frequencyType, boolean isSkippMeetingOnFirstDay, final Integer numberOfDays) {
final Collection<LocalDate> recurringDates = new ArrayList<>(); final Collection<LocalDate> recurringDates = new ArrayList<>();
for (@SuppressWarnings("rawtypes") for (@SuppressWarnings("rawtypes")
@@ -407,10 +407,6 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom {
return ClientStatus.fromInt(this.status).isPending(); return ClientStatus.fromInt(this.status).isPending();
} }
private boolean isDateInTheFuture(final LocalDate localDate) {
return localDate.isAfter(DateUtils.getBusinessLocalDate());
}
public boolean isRejected() { public boolean isRejected() {
return ClientStatus.fromInt(this.status).isRejected(); return ClientStatus.fromInt(this.status).isRejected();
} }
@@ -444,49 +440,40 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom {
} }
private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) { private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) {
if (getSubmittedOnDate() != null && DateUtils.isDateInTheFuture(getSubmittedOnDate())) {
if (getSubmittedOnDate() != null && isDateInTheFuture(getSubmittedOnDate())) {
final String defaultUserMessage = "submitted date cannot be in the future."; final String defaultUserMessage = "submitted date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.in.the.future",
defaultUserMessage, ClientApiConstants.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, ClientApiConstants.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getActivationDate() != null && DateUtils.isAfter(getSubmittedOnDate(), getActivationDate())) {
if (getActivationLocalDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getActivationLocalDate())) {
final String defaultUserMessage = "submitted date cannot be after the activation date"; final String defaultUserMessage = "submitted date cannot be after the activation date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.after.activation.date", final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.after.activation.date",
defaultUserMessage, ClientApiConstants.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, ClientApiConstants.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getActivationDate() != null && DateUtils.isAfter(getReopenedDate(), getActivationDate())) {
if (getReopenedDate() != null && getActivationLocalDate() != null && getReopenedDate().isAfter(getActivationLocalDate())) {
final String defaultUserMessage = "reopened date cannot be after the submittedon date"; final String defaultUserMessage = "reopened date cannot be after the submittedon date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.after.reopened.date", final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.submittedOnDate.after.reopened.date",
defaultUserMessage, ClientApiConstants.reopenedDateParamName, this.reopenedDate); defaultUserMessage, ClientApiConstants.reopenedDateParamName, this.reopenedDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (DateUtils.isDateInTheFuture(getActivationDate())) {
if (getActivationLocalDate() != null && isDateInTheFuture(getActivationLocalDate())) {
final String defaultUserMessage = "Activation date cannot be in the future."; final String defaultUserMessage = "Activation date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.activationDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.activationDate.in.the.future",
defaultUserMessage, ClientApiConstants.activationDateParamName, getActivationLocalDate()); defaultUserMessage, ClientApiConstants.activationDateParamName, getActivationDate());
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getActivationDate() != null) {
if (getActivationLocalDate() != null) { if (this.office.isOpeningDateAfter(getActivationDate())) {
if (this.office.isOpeningDateAfter(getActivationLocalDate())) {
final String defaultUserMessage = "Client activation date cannot be a date before the office opening date."; final String defaultUserMessage = "Client activation date cannot be a date before the office opening date.";
final ApiParameterError error = ApiParameterError.parameterError( final ApiParameterError error = ApiParameterError.parameterError(
"error.msg.clients.activationDate.cannot.be.before.office.activation.date", defaultUserMessage, "error.msg.clients.activationDate.cannot.be.before.office.activation.date", defaultUserMessage,
ClientApiConstants.activationDateParamName, getActivationLocalDate()); ClientApiConstants.activationDateParamName, getActivationDate());
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
} }
@@ -518,14 +505,6 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom {
} }
} }
public LocalDate getActivationLocalDate() {
return this.activationDate;
}
public LocalDate getOfficeJoiningLocalDate() {
return this.officeJoiningDate;
}
public boolean isOfficeIdentifiedBy(final Long officeId) { public boolean isOfficeIdentifiedBy(final Long officeId) {
return this.office.identifiedBy(officeId); return this.office.identifiedBy(officeId);
} }
@@ -618,7 +597,7 @@ public class Client extends AbstractAuditableWithUTCDateTimeCustom {
} }
public boolean isActivatedAfter(final LocalDate submittedOn) { public boolean isActivatedAfter(final LocalDate submittedOn) {
return getActivationLocalDate().isAfter(submittedOn); return DateUtils.isAfter(getActivationDate(), submittedOn);
} }
public boolean isChildOfGroup(final Long groupId) { public boolean isChildOfGroup(final Long groupId) {
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.floatingrates.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Collection; import java.util.Collection;
import org.apache.fineract.infrastructure.core.service.DateUtils;
public class FloatingRateDTO { public class FloatingRateDTO {
@@ -43,7 +44,7 @@ public class FloatingRateDTO {
BigDecimal rate = null; BigDecimal rate = null;
for (FloatingRatePeriodData periodData : this.baseLendingRatePeriods) { for (FloatingRatePeriodData periodData : this.baseLendingRatePeriods) {
final LocalDate periodFromDate = periodData.getFromDate(); final LocalDate periodFromDate = periodData.getFromDate();
if (periodFromDate.isBefore(date) || periodFromDate.isEqual(date)) { if (!DateUtils.isAfter(periodFromDate, date)) {
rate = periodData.getInterestRate(); rate = periodData.getInterestRate();
break; break;
} }
@@ -89,9 +89,8 @@ public class FloatingRate extends AbstractAuditableWithUTCDateTimeCustom {
final String name = command.stringValueOfParameterNamed("name"); final String name = command.stringValueOfParameterNamed("name");
final boolean isBaseLendingRate = command.parameterExists("isBaseLendingRate") final boolean isBaseLendingRate = command.parameterExists("isBaseLendingRate")
? command.booleanPrimitiveValueOfParameterNamed("isBaseLendingRate") && command.booleanPrimitiveValueOfParameterNamed("isBaseLendingRate");
: false; final boolean isActive = !command.parameterExists("isActive") || command.booleanPrimitiveValueOfParameterNamed("isActive");
final boolean isActive = command.parameterExists("isActive") ? command.booleanPrimitiveValueOfParameterNamed("isActive") : true;
final List<FloatingRatePeriod> floatingRatePeriods = getRatePeriods(command); final List<FloatingRatePeriod> floatingRatePeriods = getRatePeriods(command);
return new FloatingRate(name, isBaseLendingRate, isActive, floatingRatePeriods); return new FloatingRate(name, isBaseLendingRate, isActive, floatingRatePeriods);
@@ -109,8 +108,7 @@ public class FloatingRate extends AbstractAuditableWithUTCDateTimeCustom {
final LocalDate fromDate = helper.extractLocalDateNamed("fromDate", ratePeriod, new HashSet<String>()); final LocalDate fromDate = helper.extractLocalDateNamed("fromDate", ratePeriod, new HashSet<String>());
final BigDecimal interestRate = ratePeriodObject.get("interestRate").getAsBigDecimal(); final BigDecimal interestRate = ratePeriodObject.get("interestRate").getAsBigDecimal();
final boolean isDifferentialToBaseLendingRate = helper.parameterExists("isDifferentialToBaseLendingRate", ratePeriod) final boolean isDifferentialToBaseLendingRate = helper.parameterExists("isDifferentialToBaseLendingRate", ratePeriod)
? ratePeriodObject.get("isDifferentialToBaseLendingRate").getAsBoolean() && ratePeriodObject.get("isDifferentialToBaseLendingRate").getAsBoolean();
: false;
final boolean isActive = true; final boolean isActive = true;
ratePeriods.add(new FloatingRatePeriod(fromDate, interestRate, isDifferentialToBaseLendingRate, isActive)); ratePeriods.add(new FloatingRatePeriod(fromDate, interestRate, isDifferentialToBaseLendingRate, isActive));
} }
@@ -170,7 +168,7 @@ public class FloatingRate extends AbstractAuditableWithUTCDateTimeCustom {
if (this.floatingRatePeriods != null) { if (this.floatingRatePeriods != null) {
for (FloatingRatePeriod ratePeriod : this.floatingRatePeriods) { for (FloatingRatePeriod ratePeriod : this.floatingRatePeriods) {
LocalDate fromDate = ratePeriod.getFromDate(); LocalDate fromDate = ratePeriod.getFromDate();
if (fromDate.isAfter(today)) { if (DateUtils.isAfter(fromDate, today)) {
ratePeriod.setActive(false); ratePeriod.setActive(false);
} }
} }
@@ -188,7 +186,7 @@ public class FloatingRate extends AbstractAuditableWithUTCDateTimeCustom {
for (FloatingRatePeriod floatingRatePeriod : this.floatingRatePeriods) { for (FloatingRatePeriod floatingRatePeriod : this.floatingRatePeriods) {
if (floatingRatePeriod.isActive()) { if (floatingRatePeriod.isActive()) {
// will enter // will enter
if (applicableRates.isEmpty() && floatingRateDTO.getStartDate().isBefore(floatingRatePeriod.fetchFromDate())) { if (applicableRates.isEmpty() && DateUtils.isBefore(floatingRateDTO.getStartDate(), floatingRatePeriod.fetchFromDate())) {
if (floatingRateDTO.isFloatingInterestRate()) { if (floatingRateDTO.isFloatingInterestRate()) {
addPeriodData = true; addPeriodData = true;
} }
@@ -340,7 +340,7 @@ public class GroupGeneralData implements Serializable {
} }
public boolean isChildGroup() { public boolean isChildGroup() {
return this.centerId == null ? false : true; return this.centerId != null;
} }
public Long getParentId() { public Long getParentId() {
@@ -25,6 +25,7 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; import jakarta.persistence.Table;
import java.time.LocalDate; import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.staff.domain.Staff; import org.apache.fineract.organisation.staff.domain.Staff;
@Entity @Entity
@@ -73,7 +74,7 @@ public class StaffAssignmentHistory extends AbstractAuditableCustom {
} }
public boolean matchesStartDateOf(final LocalDate matchingDate) { public boolean matchesStartDateOf(final LocalDate matchingDate) {
return getStartDate().isEqual(matchingDate); return DateUtils.isEqual(matchingDate, getStartDate());
} }
public LocalDate getStartDate() { public LocalDate getStartDate() {
@@ -27,6 +27,7 @@ import java.util.Map;
import lombok.Getter; import lombok.Getter;
import org.apache.fineract.accounting.glaccount.data.GLAccountData; import org.apache.fineract.accounting.glaccount.data.GLAccountData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@Getter @Getter
public final class TaxComponentData implements Serializable { public final class TaxComponentData implements Serializable {
@@ -136,7 +137,7 @@ public final class TaxComponentData implements Serializable {
} }
private boolean occursOnDayFrom(final LocalDate target) { private boolean occursOnDayFrom(final LocalDate target) {
return target != null && target.isAfter(startDate()); return DateUtils.isAfter(target, startDate());
} }
public LocalDate startDate() { public LocalDate startDate() {
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.tax.data;
import java.io.Serializable; import java.io.Serializable;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.service.DateUtils;
public class TaxComponentHistoryData implements Serializable { public class TaxComponentHistoryData implements Serializable {
@@ -38,10 +39,7 @@ public class TaxComponentHistoryData implements Serializable {
} }
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) { public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
if (this.endDate == null) { return DateUtils.isAfter(target, startDate()) && (endDate == null || !DateUtils.isAfter(target, endDate()));
return target != null && target.isAfter(startDate());
}
return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
} }
public LocalDate startDate() { public LocalDate startDate() {
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.tax.data;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate; import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.service.DateUtils;
public class TaxGroupMappingsData implements Serializable { public class TaxGroupMappingsData implements Serializable {
@@ -44,10 +45,7 @@ public class TaxGroupMappingsData implements Serializable {
} }
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) { public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
if (this.endDate == null) { return DateUtils.isAfter(target, startDate()) && (endDate == null || !DateUtils.isAfter(target, endDate()));
return target != null && target.isAfter(startDate());
}
return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
} }
public LocalDate startDate() { public LocalDate startDate() {
@@ -167,7 +167,7 @@ public class TaxComponent extends AbstractAuditableCustom {
} }
private boolean occursOnDayFrom(final LocalDate target) { private boolean occursOnDayFrom(final LocalDate target) {
return target != null && target.isAfter(startDate()); return DateUtils.isAfter(target, startDate());
} }
public Set<TaxComponentHistory> getTaxComponentHistories() { public Set<TaxComponentHistory> getTaxComponentHistories() {
@@ -24,6 +24,7 @@ import jakarta.persistence.Table;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
@Entity @Entity
@Table(name = "m_tax_component_history") @Table(name = "m_tax_component_history")
@@ -62,10 +63,7 @@ public class TaxComponentHistory extends AbstractAuditableCustom {
} }
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) { public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
if (this.endDate == null) { return DateUtils.isAfter(target, startDate()) && (endDate == null || !DateUtils.isAfter(target, endDate()));
return target != null && target.isAfter(startDate());
}
return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
} }
public BigDecimal getPercentage() { public BigDecimal getPercentage() {
@@ -28,6 +28,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.tax.api.TaxApiConstants; import org.apache.fineract.portfolio.tax.api.TaxApiConstants;
@Entity @Entity
@@ -81,10 +82,7 @@ public class TaxGroupMappings extends AbstractAuditableCustom {
} }
public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) { public boolean occursOnDayFromAndUpToAndIncluding(final LocalDate target) {
if (this.endDate == null) { return DateUtils.isAfter(target, startDate()) && (endDate == null || !DateUtils.isAfter(target, endDate()));
return target != null && target.isAfter(startDate());
}
return target != null && target.isAfter(startDate()) && !target.isAfter(endDate());
} }
public TaxComponent getTaxComponent() { public TaxComponent getTaxComponent() {
@@ -30,4 +30,6 @@ public final class AppUserConstants {
// TODO: Remove hard coding of system user name and make this a configurable parameter // TODO: Remove hard coding of system user name and make this a configurable parameter
public static final String SYSTEM_USER_NAME = "system"; public static final String SYSTEM_USER_NAME = "system";
public static final Long ADMIN_USER_ID = 1L;
public static final Long SYSTEM_USER_ID = 2L;
} }
@@ -37,6 +37,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.domain.PortfolioPr
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMapping;
import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository; import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGLAccountMappingRepository;
import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException; import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
@@ -61,7 +62,7 @@ public class InvestorAccountingHelper {
**/ **/
GLClosure gLClosure = getLatestClosureByBranch(officeId); GLClosure gLClosure = getLatestClosureByBranch(officeId);
if (gLClosure != null) { if (gLClosure != null) {
if (gLClosure.getClosingDate().isAfter(transactionDate) || gLClosure.getClosingDate().isEqual(transactionDate)) { if (!DateUtils.isAfter(transactionDate, gLClosure.getClosingDate())) {
throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED, gLClosure.getClosingDate(), null, throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED, gLClosure.getClosingDate(), null,
null); null);
} }
@@ -170,7 +170,7 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
throw new ExternalAssetOwnerInitiateTransferException( throw new ExternalAssetOwnerInitiateTransferException(
String.format("This loan cannot be bought back, effective transfer is not in right state: %s", String.format("This loan cannot be bought back, effective transfer is not in right state: %s",
effectiveTransfers.get(0).getStatus())); effectiveTransfers.get(0).getStatus()));
} else if (settlementDate.isBefore(effectiveTransfers.get(0).getSettlementDate())) { } else if (DateUtils.isBefore(settlementDate, effectiveTransfers.get(0).getSettlementDate())) {
throw new ExternalAssetOwnerInitiateTransferException( throw new ExternalAssetOwnerInitiateTransferException(
String.format("This loan cannot be bought back, settlement date is earlier than effective transfer settlement date: %s", String.format("This loan cannot be bought back, settlement date is earlier than effective transfer settlement date: %s",
effectiveTransfers.get(0).getSettlementDate())); effectiveTransfers.get(0).getSettlementDate()));
@@ -251,7 +251,7 @@ public class ExternalAssetOwnersWriteServiceImpl implements ExternalAssetOwnersW
} }
private void validateSettlementDate(LocalDate settlementDate) { private void validateSettlementDate(LocalDate settlementDate) {
if (settlementDate.isBefore(ThreadLocalContextUtil.getBusinessDate())) { if (DateUtils.isBeforeBusinessDate(settlementDate)) {
throw new ExternalAssetOwnerInitiateTransferException("Settlement date cannot be in the past"); throw new ExternalAssetOwnerInitiateTransferException("Settlement date cannot be in the past");
} }
} }
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.service.DateUtils;
/** /**
* Immutable data object representing disbursement information. * Immutable data object representing disbursement information.
@@ -85,8 +86,7 @@ public final class DisbursementData implements Comparable<DisbursementData> {
if (obj == null) { if (obj == null) {
return -1; return -1;
} }
return DateUtils.compare(obj.expectedDisbursementDate, this.expectedDisbursementDate);
return obj.expectedDisbursementDate.compareTo(this.expectedDisbursementDate);
} }
public boolean isDueForDisbursement(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) { public boolean isDueForDisbursement(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive) {
@@ -96,14 +96,11 @@ public final class DisbursementData implements Comparable<DisbursementData> {
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive, private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
final LocalDate target) { final LocalDate target) {
return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToAndInclusive); return DateUtils.isAfter(target, fromNotInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
} }
public BigDecimal getWaivedChargeAmount() { public BigDecimal getWaivedChargeAmount() {
if (this.waivedChargeAmount == null) { return this.waivedChargeAmount == null ? BigDecimal.ZERO : this.waivedChargeAmount;
return BigDecimal.ZERO;
}
return this.waivedChargeAmount;
} }
} }
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import lombok.Getter; import lombok.Getter;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType; import org.apache.fineract.portfolio.loanaccount.domain.LoanTermVariationType;
@Getter @Getter
@@ -64,7 +65,7 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData
} }
private boolean occursOnDayFromAndUpTo(final LocalDate fromNotInclusive, final LocalDate upToInclusive, final LocalDate target) { private boolean occursOnDayFromAndUpTo(final LocalDate fromNotInclusive, final LocalDate upToInclusive, final LocalDate target) {
return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToInclusive); return DateUtils.isAfter(target, fromNotInclusive) && !DateUtils.isAfter(target, upToInclusive);
} }
public boolean isApplicable(final LocalDate fromDate) { public boolean isApplicable(final LocalDate fromDate) {
@@ -72,7 +73,7 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData
} }
private boolean occursBefore(final LocalDate date, final LocalDate target) { private boolean occursBefore(final LocalDate date, final LocalDate target) {
return target != null && !target.isAfter(date); return target != null && !DateUtils.isAfter(target, date);
} }
public boolean isSpecificToInstallment() { public boolean isSpecificToInstallment() {
@@ -84,7 +85,7 @@ public class LoanTermVariationsData implements Comparable<LoanTermVariationsData
} }
public Boolean isProcessed() { public Boolean isProcessed() {
return this.isProcessed == null ? false : this.isProcessed; return this.isProcessed != null && this.isProcessed;
} }
public void setProcessed(Boolean isProcessed) { public void setProcessed(Boolean isProcessed) {
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import org.apache.fineract.infrastructure.core.service.DateUtils;
public class LoanTermVariationsDataWrapper { public class LoanTermVariationsDataWrapper {
@@ -54,7 +55,7 @@ public class LoanTermVariationsDataWrapper {
boolean hasVariation = false; boolean hasVariation = false;
if (iterator.hasNext()) { if (iterator.hasNext()) {
LoanTermVariationsData loanTermVariationsData = iterator.next(); LoanTermVariationsData loanTermVariationsData = iterator.next();
if (!loanTermVariationsData.getTermVariationApplicableFrom().isAfter(date)) { if (!DateUtils.isAfter(loanTermVariationsData.getTermVariationApplicableFrom(), date)) {
hasVariation = true; hasVariation = true;
} }
iterator.previous(); iterator.previous();
@@ -123,7 +124,7 @@ public class LoanTermVariationsDataWrapper {
public LoanTermVariationsData fetchLoanTermDueDateVariationsData(final LocalDate onDate) { public LoanTermVariationsData fetchLoanTermDueDateVariationsData(final LocalDate onDate) {
LoanTermVariationsData data = null; LoanTermVariationsData data = null;
for (LoanTermVariationsData termVariationsData : this.dueDateVariation) { for (LoanTermVariationsData termVariationsData : this.dueDateVariation) {
if (onDate.isEqual(termVariationsData.getTermVariationApplicableFrom())) { if (DateUtils.isEqual(onDate, termVariationsData.getTermVariationApplicableFrom())) {
data = termVariationsData; data = termVariationsData;
break; break;
} }
@@ -2352,8 +2352,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final LocalDate submittalDate = this.submittedOnDate; final LocalDate submittalDate = this.submittedOnDate;
if (DateUtils.isBefore(approvedOn, submittalDate)) { if (DateUtils.isBefore(approvedOn, submittalDate)) {
final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: " final String errorMessage = "The date on which a loan is approved cannot be before its submittal date: " + submittalDate;
+ submittalDate.toString();
throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage, throw new InvalidLoanStateTransitionException("approval", "cannot.be.before.submittal.date", errorMessage,
getApprovedOnDate(), submittalDate); getApprovedOnDate(), submittalDate);
} }
@@ -3394,8 +3393,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
incomePostTransactions.add(transaction); incomePostTransactions.add(transaction);
} }
} }
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); incomePostTransactions.sort(LoanTransactionComparator.INSTANCE);
Collections.sort(incomePostTransactions, transactionComparator);
return incomePostTransactions; return incomePostTransactions;
} }
@@ -3408,8 +3406,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
repaymentsOrWaivers.add(transaction); repaymentsOrWaivers.add(transaction);
} }
} }
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); repaymentsOrWaivers.sort(LoanTransactionComparator.INSTANCE);
Collections.sort(repaymentsOrWaivers, transactionComparator);
return repaymentsOrWaivers; return repaymentsOrWaivers;
} }
@@ -3422,8 +3419,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
repaymentsOrWaivers.add(transaction); repaymentsOrWaivers.add(transaction);
} }
} }
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); repaymentsOrWaivers.sort(LoanTransactionComparator.INSTANCE);
Collections.sort(repaymentsOrWaivers, transactionComparator);
return repaymentsOrWaivers; return repaymentsOrWaivers;
} }
@@ -3434,8 +3430,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
repaymentsOrWaivers.add(transaction); repaymentsOrWaivers.add(transaction);
} }
} }
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); repaymentsOrWaivers.sort(LoanTransactionComparator.INSTANCE);
Collections.sort(repaymentsOrWaivers, transactionComparator);
return repaymentsOrWaivers; return repaymentsOrWaivers;
} }
@@ -3446,8 +3441,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
transactions.add(transaction); transactions.add(transaction);
} }
} }
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); transactions.sort(LoanTransactionComparator.INSTANCE);
Collections.sort(transactions, transactionComparator);
return transactions; return transactions;
} }
@@ -3458,8 +3452,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
transactions.add(transaction); transactions.add(transaction);
} }
} }
final LoanTransactionComparator transactionComparator = new LoanTransactionComparator(); transactions.sort(LoanTransactionComparator.INSTANCE);
Collections.sort(transactions, transactionComparator);
return transactions; return transactions;
} }
@@ -6670,7 +6663,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
paidFromFutureInstallments = paidFromFutureInstallments.plus(balancesForCurrentPeroid[4]) paidFromFutureInstallments = paidFromFutureInstallments.plus(balancesForCurrentPeroid[4])
.minus(balancesForCurrentPeroid[2]); .minus(balancesForCurrentPeroid[2]);
} }
} else if (installment.getDueDate().isAfter(paymentDate)) { } else {
paidFromFutureInstallments = paidFromFutureInstallments.plus(installment.getInterestPaid(currency)) paidFromFutureInstallments = paidFromFutureInstallments.plus(installment.getInterestPaid(currency))
.plus(installment.getPenaltyChargesPaid(currency)).plus(installment.getFeeChargesPaid(currency)); .plus(installment.getPenaltyChargesPaid(currency)).plus(installment.getFeeChargesPaid(currency));
} }
@@ -363,13 +363,11 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
if (this.loan != null) { if (this.loan != null) {
switch (ChargeCalculationType.fromInt(this.chargeCalculation)) { switch (ChargeCalculationType.fromInt(this.chargeCalculation)) {
case PERCENT_OF_AMOUNT: case PERCENT_OF_AMOUNT:
// If charge type is specified due date and loan is multi // If charge type is specified due date and loan is multi disburment loan.
// disburment loan. // Then we need to get as of this loan charge due date how much amount disbursed.
// Then we need to get as of this loan charge due date how
// much amount disbursed.
if (this.loan.isMultiDisburmentLoan() && this.isSpecifiedDueDate()) { if (this.loan.isMultiDisburmentLoan() && this.isSpecifiedDueDate()) {
for (final LoanDisbursementDetails loanDisbursementDetails : this.loan.getDisbursementDetails()) { for (final LoanDisbursementDetails loanDisbursementDetails : this.loan.getDisbursementDetails()) {
if (!loanDisbursementDetails.expectedDisbursementDate().isAfter(this.getDueDate())) { if (!DateUtils.isAfter(loanDisbursementDetails.expectedDisbursementDate(), this.getDueDate())) {
amountPercentageAppliedTo = amountPercentageAppliedTo.add(loanDisbursementDetails.principal()); amountPercentageAppliedTo = amountPercentageAppliedTo.add(loanDisbursementDetails.principal());
} }
} }
@@ -635,13 +633,12 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
private boolean occursOnDayFromExclusiveAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive, private boolean occursOnDayFromExclusiveAndUpToAndIncluding(final LocalDate fromNotInclusive, final LocalDate upToAndInclusive,
final LocalDate target) { final LocalDate target) {
return target != null && target.isAfter(fromNotInclusive) && !target.isAfter(upToAndInclusive); return DateUtils.isAfter(target, fromNotInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
} }
private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive, private boolean occursOnDayFromAndUpToAndIncluding(final LocalDate fromAndInclusive, final LocalDate upToAndInclusive,
final LocalDate target) { final LocalDate target) {
return target != null && (target.isEqual(fromAndInclusive) || target.isAfter(fromAndInclusive)) return target != null && !DateUtils.isBefore(target, fromAndInclusive) && !DateUtils.isAfter(target, upToAndInclusive);
&& !target.isAfter(upToAndInclusive);
} }
public boolean isFeeCharge() { public boolean isFeeCharge() {
@@ -792,8 +789,9 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
public LoanInstallmentCharge getUnpaidInstallmentLoanCharge() { public LoanInstallmentCharge getUnpaidInstallmentLoanCharge() {
LoanInstallmentCharge unpaidChargePerInstallment = null; LoanInstallmentCharge unpaidChargePerInstallment = null;
for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
if (loanChargePerInstallment.isPending() && (unpaidChargePerInstallment == null || unpaidChargePerInstallment if (loanChargePerInstallment.isPending() && (unpaidChargePerInstallment == null
.getRepaymentInstallment().getDueDate().isAfter(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { || DateUtils.isAfter(unpaidChargePerInstallment.getRepaymentInstallment().getDueDate(),
loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
unpaidChargePerInstallment = loanChargePerInstallment; unpaidChargePerInstallment = loanChargePerInstallment;
} }
} }
@@ -802,7 +800,7 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
public LoanInstallmentCharge getInstallmentLoanCharge(final LocalDate periodDueDate) { public LoanInstallmentCharge getInstallmentLoanCharge(final LocalDate periodDueDate) {
for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) { for (final LoanInstallmentCharge loanChargePerInstallment : this.loanInstallmentCharge) {
if (periodDueDate.isEqual(loanChargePerInstallment.getRepaymentInstallment().getDueDate())) { if (DateUtils.isEqual(periodDueDate, loanChargePerInstallment.getRepaymentInstallment().getDueDate())) {
return loanChargePerInstallment; return loanChargePerInstallment;
} }
} }
@@ -974,8 +972,9 @@ public class LoanCharge extends AbstractAuditableWithUTCDateTimeCustom {
Money outstanding = Money.of(currency, loanChargePerInstallment.getAmountOutstanding()); Money outstanding = Money.of(currency, loanChargePerInstallment.getAmountOutstanding());
final boolean partiallyPaid = outstanding.isGreaterThanZero() final boolean partiallyPaid = outstanding.isGreaterThanZero()
&& outstanding.isLessThan(loanChargePerInstallment.getAmount(currency)); && outstanding.isLessThan(loanChargePerInstallment.getAmount(currency));
if ((partiallyPaid || loanChargePerInstallment.isPaid()) && (paidChargePerInstallment == null || paidChargePerInstallment if ((partiallyPaid || loanChargePerInstallment.isPaid()) && (paidChargePerInstallment == null
.getRepaymentInstallment().getDueDate().isBefore(loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { || DateUtils.isBefore(paidChargePerInstallment.getRepaymentInstallment().getDueDate(),
loanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
paidChargePerInstallment = loanChargePerInstallment; paidChargePerInstallment = loanChargePerInstallment;
} }
} }
@@ -27,6 +27,7 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Objects; import java.util.Objects;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.loanaccount.data.DisbursementData; import org.apache.fineract.portfolio.loanaccount.data.DisbursementData;
@Entity @Entity
@@ -71,16 +72,11 @@ public class LoanDisbursementDetails extends AbstractPersistableCustom {
@Override @Override
public boolean equals(final Object obj) { public boolean equals(final Object obj) {
if (!(obj instanceof LoanDisbursementDetails)) { if (!(obj instanceof LoanDisbursementDetails loanDisbursementDetails)) {
return false; return false;
} }
final LoanDisbursementDetails loanDisbursementDetails = (LoanDisbursementDetails) obj; return loanDisbursementDetails.principal.equals(this.principal)
if (loanDisbursementDetails.principal.equals(this.principal) && DateUtils.isEqual(loanDisbursementDetails.expectedDisbursementDate, this.expectedDisbursementDate);
&& loanDisbursementDetails.expectedDisbursementDate.compareTo(this.expectedDisbursementDate) == 0 ? Boolean.TRUE
: Boolean.FALSE) {
return true;
}
return false;
} }
@Override @Override
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
@@ -114,7 +115,7 @@ public class LoanRepaymentScheduleProcessingWrapper {
if (loanCharge.getLoan() != null && loanCharge.isSpecifiedDueDate() if (loanCharge.getLoan() != null && loanCharge.isSpecifiedDueDate()
&& loanCharge.getLoan().isMultiDisburmentLoan()) { && loanCharge.getLoan().isMultiDisburmentLoan()) {
for (final LoanDisbursementDetails loanDisbursementDetails : loanCharge.getLoan().getDisbursementDetails()) { for (final LoanDisbursementDetails loanDisbursementDetails : loanCharge.getLoan().getDisbursementDetails()) {
if (!loanDisbursementDetails.expectedDisbursementDate().isAfter(loanCharge.getDueDate())) { if (!DateUtils.isAfter(loanDisbursementDetails.expectedDisbursementDate(), loanCharge.getDueDate())) {
amount = amount.add(loanDisbursementDetails.principal()); amount = amount.add(loanDisbursementDetails.principal());
} }
} }
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -230,7 +231,7 @@ public final class LoanSummaryWrapper {
if (totalOverdue.isGreaterThanZero()) { if (totalOverdue.isGreaterThanZero()) {
for (final LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) { for (final LoanRepaymentScheduleInstallment installment : repaymentScheduleInstallments) {
if (installment.isOverdueOn(from)) { if (installment.isOverdueOn(from)) {
if (overdueSince == null || overdueSince.isAfter(installment.getDueDate())) { if (overdueSince == null || DateUtils.isAfter(overdueSince, installment.getDueDate())) {
overdueSince = installment.getDueDate(); overdueSince = installment.getDueDate();
} }
} }
@@ -29,7 +29,6 @@ import jakarta.persistence.Table;
import jakarta.persistence.UniqueConstraint; import jakarta.persistence.UniqueConstraint;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
@@ -850,29 +849,6 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
this.manuallyAdjustedOrReversed = true; this.manuallyAdjustedOrReversed = true;
} }
@Override
public OffsetDateTime getCreatedDateTime() {
return (this.getCreatedDate().isPresent() ? this.getCreatedDate().get() : DateUtils.getOffsetDateTimeOfTenantWithMostPrecision());
}
public boolean isLastTransaction(final LoanTransaction loanTransaction) {
boolean isLatest = false;
if (loanTransaction != null) {
isLatest = this.getTransactionDate().isBefore(loanTransaction.getTransactionDate())
|| (this.getTransactionDate().isEqual(loanTransaction.getTransactionDate())
&& this.getCreatedDateTime().isBefore(loanTransaction.getCreatedDateTime()));
}
return isLatest;
}
public boolean isLatestTransaction(final LoanTransaction loanTransaction) {
boolean isLatest = false;
if (loanTransaction != null) {
isLatest = this.getCreatedDateTime().isBefore(loanTransaction.getCreatedDateTime());
}
return isLatest;
}
public void updateLoanTransactionToRepaymentScheduleMappings(final Collection<LoanTransactionToRepaymentScheduleMapping> mappings) { public void updateLoanTransactionToRepaymentScheduleMappings(final Collection<LoanTransactionToRepaymentScheduleMapping> mappings) {
Collection<LoanTransactionToRepaymentScheduleMapping> retainMappings = new ArrayList<>(); Collection<LoanTransactionToRepaymentScheduleMapping> retainMappings = new ArrayList<>();
for (LoanTransactionToRepaymentScheduleMapping updatedrepaymentScheduleMapping : mappings) { for (LoanTransactionToRepaymentScheduleMapping updatedrepaymentScheduleMapping : mappings) {
@@ -963,6 +939,18 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
return amount; return amount;
} }
public boolean isBefore(final LocalDate date) {
return DateUtils.isBefore(getTransactionDate(), date);
}
public boolean isAfter(final LocalDate date) {
return DateUtils.isAfter(getTransactionDate(), date);
}
public boolean isOn(final LocalDate date) {
return DateUtils.isEqual(getTransactionDate(), date);
}
// TODO missing hashCode(), equals(Object obj), but probably OK as long as // TODO missing hashCode(), equals(Object obj), but probably OK as long as
// this is never stored in a Collection. // this is never stored in a Collection.
} }
@@ -19,47 +19,40 @@
package org.apache.fineract.portfolio.loanaccount.domain; package org.apache.fineract.portfolio.loanaccount.domain;
import java.util.Comparator; import java.util.Comparator;
import org.apache.fineract.infrastructure.core.service.DateUtils;
/** /**
* Sort loan transactions by transaction date, created date and transaction type placing * Sort loan transactions by transaction date, created date and transaction type placing
*/ */
public class LoanTransactionComparator implements Comparator<LoanTransaction> { public class LoanTransactionComparator implements Comparator<LoanTransaction> {
public static final LoanTransactionComparator INSTANCE = new LoanTransactionComparator();
@Override @Override
public int compare(final LoanTransaction o1, final LoanTransaction o2) { public int compare(final LoanTransaction o1, final LoanTransaction o2) {
int compareResult = 0; int result = DateUtils.compare(o1.getTransactionDate(), o2.getTransactionDate());
final int comparsion = o1.getTransactionDate().compareTo(o2.getTransactionDate()); if (result != 0) {
/** return result;
* For transactions bearing the same transaction date, we sort transactions based on created date (when
* available) after which sorting for waivers takes place
**/
if (comparsion == 0) {
int comparisonBasedOnCreatedDate = 0;
if (o1.isIncomePosting() && o2.isNotIncomePosting()) {
compareResult = -1;
} else if (o1.isNotIncomePosting() && o2.isIncomePosting()) {
compareResult = 1;
} else {
compareResult = 0;
}
if (o1.getCreatedDateTime() != null && o2.getCreatedDateTime() != null) {
comparisonBasedOnCreatedDate = o1.getCreatedDateTime().compareTo(o2.getCreatedDateTime());
}
// equal transaction dates
if (comparisonBasedOnCreatedDate == 0) {
if (o1.isWaiver() && o2.isNotWaiver()) {
compareResult = -1;
} else if (o1.isNotWaiver() && o2.isWaiver()) {
compareResult = 1;
} else {
compareResult = 0;
}
}
} else {
compareResult = comparsion;
} }
// For transactions bearing the same transaction date, we sort transactions based on created date (when
return compareResult; // available) after which sorting for waivers takes place
result = o1.getCreatedDate().isPresent()
? (o2.getCreatedDate().isPresent() ? DateUtils.compare(o1.getCreatedDateTime(), o2.getCreatedDateTime()) : -1)
: (o2.getCreatedDate().isPresent() ? 1 : 0);
if (result != 0) {
return result;
}
// equal transaction dates
if (o1.isIncomePosting() != o2.isIncomePosting()) {
return o1.isIncomePosting() ? -1 : 1;
}
if (o1.isWaiver() != o2.isWaiver()) {
return o1.isWaiver() ? -1 : 1;
}
result = o1.getId() != null ? (o2.getId() != null ? Long.compare(o1.getId(), o2.getId()) : -1) : (o2.getId() != null ? 1 : 0);
if (result != 0) {
return result;
}
return 0;
} }
} }
@@ -200,11 +200,7 @@ public final class LoanSchedulePeriodData {
this.totalOutstandingForPeriod = this.feeChargesOutstanding; this.totalOutstandingForPeriod = this.feeChargesOutstanding;
this.totalActualCostOfLoanForPeriod = this.feeChargesDue; this.totalActualCostOfLoanForPeriod = this.feeChargesDue;
this.totalInstallmentAmountForPeriod = null; this.totalInstallmentAmountForPeriod = null;
if (dueDate.isBefore(DateUtils.getBusinessLocalDate())) { this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
this.totalCredits = BigDecimal.ZERO; this.totalCredits = BigDecimal.ZERO;
this.downPaymentPeriod = false; this.downPaymentPeriod = false;
} }
@@ -263,12 +259,7 @@ public final class LoanSchedulePeriodData {
this.totalOutstandingForPeriod = totalDueForPeriod; this.totalOutstandingForPeriod = totalDueForPeriod;
this.totalActualCostOfLoanForPeriod = interestDueOnPrincipalOutstanding.add(feeChargesDueForPeriod); this.totalActualCostOfLoanForPeriod = interestDueOnPrincipalOutstanding.add(feeChargesDueForPeriod);
this.totalInstallmentAmountForPeriod = totalInstallmentAmountForPeriod; this.totalInstallmentAmountForPeriod = totalInstallmentAmountForPeriod;
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
if (dueDate.isBefore(DateUtils.getBusinessLocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
this.totalCredits = BigDecimal.ZERO; this.totalCredits = BigDecimal.ZERO;
this.downPaymentPeriod = false; this.downPaymentPeriod = false;
} }
@@ -322,12 +313,7 @@ public final class LoanSchedulePeriodData {
this.totalOutstandingForPeriod = totalDueForPeriod; this.totalOutstandingForPeriod = totalDueForPeriod;
this.totalActualCostOfLoanForPeriod = null; this.totalActualCostOfLoanForPeriod = null;
this.totalInstallmentAmountForPeriod = totalDueForPeriod; this.totalInstallmentAmountForPeriod = totalDueForPeriod;
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
if (dueDate.isBefore(DateUtils.getBusinessLocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
this.totalCredits = BigDecimal.ZERO; this.totalCredits = BigDecimal.ZERO;
this.downPaymentPeriod = true; this.downPaymentPeriod = true;
} }
@@ -396,12 +382,7 @@ public final class LoanSchedulePeriodData {
this.totalOutstandingForPeriod = totalOutstanding; this.totalOutstandingForPeriod = totalOutstanding;
this.totalActualCostOfLoanForPeriod = totalActualCostOfLoanForPeriod; this.totalActualCostOfLoanForPeriod = totalActualCostOfLoanForPeriod;
this.totalInstallmentAmountForPeriod = totalInstallmentAmountForPeriod; this.totalInstallmentAmountForPeriod = totalInstallmentAmountForPeriod;
this.totalOverdue = DateUtils.isBeforeBusinessDate(dueDate) ? this.totalOutstandingForPeriod : null;
if (dueDate.isBefore(DateUtils.getBusinessLocalDate())) {
this.totalOverdue = this.totalOutstandingForPeriod;
} else {
this.totalOverdue = null;
}
this.totalCredits = totalCredits; this.totalCredits = totalCredits;
this.downPaymentPeriod = isDownPayment; this.downPaymentPeriod = isDownPayment;
} }
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.portfolio.loanaccount.loanschedule.domain; package org.apache.fineract.portfolio.loanaccount.loanschedule.domain;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.time.LocalDate; import java.time.LocalDate;
@@ -26,6 +27,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency; import org.apache.fineract.organisation.monetary.domain.ApplicationCurrency;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
@@ -716,7 +718,7 @@ public final class LoanApplicationTerms {
// number of days from 'ideal disbursement' to final date // number of days from 'ideal disbursement' to final date
LocalDate loanStartDate = getExpectedDisbursementDate(); LocalDate loanStartDate = getExpectedDisbursementDate();
if (getInterestChargedFromDate() != null && loanStartDate.isBefore(getInterestChargedFromLocalDate())) { if (DateUtils.isBefore(loanStartDate, getInterestChargedFromLocalDate())) {
loanStartDate = getInterestChargedFromLocalDate(); loanStartDate = getInterestChargedFromLocalDate();
} }
@@ -773,7 +775,7 @@ public final class LoanApplicationTerms {
CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(getLoanTermPeriodFrequencyType()), CalendarUtils.getMeetingFrequencyFromPeriodFrequencyType(getLoanTermPeriodFrequencyType()),
this.holidayDetailDTO.getWorkingDays(), isSkipRepaymentOnFirstDayOfMonth, numberOfDays); this.holidayDetailDTO.getWorkingDays(), isSkipRepaymentOnFirstDayOfMonth, numberOfDays);
} }
if (!expectedStartDate.isEqual(startDate)) { if (!DateUtils.isEqual(expectedStartDate, startDate)) {
diffDays = Math.toIntExact(ChronoUnit.DAYS.between(startDate, expectedStartDate)); diffDays = Math.toIntExact(ChronoUnit.DAYS.between(startDate, expectedStartDate));
} }
if (numberOfMonths == 0) { if (numberOfMonths == 0) {
@@ -1368,6 +1370,7 @@ public final class LoanApplicationTerms {
return this.multiDisburseLoan; return this.multiDisburseLoan;
} }
@NotNull
public Money getMaxOutstandingBalance() { public Money getMaxOutstandingBalance() {
return Money.of(getCurrency(), this.maxOutstandingBalance); return Money.of(getCurrency(), this.maxOutstandingBalance);
} }
@@ -1440,7 +1443,7 @@ public final class LoanApplicationTerms {
int months = getPeriodsBetween(fromDate, toDate); int months = getPeriodsBetween(fromDate, toDate);
fromDate = fromDate.plusMonths(months); fromDate = fromDate.plusMonths(months);
isSameAsRepaymentPeriod = fromDate.isEqual(toDate); isSameAsRepaymentPeriod = DateUtils.isEqual(fromDate, toDate);
} }
break; break;
@@ -59,6 +59,7 @@ import org.apache.fineract.accounting.producttoaccountmapping.domain.ProductToGL
import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException; import org.apache.fineract.accounting.producttoaccountmapping.exception.ProductToGLAccountMappingNotFoundException;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.event.business.domain.journalentry.LoanJournalEntryCreatedBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.journalentry.LoanJournalEntryCreatedBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.organisation.office.domain.Office; import org.apache.fineract.organisation.office.domain.Office;
@@ -502,12 +503,9 @@ public class AccountingProcessorHelper {
* @param transactionDate * @param transactionDate
*/ */
public void checkForBranchClosures(final GLClosure latestGLClosure, final LocalDate transactionDate) { public void checkForBranchClosures(final GLClosure latestGLClosure, final LocalDate transactionDate) {
/** // check if an accounting closure has happened for this branch after the transaction Date
* check if an accounting closure has happened for this branch after the transaction Date
**/
if (latestGLClosure != null) { if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().isAfter(transactionDate) if (!DateUtils.isBefore(latestGLClosure.getClosingDate(), transactionDate)) {
|| latestGLClosure.getClosingDate().compareTo(transactionDate) == 0 ? Boolean.TRUE : Boolean.FALSE) {
throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(),
null, null); null, null);
} }
@@ -175,8 +175,7 @@ public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntry
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap); BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
params.add(new Object[] { Boolean.TRUE, runningBalance, officeRunningBalance, params.add(new Object[] { Boolean.TRUE, runningBalance, officeRunningBalance,
platformSecurityContext.authenticatedUser().getId(), DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(), platformSecurityContext.authenticatedUser().getId(), DateUtils.getAuditOffsetDateTime(), entryData.getId() });
entryData.getId() });
batchIndex++; batchIndex++;
if (batchIndex == batchUpdateSize || index == entryDataList.size() - 1) { if (batchIndex == batchUpdateSize || index == entryDataList.size() - 1) {
this.jdbcTemplate.batchUpdate(sql, params); this.jdbcTemplate.batchUpdate(sql, params);
@@ -213,7 +212,7 @@ public class JournalEntryRunningBalanceUpdateServiceImpl implements JournalEntry
for (JournalEntryData entryData : entryDataList) { for (JournalEntryData entryData : entryDataList) {
BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap); BigDecimal runningBalance = calculateRunningBalance(entryData, runningBalanceMap);
params.add(new Object[] { runningBalance, platformSecurityContext.authenticatedUser().getId(), params.add(new Object[] { runningBalance, platformSecurityContext.authenticatedUser().getId(),
DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(), entryData.getId() }); DateUtils.getAuditOffsetDateTime(), entryData.getId() });
} }
this.jdbcTemplate.batchUpdate(sql, params); this.jdbcTemplate.batchUpdate(sql, params);
} }
@@ -314,9 +314,7 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
final LocalDate journalEntriesTransactionDate = journalEntries.get(0).getTransactionDate(); final LocalDate journalEntriesTransactionDate = journalEntries.get(0).getTransactionDate();
final GLClosure latestGLClosureByBranch = this.glClosureRepository.getLatestGLClosureByBranch(officeId); final GLClosure latestGLClosureByBranch = this.glClosureRepository.getLatestGLClosureByBranch(officeId);
if (latestGLClosureByBranch != null) { if (latestGLClosureByBranch != null) {
if (latestGLClosureByBranch.getClosingDate().isAfter(journalEntriesTransactionDate) if (!DateUtils.isBefore(latestGLClosureByBranch.getClosingDate(), journalEntriesTransactionDate)) {
|| latestGLClosureByBranch.getClosingDate().compareTo(journalEntriesTransactionDate) == 0 ? Boolean.TRUE
: Boolean.FALSE) {
final String accountName = null; final String accountName = null;
final String accountGLCode = null; final String accountGLCode = null;
throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED, throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED,
@@ -555,24 +553,22 @@ public class JournalEntryWritePlatformServiceJpaRepositoryImpl implements Journa
} }
private void validateBusinessRulesForJournalEntries(final JournalEntryCommand command) { private void validateBusinessRulesForJournalEntries(final JournalEntryCommand command) {
/** check if date of Journal entry is valid ***/ // check if date of Journal entry is valid
final LocalDate transactionDate = command.getTransactionDate(); final LocalDate transactionDate = command.getTransactionDate();
// shouldn't be in the future // shouldn't be in the future
final LocalDate todaysDate = DateUtils.getBusinessLocalDate(); if (DateUtils.isDateInTheFuture(transactionDate)) {
if (transactionDate.isAfter(todaysDate)) {
throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.FUTURE_DATE, transactionDate, null, null); throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.FUTURE_DATE, transactionDate, null, null);
} }
// shouldn't be before an accounting closure // shouldn't be before an accounting closure
final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId()); final GLClosure latestGLClosure = this.glClosureRepository.getLatestGLClosureByBranch(command.getOfficeId());
if (latestGLClosure != null) { if (latestGLClosure != null) {
if (latestGLClosure.getClosingDate().isAfter(transactionDate) if (!DateUtils.isBefore(latestGLClosure.getClosingDate(), transactionDate)) {
|| latestGLClosure.getClosingDate().compareTo(transactionDate) == 0 ? Boolean.TRUE : Boolean.FALSE) {
throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(), throw new JournalEntryInvalidException(GlJournalEntryInvalidReason.ACCOUNTING_CLOSED, latestGLClosure.getClosingDate(),
null, null); null, null);
} }
} }
/*** check if credits and debits are valid **/ // check if credits and debits are valid
final SingleDebitOrCreditEntryCommand[] credits = command.getCredits(); final SingleDebitOrCreditEntryCommand[] credits = command.getCredits();
final SingleDebitOrCreditEntryCommand[] debits = command.getDebits(); final SingleDebitOrCreditEntryCommand[] debits = command.getDebits();
@@ -43,6 +43,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
@@ -107,7 +108,7 @@ public class ProvisioningEntriesWritePlatformServiceJpaRepositoryImpl implements
private void validateForCreateJournalEntry(ProvisioningEntryData existingEntry, ProvisioningEntry requested) { private void validateForCreateJournalEntry(ProvisioningEntryData existingEntry, ProvisioningEntry requested) {
LocalDate existingDate = existingEntry.getCreatedDate(); LocalDate existingDate = existingEntry.getCreatedDate();
LocalDate requestedDate = requested.getCreatedDate(); LocalDate requestedDate = requested.getCreatedDate();
if (existingDate.isAfter(requestedDate) || existingDate.compareTo(requestedDate) == 0 ? Boolean.TRUE : Boolean.FALSE) { if (!DateUtils.isBefore(existingDate, requestedDate)) {
throw new ProvisioningJournalEntriesCannotbeCreatedException(existingEntry.getCreatedDate(), requestedDate); throw new ProvisioningJournalEntriesCannotbeCreatedException(existingEntry.getCreatedDate(), requestedDate);
} }
} }
@@ -64,7 +64,7 @@ public class LoanAccountLock {
public LoanAccountLock(Long loanId, LockOwner lockOwner, LocalDate lockPlacedOnCobBusinessDate) { public LoanAccountLock(Long loanId, LockOwner lockOwner, LocalDate lockPlacedOnCobBusinessDate) {
this.loanId = loanId; this.loanId = loanId;
this.lockOwner = lockOwner; this.lockOwner = lockOwner;
this.lockPlacedOn = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(); this.lockPlacedOn = DateUtils.getAuditOffsetDateTime();
this.lockPlacedOnCobBusinessDate = lockPlacedOnCobBusinessDate; this.lockPlacedOnCobBusinessDate = lockPlacedOnCobBusinessDate;
} }
@@ -75,6 +75,6 @@ public class LoanAccountLock {
public void setNewLockOwner(LockOwner newLockOwner) { public void setNewLockOwner(LockOwner newLockOwner) {
this.lockOwner = newLockOwner; this.lockOwner = newLockOwner;
this.lockPlacedOn = DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(); this.lockPlacedOn = DateUtils.getAuditOffsetDateTime();
} }
} }
@@ -81,8 +81,8 @@ public class LoanCOBPartitioner implements Partitioner {
} }
StopWatch sw = new StopWatch(); StopWatch sw = new StopWatch();
sw.start(); sw.start();
List<LoanCOBPartition> loanCOBPartitions = new ArrayList<>(retrieveLoanIdService.retrieveLoanCOBPartitions(numberOfDays, List<LoanCOBPartition> loanCOBPartitions = new ArrayList<>(
businessDate, isCatchUp != null ? isCatchUp : false, partitionSize)); retrieveLoanIdService.retrieveLoanCOBPartitions(numberOfDays, businessDate, isCatchUp != null && isCatchUp, partitionSize));
sw.stop(); sw.stop();
// if there is no loan to be closed, we still would like to create at least one partition // if there is no loan to be closed, we still would like to create at least one partition
@@ -67,7 +67,7 @@ public class LoanLockingServiceImpl implements LoanLockingService {
UPDATE m_loan_account_locks SET version= version + 1, lock_owner = ?, lock_placed_on = ? WHERE loan_id = ? UPDATE m_loan_account_locks SET version= version + 1, lock_owner = ?, lock_placed_on = ? WHERE loan_id = ?
""", accountsToLock, getInClauseParameterSizeLimit(), (ps, id) -> { """, accountsToLock, getInClauseParameterSizeLimit(), (ps, id) -> {
ps.setString(1, lockOwner.name()); ps.setString(1, lockOwner.name());
ps.setObject(2, DateUtils.getOffsetDateTimeOfTenantWithMostPrecision()); ps.setObject(2, DateUtils.getAuditOffsetDateTime());
ps.setLong(3, id); ps.setLong(3, id);
}); });
} }
@@ -97,7 +97,7 @@ public class LoanLockingServiceImpl implements LoanLockingService {
ps.setLong(1, loanId); ps.setLong(1, loanId);
ps.setLong(2, 1); ps.setLong(2, 1);
ps.setString(3, lockOwner.name()); ps.setString(3, lockOwner.name());
ps.setObject(4, DateUtils.getOffsetDateTimeOfTenantWithMostPrecision()); ps.setObject(4, DateUtils.getAuditOffsetDateTime());
ps.setObject(5, cobBusinessDate); ps.setObject(5, cobBusinessDate);
}); });
} }
@@ -32,6 +32,7 @@ import org.apache.fineract.cob.loan.RetrieveLoanIdService;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.config.TaskExecutorConstant; import org.apache.fineract.infrastructure.core.config.TaskExecutorConstant;
import org.apache.fineract.infrastructure.core.domain.FineractContext; import org.apache.fineract.infrastructure.core.domain.FineractContext;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO; import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
import org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository; import org.apache.fineract.infrastructure.jobs.domain.JobParameterRepository;
@@ -73,7 +74,7 @@ public class AsyncLoanCOBExecutorServiceImpl implements AsyncLoanCOBExecutorServ
LocalDate oldestCOBProcessedDate = !loanIdAndLastClosedBusinessDate.isEmpty() LocalDate oldestCOBProcessedDate = !loanIdAndLastClosedBusinessDate.isEmpty()
? loanIdAndLastClosedBusinessDate.get(0).getLastClosedBusinessDate() ? loanIdAndLastClosedBusinessDate.get(0).getLastClosedBusinessDate()
: cobBusinessDate; : cobBusinessDate;
if (oldestCOBProcessedDate.isBefore(cobBusinessDate)) { if (DateUtils.isBefore(oldestCOBProcessedDate, cobBusinessDate)) {
executeLoanCOBDayByDayUntilCOBBusinessDate(oldestCOBProcessedDate, cobBusinessDate); executeLoanCOBDayByDayUntilCOBBusinessDate(oldestCOBProcessedDate, cobBusinessDate);
} }
} catch (NoSuchJobException e) { } catch (NoSuchJobException e) {
@@ -92,7 +93,7 @@ public class AsyncLoanCOBExecutorServiceImpl implements AsyncLoanCOBExecutorServ
Job job = jobLocator.getJob(LoanCOBConstant.JOB_NAME); Job job = jobLocator.getJob(LoanCOBConstant.JOB_NAME);
ScheduledJobDetail scheduledJobDetail = scheduledJobDetailRepository.findByJobName(LoanCOBConstant.JOB_HUMAN_READABLE_NAME); ScheduledJobDetail scheduledJobDetail = scheduledJobDetailRepository.findByJobName(LoanCOBConstant.JOB_HUMAN_READABLE_NAME);
LocalDate executingBusinessDate = oldestCOBProcessedDate.plusDays(1); LocalDate executingBusinessDate = oldestCOBProcessedDate.plusDays(1);
while (!executingBusinessDate.isAfter(cobBusinessDate)) { while (!DateUtils.isAfter(executingBusinessDate, cobBusinessDate)) {
JobParameterDTO jobParameterDTO = new JobParameterDTO(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME, JobParameterDTO jobParameterDTO = new JobParameterDTO(LoanCOBConstant.BUSINESS_DATE_PARAMETER_NAME,
executingBusinessDate.format(DateTimeFormatter.ISO_DATE)); executingBusinessDate.format(DateTimeFormatter.ISO_DATE));
JobParameterDTO jobParameterCatchUpDTO = new JobParameterDTO(LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME, "true"); JobParameterDTO jobParameterCatchUpDTO = new JobParameterDTO(LoanCOBConstant.IS_CATCH_UP_PARAMETER_NAME, "true");
@@ -51,6 +51,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException; import org.apache.fineract.infrastructure.core.exception.PlatformInternalServerException;
import org.apache.fineract.infrastructure.core.exception.PlatformRequestBodyItemLimitValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformRequestBodyItemLimitValidationException;
import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper; import org.apache.fineract.infrastructure.core.serialization.GoogleGsonSerializerHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil; import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO; import org.apache.fineract.infrastructure.jobs.data.JobParameterDTO;
import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository; import org.apache.fineract.infrastructure.jobs.domain.CustomJobParameterRepository;
@@ -112,7 +113,7 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
List<LoanIdAndLastClosedBusinessDate> loansToBeProcessed = getLoansToBeProcessed(loanIds, cobBusinessDate); List<LoanIdAndLastClosedBusinessDate> loansToBeProcessed = getLoansToBeProcessed(loanIds, cobBusinessDate);
LocalDate executingBusinessDate = getOldestCOBBusinessDate(loansToBeProcessed).plusDays(1); LocalDate executingBusinessDate = getOldestCOBBusinessDate(loansToBeProcessed).plusDays(1);
if (!loansToBeProcessed.isEmpty()) { if (!loansToBeProcessed.isEmpty()) {
while (!executingBusinessDate.isAfter(cobBusinessDate)) { while (!DateUtils.isAfter(executingBusinessDate, cobBusinessDate)) {
execute(getLoanIdsToBeProcessed(loansToBeProcessed, executingBusinessDate), jobName, executingBusinessDate); execute(getLoanIdsToBeProcessed(loansToBeProcessed, executingBusinessDate), jobName, executingBusinessDate);
executingBusinessDate = executingBusinessDate.plusDays(1); executingBusinessDate = executingBusinessDate.plusDays(1);
} }
@@ -123,7 +124,7 @@ public class InlineLoanCOBExecutorServiceImpl implements InlineExecutorService<L
List<Long> loanIdsToBeProcessed = new ArrayList<>(); List<Long> loanIdsToBeProcessed = new ArrayList<>();
loansToBeProcessed.forEach(loan -> { loansToBeProcessed.forEach(loan -> {
if (loan.getLastClosedBusinessDate() != null) { if (loan.getLastClosedBusinessDate() != null) {
if (loan.getLastClosedBusinessDate().isBefore(executingBusinessDate)) { if (DateUtils.isBefore(loan.getLastClosedBusinessDate(), executingBusinessDate)) {
loanIdsToBeProcessed.add(loan.getId()); loanIdsToBeProcessed.add(loan.getId());
} }
} else { } else {
@@ -73,7 +73,7 @@ public class ImportDocument extends AbstractPersistableCustom {
final Boolean completed = Boolean.FALSE; final Boolean completed = Boolean.FALSE;
final Integer successCount = 0; final Integer successCount = 0;
final Integer failureCount = 0; final Integer failureCount = 0;
final LocalDateTime endTime = LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()); final LocalDateTime endTime = DateUtils.getLocalDateTimeOfTenant();
return new ImportDocument(document, importTime, endTime, completed, entityType, createdBy, totalRecords, successCount, return new ImportDocument(document, importTime, endTime, completed, entityType, createdBy, totalRecords, successCount,
failureCount); failureCount);
@@ -145,8 +145,7 @@ public final class ImportHandlerUtils {
return null; return null;
} }
LocalDate localDate = LocalDate.ofInstant(c.getDateCellValue().toInstant(), DateUtils.getDateTimeZoneOfTenant()); return LocalDate.ofInstant(c.getDateCellValue().toInstant(), DateUtils.getDateTimeZoneOfTenant());
return localDate;
} }
public static Boolean readAsBoolean(int colIndex, Row row) { public static Boolean readAsBoolean(int colIndex, Row row) {
@@ -101,8 +101,7 @@ public class SharedProductsSheetPopulator extends AbstractWorkbookPopulator {
if (marketDataSet != null && !marketDataSet.isEmpty()) { if (marketDataSet != null && !marketDataSet.isEmpty()) {
LocalDate currentDate = DateUtils.getBusinessLocalDate(); LocalDate currentDate = DateUtils.getBusinessLocalDate();
for (ShareProductMarketPriceData data : marketDataSet) { for (ShareProductMarketPriceData data : marketDataSet) {
LocalDate futureDate = data.getFromDate(); if (DateUtils.isBefore(data.getFromDate(), currentDate)) {
if (currentDate.isAfter(futureDate)) {
marketValue = data.getShareValue(); marketValue = data.getShareValue();
} }
} }
@@ -160,7 +160,7 @@ public class EmailCampaign extends AbstractPersistableCustom {
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
LocalDateTime recurrenceStartDate = LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()); LocalDateTime recurrenceStartDate = DateUtils.getLocalDateTimeOfTenant();
if (EmailCampaignType.fromInt(campaignType.intValue()).isSchedule()) { if (EmailCampaignType.fromInt(campaignType.intValue()).isSchedule()) {
if (command.hasParameter(EmailCampaignValidator.recurrenceStartDate)) { if (command.hasParameter(EmailCampaignValidator.recurrenceStartDate)) {
recurrenceStartDate = LocalDateTime.parse(command.stringValueOfParameterNamed(EmailCampaignValidator.recurrenceStartDate), recurrenceStartDate = LocalDateTime.parse(command.stringValueOfParameterNamed(EmailCampaignValidator.recurrenceStartDate),
@@ -178,7 +178,6 @@ public class EmailCampaign extends AbstractPersistableCustom {
} }
public Map<String, Object> update(JsonCommand command) { public Map<String, Object> update(JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(5); final Map<String, Object> actualChanges = new LinkedHashMap<>(5);
if (command.isChangeInStringParameterNamed(EmailCampaignValidator.campaignName, this.campaignName)) { if (command.isChangeInStringParameterNamed(EmailCampaignValidator.campaignName, this.campaignName)) {
@@ -229,7 +228,6 @@ public class EmailCampaign extends AbstractPersistableCustom {
} }
public void activate(final AppUser currentUser, final DateTimeFormatter formatter, final LocalDate activationLocalDate) { public void activate(final AppUser currentUser, final DateTimeFormatter formatter, final LocalDate activationLocalDate) {
if (isActive()) { if (isActive()) {
// handle errors if already activated // handle errors if already activated
final String defaultUserMessage = "Cannot activate campaign. Campaign is already active."; final String defaultUserMessage = "Cannot activate campaign. Campaign is already active.";
@@ -271,14 +269,13 @@ public class EmailCampaign extends AbstractPersistableCustom {
} }
public void reactivate(final AppUser currentUser, final DateTimeFormatter dateTimeFormat, final LocalDate reactivateLocalDate) { public void reactivate(final AppUser currentUser, final DateTimeFormatter dateTimeFormat, final LocalDate reactivateLocalDate) {
if (!isClosed()) { if (!isClosed()) {
// handle errors if already activated // handle errors if already activated
final String defaultUserMessage = "Cannot reactivate campaign. Campaign must be in closed state."; final String defaultUserMessage = "Cannot reactivate campaign. Campaign must be in closed state.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.must.be.closed", defaultUserMessage, final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.must.be.closed", defaultUserMessage,
EmailCampaignValidator.statusParamName, EmailCampaignStatus.fromInt(this.status).getCode()); EmailCampaignValidator.statusParamName, EmailCampaignStatus.fromInt(this.status).getCode());
final List<ApiParameterError> dataValidationErrors = new ArrayList<ApiParameterError>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
dataValidationErrors.add(error); dataValidationErrors.add(error);
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -352,55 +349,45 @@ public class EmailCampaign extends AbstractPersistableCustom {
} }
private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) { private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) {
if (getSubmittedOnDate() != null && DateUtils.isDateInTheFuture(getSubmittedOnDate())) {
if (getSubmittedOnDate() != null && isDateInTheFuture(getSubmittedOnDate())) {
final String defaultUserMessage = "submitted date cannot be in the future."; final String defaultUserMessage = "submitted date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getApprovedOnDate() != null && DateUtils.isAfter(getSubmittedOnDate(), getApprovedOnDate())) {
if (getApprovedOnDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getApprovedOnDate())) {
final String defaultUserMessage = "submitted date cannot be after the activation date"; final String defaultUserMessage = "submitted date cannot be after the activation date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.after.activation.date", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.after.activation.date",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (DateUtils.isDateInTheFuture(getApprovedOnDate())) {
if (getApprovedOnDate() != null && isDateInTheFuture(getApprovedOnDate())) {
final String defaultUserMessage = "Activation date cannot be in the future."; final String defaultUserMessage = "Activation date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.activationDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.activationDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.activationDateParamName, getApprovedOnDate()); defaultUserMessage, EmailCampaignValidator.activationDateParamName, getApprovedOnDate());
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
} }
private void validateReactivationDate(final List<ApiParameterError> dataValidationErrors) { private void validateReactivationDate(final List<ApiParameterError> dataValidationErrors) {
if (getApprovedOnDate() != null && isDateInTheFuture(getApprovedOnDate())) { if (DateUtils.isDateInTheFuture(getApprovedOnDate())) {
final String defaultUserMessage = "Activation date cannot be in the future."; final String defaultUserMessage = "Activation date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.activationDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.activationDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.activationDateParamName, getApprovedOnDate()); defaultUserMessage, EmailCampaignValidator.activationDateParamName, getApprovedOnDate());
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getApprovedOnDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getApprovedOnDate())) { if (getApprovedOnDate() != null && DateUtils.isAfter(getSubmittedOnDate(), getApprovedOnDate())) {
final String defaultUserMessage = "submitted date cannot be after the activation date"; final String defaultUserMessage = "submitted date cannot be after the activation date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.after.activation.date", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.after.activation.date",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getSubmittedOnDate() != null && isDateInTheFuture(getSubmittedOnDate())) { if (DateUtils.isDateInTheFuture(getSubmittedOnDate())) {
final String defaultUserMessage = "submitted date cannot be in the future."; final String defaultUserMessage = "submitted date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, EmailCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
@@ -411,7 +398,7 @@ public class EmailCampaign extends AbstractPersistableCustom {
} }
private void validateClosureDate(final List<ApiParameterError> dataValidationErrors) { private void validateClosureDate(final List<ApiParameterError> dataValidationErrors) {
if (getClosureDate() != null && isDateInTheFuture(getClosureDate())) { if (getClosureDate() != null && DateUtils.isDateInTheFuture(getClosureDate())) {
final String defaultUserMessage = "closure date cannot be in the future."; final String defaultUserMessage = "closure date cannot be in the future.";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.closureDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.closureDate.in.the.future",
defaultUserMessage, EmailCampaignValidator.closureDateParamName, this.closureDate); defaultUserMessage, EmailCampaignValidator.closureDateParamName, this.closureDate);
@@ -419,9 +406,4 @@ public class EmailCampaign extends AbstractPersistableCustom {
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
} }
private boolean isDateInTheFuture(final LocalDate localDate) {
return localDate.isAfter(DateUtils.getBusinessLocalDate());
}
} }
@@ -31,7 +31,6 @@ import java.io.StringReader;
import java.io.StringWriter; import java.io.StringWriter;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.util.ArrayList; import java.util.ArrayList;
@@ -56,11 +55,9 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.api.JsonQuery; import org.apache.fineract.infrastructure.core.api.JsonQuery;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData; import org.apache.fineract.infrastructure.dataqueries.data.GenericResultsetData;
import org.apache.fineract.infrastructure.dataqueries.domain.Report; import org.apache.fineract.infrastructure.dataqueries.domain.Report;
import org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterUsage; import org.apache.fineract.infrastructure.dataqueries.domain.ReportParameterUsage;
@@ -98,7 +95,6 @@ public class EmailCampaignWritePlatformCommandHandlerImpl implements EmailCampai
@Transactional @Transactional
@Override @Override
public CommandProcessingResult create(JsonCommand command) { public CommandProcessingResult create(JsonCommand command) {
final AppUser currentUser = this.context.authenticatedUser(); final AppUser currentUser = this.context.authenticatedUser();
this.emailCampaignValidator.validateCreate(command.json()); this.emailCampaignValidator.validateCreate(command.json());
@@ -286,24 +282,21 @@ public class EmailCampaignWritePlatformCommandHandlerImpl implements EmailCampai
if (emailCampaign.isDirect()) { if (emailCampaign.isDirect()) {
insertDirectCampaignIntoEmailOutboundTable(emailCampaign.getParamValue(), emailCampaign.getEmailSubject(), insertDirectCampaignIntoEmailOutboundTable(emailCampaign.getParamValue(), emailCampaign.getEmailSubject(),
emailCampaign.getEmailMessage(), emailCampaign.getCampaignName(), emailCampaign.getId()); emailCampaign.getEmailMessage(), emailCampaign.getCampaignName(), emailCampaign.getId());
} else { } else if (emailCampaign.isSchedule()) {
if (emailCampaign.isSchedule()) { // if recurrence start date is in the past, calculate next trigger date, otherwise use recurrence start
// date as next trigger date when activating
/** LocalDateTime nextTriggerDateWithTime;
* if recurrence start date is in the future calculate next trigger date if not use recurrence start LocalDateTime recurrenceStartDate = emailCampaign.getRecurrenceStartDate();
* date us next trigger date when activating LocalDateTime tenantDateTime = DateUtils.getLocalDateTimeOfTenant();
*/ if (DateUtils.isBefore(recurrenceStartDate, tenantDateTime)) {
LocalDateTime nextTriggerDateWithTime; nextTriggerDateWithTime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), recurrenceStartDate,
if (emailCampaign.getRecurrenceStartDate().isBefore(tenantDateTime())) { tenantDateTime);
nextTriggerDateWithTime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), } else {
emailCampaign.getRecurrenceStartDate(), DateUtils.getLocalDateTimeOfTenant()); nextTriggerDateWithTime = recurrenceStartDate;
} else {
nextTriggerDateWithTime = emailCampaign.getRecurrenceStartDate();
}
emailCampaign.setNextTriggerDate(nextTriggerDateWithTime);
this.emailCampaignRepository.saveAndFlush(emailCampaign);
} }
emailCampaign.setNextTriggerDate(nextTriggerDateWithTime);
this.emailCampaignRepository.saveAndFlush(emailCampaign);
} }
/* /*
@@ -420,7 +413,6 @@ public class EmailCampaignWritePlatformCommandHandlerImpl implements EmailCampai
@Transactional @Transactional
@Override @Override
public CommandProcessingResult reactivateEmailCampaign(final Long campaignId, JsonCommand command) { public CommandProcessingResult reactivateEmailCampaign(final Long campaignId, JsonCommand command) {
this.emailCampaignValidator.validateActivation(command.json()); this.emailCampaignValidator.validateActivation(command.json());
final AppUser currentUser = this.context.authenticatedUser(); final AppUser currentUser = this.context.authenticatedUser();
@@ -433,23 +425,18 @@ public class EmailCampaignWritePlatformCommandHandlerImpl implements EmailCampai
final LocalDate reactivationDate = command.localDateValueOfParameterNamed("activationDate"); final LocalDate reactivationDate = command.localDateValueOfParameterNamed("activationDate");
emailCampaign.reactivate(currentUser, fmt, reactivationDate); emailCampaign.reactivate(currentUser, fmt, reactivationDate);
if (emailCampaign.isSchedule()) { if (emailCampaign.isSchedule()) {
// if recurrence start date is in the past, calculate next trigger date, otherwise use recurrence start date
/** // as next trigger date when activating
* if recurrence start date is in the future calculate next trigger date if not use recurrence start date us
* next trigger date when activating
*/
LocalDateTime nextTriggerDate = null; LocalDateTime nextTriggerDate = null;
if (emailCampaign.getRecurrenceStartDate().isBefore(tenantDateTime())) { LocalDateTime tenantDateTime = DateUtils.getLocalDateTimeOfTenant();
nextTriggerDate = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), emailCampaign.getRecurrenceStartDate(), LocalDateTime recurrenceStartDate = emailCampaign.getRecurrenceStartDate();
DateUtils.getLocalDateTimeOfTenant()); if (DateUtils.isBefore(recurrenceStartDate, tenantDateTime)) {
nextTriggerDate = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), recurrenceStartDate, tenantDateTime);
} else { } else {
nextTriggerDate = emailCampaign.getRecurrenceStartDate(); nextTriggerDate = recurrenceStartDate;
} }
// to get time of tenant final String dateString = nextTriggerDate.toString() + " " + recurrenceStartDate.getHour() + ":"
final LocalDateTime getTime = emailCampaign.getRecurrenceStartDate(); + recurrenceStartDate.getMinute() + ":" + recurrenceStartDate.getSecond();
final String dateString = nextTriggerDate.toString() + " " + getTime.getHour() + ":" + getTime.getMinute() + ":"
+ getTime.getSecond();
final DateTimeFormatter simpleDateFormat = new DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient() final DateTimeFormatter simpleDateFormat = new DateTimeFormatterBuilder().parseCaseInsensitive().parseLenient()
.appendPattern("yyyy-MM-dd HH:mm:ss").toFormatter(); .appendPattern("yyyy-MM-dd HH:mm:ss").toFormatter();
final LocalDateTime nextTriggerDateWithTime = LocalDateTime.parse(dateString, simpleDateFormat); final LocalDateTime nextTriggerDateWithTime = LocalDateTime.parse(dateString, simpleDateFormat);
@@ -470,17 +457,4 @@ public class EmailCampaignWritePlatformCommandHandlerImpl implements EmailCampai
throw new PlatformDataIntegrityException("error.msg.email.campaign.unknown.data.integrity.issue", throw new PlatformDataIntegrityException("error.msg.email.campaign.unknown.data.integrity.issue",
"Unknown data integrity issue with resource: " + realCause.getMessage()); "Unknown data integrity issue with resource: " + realCause.getMessage());
} }
private LocalDateTime tenantDateTime() {
LocalDateTime today = LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant());
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
if (tenant != null) {
final ZoneId zone = ZoneId.of(tenant.getTimezoneId());
if (zone != null) {
today = LocalDateTime.now(zone);
}
}
return today;
}
} }
@@ -1,41 +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.infrastructure.campaigns.jobs;
import java.time.LocalDateTime;
import java.time.ZoneId;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
public final class TenantDateTimeUtil {
private TenantDateTimeUtil() {}
public static LocalDateTime tenantDateTime() {
LocalDateTime today = LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant());
final FineractPlatformTenant tenant = ThreadLocalContextUtil.getTenant();
if (tenant != null) {
final ZoneId zone = ZoneId.of(tenant.getTimezoneId());
today = LocalDateTime.now(zone);
}
return today;
}
}
@@ -80,7 +80,7 @@ public class ExecuteReportMailingJobsTasklet implements Tasklet {
final LocalDateTime localDateTimeOftenant = DateUtils.getLocalDateTimeOfTenant(); final LocalDateTime localDateTimeOftenant = DateUtils.getLocalDateTimeOfTenant();
final LocalDateTime nextRunDateTime = reportMailingJob.getNextRunDateTime(); final LocalDateTime nextRunDateTime = reportMailingJob.getNextRunDateTime();
if (nextRunDateTime != null && nextRunDateTime.isBefore(localDateTimeOftenant)) { if (nextRunDateTime != null && DateUtils.isBefore(nextRunDateTime, localDateTimeOftenant)) {
final ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat = ReportMailingJobEmailAttachmentFileFormat final ReportMailingJobEmailAttachmentFileFormat emailAttachmentFileFormat = ReportMailingJobEmailAttachmentFileFormat
.newInstance(reportMailingJob.getEmailAttachmentFileFormat()); .newInstance(reportMailingJob.getEmailAttachmentFileFormat());
@@ -43,7 +43,6 @@ import org.apache.fineract.infrastructure.campaigns.email.domain.EmailMessageRep
import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignNotFound; import org.apache.fineract.infrastructure.campaigns.email.exception.EmailCampaignNotFound;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignReadPlatformService; import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignReadPlatformService;
import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService; import org.apache.fineract.infrastructure.campaigns.email.service.EmailCampaignWritePlatformService;
import org.apache.fineract.infrastructure.campaigns.jobs.TenantDateTimeUtil;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.calendar.service.CalendarUtils; import org.apache.fineract.portfolio.calendar.service.CalendarUtils;
import org.apache.fineract.portfolio.client.domain.Client; import org.apache.fineract.portfolio.client.domain.Client;
@@ -69,11 +68,11 @@ public class UpdateEmailOutboundWithCampaignMessageTasklet implements Tasklet {
.retrieveAllScheduleActiveCampaign(); .retrieveAllScheduleActiveCampaign();
if (emailCampaignDataCollection != null) { if (emailCampaignDataCollection != null) {
for (EmailCampaignData emailCampaignData : emailCampaignDataCollection) { for (EmailCampaignData emailCampaignData : emailCampaignDataCollection) {
LocalDateTime tenantDateNow = TenantDateTimeUtil.tenantDateTime(); LocalDateTime tenantDateNow = DateUtils.getLocalDateTimeOfTenant();
LocalDateTime nextTriggerDate = emailCampaignData.getNextTriggerDate().toLocalDateTime(); LocalDateTime nextTriggerDate = emailCampaignData.getNextTriggerDate().toLocalDateTime();
log.debug("tenant time {} trigger time {}", tenantDateNow, nextTriggerDate); log.debug("tenant time {} trigger time {}", tenantDateNow, nextTriggerDate);
if (nextTriggerDate.isBefore(tenantDateNow)) { if (DateUtils.isBefore(nextTriggerDate, tenantDateNow)) {
insertDirectCampaignIntoEmailOutboundTable(emailCampaignData.getParamValue(), emailCampaignData.getEmailSubject(), insertDirectCampaignIntoEmailOutboundTable(emailCampaignData.getParamValue(), emailCampaignData.getEmailSubject(),
emailCampaignData.getEmailMessage(), emailCampaignData.getCampaignName(), emailCampaignData.getId()); emailCampaignData.getEmailMessage(), emailCampaignData.getCampaignName(), emailCampaignData.getId());
updateTriggerDates(emailCampaignData.getId()); updateTriggerDates(emailCampaignData.getId());
@@ -115,13 +114,12 @@ public class UpdateEmailOutboundWithCampaignMessageTasklet implements Tasklet {
.orElseThrow(() -> new EmailCampaignNotFound(campaignId)); .orElseThrow(() -> new EmailCampaignNotFound(campaignId));
LocalDateTime nextTriggerDate = emailCampaign.getNextTriggerDate(); LocalDateTime nextTriggerDate = emailCampaign.getNextTriggerDate();
emailCampaign.setLastTriggerDate(nextTriggerDate); emailCampaign.setLastTriggerDate(nextTriggerDate);
LocalDateTime newTriggerDateWithTime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), LocalDateTime newTriggerDateWithTime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), nextTriggerDate,
emailCampaign.getNextTriggerDate(), nextTriggerDate); nextTriggerDate);
if (newTriggerDateWithTime.isBefore(DateUtils.getLocalDateTimeOfTenant())) { LocalDateTime tenantDateTime = DateUtils.getLocalDateTimeOfTenant();
newTriggerDateWithTime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), emailCampaign.getNextTriggerDate(), if (DateUtils.isBefore(newTriggerDateWithTime, tenantDateTime)) {
DateUtils.getLocalDateTimeOfTenant()); newTriggerDateWithTime = CalendarUtils.getNextRecurringDate(emailCampaign.getRecurrence(), nextTriggerDate, tenantDateTime);
} }
emailCampaign.setNextTriggerDate(newTriggerDateWithTime); emailCampaign.setNextTriggerDate(newTriggerDateWithTime);
emailCampaignRepository.saveAndFlush(emailCampaign); emailCampaignRepository.saveAndFlush(emailCampaign);
} }
@@ -23,7 +23,6 @@ import java.util.Collection;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.collections4.CollectionUtils;
import org.apache.fineract.infrastructure.campaigns.jobs.TenantDateTimeUtil;
import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignStatus; import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignStatus;
import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType; import org.apache.fineract.infrastructure.campaigns.sms.constants.SmsCampaignTriggerType;
import org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaign; import org.apache.fineract.infrastructure.campaigns.sms.domain.SmsCampaign;
@@ -51,12 +50,12 @@ public class UpdateSmsOutboundWithCampaignMessageTasklet implements Tasklet {
.findByTriggerTypeAndStatus(SmsCampaignTriggerType.SCHEDULE.getValue(), SmsCampaignStatus.ACTIVE.getValue()); .findByTriggerTypeAndStatus(SmsCampaignTriggerType.SCHEDULE.getValue(), SmsCampaignStatus.ACTIVE.getValue());
if (!CollectionUtils.isEmpty(smsCampaignDataCollection)) { if (!CollectionUtils.isEmpty(smsCampaignDataCollection)) {
for (SmsCampaign smsCampaign : smsCampaignDataCollection) { for (SmsCampaign smsCampaign : smsCampaignDataCollection) {
LocalDateTime tenantDateNow = TenantDateTimeUtil.tenantDateTime(); LocalDateTime tenantDateNow = DateUtils.getLocalDateTimeOfTenant();
LocalDateTime nextTriggerDate = smsCampaign.getNextTriggerDate(); LocalDateTime nextTriggerDate = smsCampaign.getNextTriggerDate();
log.debug("tenant time {} trigger time {} {}", tenantDateNow, nextTriggerDate, log.debug("tenant time {} trigger time {} {}", tenantDateNow, nextTriggerDate,
JobName.UPDATE_SMS_OUTBOUND_WITH_CAMPAIGN_MESSAGE.name()); JobName.UPDATE_SMS_OUTBOUND_WITH_CAMPAIGN_MESSAGE.name());
if (nextTriggerDate.isBefore(tenantDateNow)) { if (DateUtils.isBefore(nextTriggerDate, tenantDateNow)) {
smsCampaignWritePlatformService.insertDirectCampaignIntoSmsOutboundTable(smsCampaign); smsCampaignWritePlatformService.insertDirectCampaignIntoSmsOutboundTable(smsCampaign);
updateTriggerDates(smsCampaign.getId()); updateTriggerDates(smsCampaign.getId());
} }
@@ -70,11 +69,10 @@ public class UpdateSmsOutboundWithCampaignMessageTasklet implements Tasklet {
.orElseThrow(() -> new SmsCampaignNotFound(campaignId)); .orElseThrow(() -> new SmsCampaignNotFound(campaignId));
LocalDateTime nextTriggerDate = smsCampaign.getNextTriggerDate(); LocalDateTime nextTriggerDate = smsCampaign.getNextTriggerDate();
smsCampaign.setLastTriggerDate(nextTriggerDate); smsCampaign.setLastTriggerDate(nextTriggerDate);
LocalDateTime nextRuntime = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), smsCampaign.getNextTriggerDate(), LocalDateTime nextRuntime = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), nextTriggerDate, nextTriggerDate);
nextTriggerDate); LocalDateTime tenantDateTime = DateUtils.getLocalDateTimeOfTenant();
if (nextRuntime.isBefore(DateUtils.getLocalDateTimeOfTenant())) { if (DateUtils.isBefore(nextRuntime, tenantDateTime)) {
nextRuntime = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), smsCampaign.getNextTriggerDate(), nextRuntime = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), nextTriggerDate, tenantDateTime);
DateUtils.getLocalDateTimeOfTenant());
} }
smsCampaign.setNextTriggerDate(nextRuntime); smsCampaign.setNextTriggerDate(nextRuntime);
@@ -18,10 +18,10 @@
*/ */
package org.apache.fineract.infrastructure.core.domain; package org.apache.fineract.infrastructure.core.domain;
import static org.apache.fineract.useradministration.service.AppUserConstants.ADMIN_USER_ID;
import java.util.Optional; import java.util.Optional;
import org.apache.fineract.useradministration.domain.AppUser; import org.apache.fineract.useradministration.domain.AppUser;
import org.apache.fineract.useradministration.domain.AppUserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.AuditorAware; import org.springframework.data.domain.AuditorAware;
import org.springframework.security.core.Authentication; import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContext;
@@ -29,9 +29,6 @@ import org.springframework.security.core.context.SecurityContextHolder;
public class AuditorAwareImpl implements AuditorAware<Long> { public class AuditorAwareImpl implements AuditorAware<Long> {
@Autowired
private AppUserRepository userRepository;
@Override @Override
public Optional<Long> getCurrentAuditor() { public Optional<Long> getCurrentAuditor() {
Optional<Long> currentUserId; Optional<Long> currentUserId;
@@ -50,6 +47,6 @@ public class AuditorAwareImpl implements AuditorAware<Long> {
} }
private Optional<Long> retrieveSuperUser() { private Optional<Long> retrieveSuperUser() {
return Optional.of(1L); return Optional.of(ADMIN_USER_ID); // TODO change to SYSTEM_USER_ID and add rights
} }
} }
@@ -420,7 +420,7 @@ public class ThitsaWorksCreditBureauIntegrationWritePlatformServiceImpl implemen
LocalDate current = DateUtils.getLocalDateOfTenant(); LocalDate current = DateUtils.getLocalDateOfTenant();
LocalDate getExpiryDate = creditBureauToken.getExpires(); LocalDate getExpiryDate = creditBureauToken.getExpires();
if (getExpiryDate.isBefore(current)) { if (DateUtils.isBefore(getExpiryDate, current)) {
this.tokenRepositoryWrapper.delete(creditBureauToken); this.tokenRepositoryWrapper.delete(creditBureauToken);
creditBureauToken = null; creditBureauToken = null;
} }
@@ -33,6 +33,7 @@ import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.entityaccess.api.FineractEntityApiResourceConstants; import org.apache.fineract.infrastructure.entityaccess.api.FineractEntityApiResourceConstants;
import org.apache.fineract.infrastructure.entityaccess.exception.FineractEntityToEntityMappingDateException; import org.apache.fineract.infrastructure.entityaccess.exception.FineractEntityToEntityMappingDateException;
@@ -69,7 +70,6 @@ public class FineractEntityToEntityMapping extends AbstractPersistableCustom {
} }
public Map<String, Object> updateMap(final JsonCommand command) { public Map<String, Object> updateMap(final JsonCommand command) {
final Map<String, Object> actualChanges = new LinkedHashMap<>(9); final Map<String, Object> actualChanges = new LinkedHashMap<>(9);
if (command.isChangeInLongParameterNamed(FineractEntityApiResourceConstants.fromEnityType, this.fromId)) { if (command.isChangeInLongParameterNamed(FineractEntityApiResourceConstants.fromEnityType, this.fromId)) {
@@ -95,10 +95,8 @@ public class FineractEntityToEntityMapping extends AbstractPersistableCustom {
actualChanges.put(FineractEntityApiResourceConstants.endDate, valueAsInput); actualChanges.put(FineractEntityApiResourceConstants.endDate, valueAsInput);
this.endDate = command.localDateValueOfParameterNamed(FineractEntityApiResourceConstants.endDate); this.endDate = command.localDateValueOfParameterNamed(FineractEntityApiResourceConstants.endDate);
} }
if (startDate != null && endDate != null) { if (endDate != null && DateUtils.isBefore(endDate, startDate)) {
if (endDate.isBefore(startDate)) { throw new FineractEntityToEntityMappingDateException(startDate.toString(), endDate.toString());
throw new FineractEntityToEntityMappingDateException(startDate.toString(), endDate.toString());
}
} }
return actualChanges; return actualChanges;
@@ -27,6 +27,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.entityaccess.api.FineractEntityApiResourceConstants; import org.apache.fineract.infrastructure.entityaccess.api.FineractEntityApiResourceConstants;
import org.apache.fineract.infrastructure.entityaccess.data.FineractEntityDataValidator; import org.apache.fineract.infrastructure.entityaccess.data.FineractEntityDataValidator;
import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccess; import org.apache.fineract.infrastructure.entityaccess.domain.FineractEntityAccess;
@@ -86,9 +87,7 @@ public class FineractEntityAccessWriteServiceImpl implements FineractEntityAcces
@Override @Override
@Transactional @Transactional
public CommandProcessingResult createEntityToEntityMapping(Long relId, JsonCommand command) { public CommandProcessingResult createEntityToEntityMapping(Long relId, JsonCommand command) {
try { try {
this.fromApiJsonDeserializer.validateForCreate(command.json()); this.fromApiJsonDeserializer.validateForCreate(command.json());
final FineractEntityRelation mapId = this.fineractEntityRelationRepositoryWrapper.findOneWithNotFoundDetection(relId); final FineractEntityRelation mapId = this.fineractEntityRelationRepositoryWrapper.findOneWithNotFoundDetection(relId);
@@ -99,10 +98,8 @@ public class FineractEntityAccessWriteServiceImpl implements FineractEntityAcces
final LocalDate endDate = command.localDateValueOfParameterNamed(FineractEntityApiResourceConstants.endDate); final LocalDate endDate = command.localDateValueOfParameterNamed(FineractEntityApiResourceConstants.endDate);
fromApiJsonDeserializer.checkForEntity(relId.toString(), fromId, toId); fromApiJsonDeserializer.checkForEntity(relId.toString(), fromId, toId);
if (startDate != null && endDate != null) { if (endDate != null && DateUtils.isBefore(endDate, startDate)) {
if (endDate.isBefore(startDate)) { throw new FineractEntityToEntityMappingDateException(startDate.toString(), endDate.toString());
throw new FineractEntityToEntityMappingDateException(startDate.toString(), endDate.toString());
}
} }
final FineractEntityToEntityMapping newMap = FineractEntityToEntityMapping.newMap(mapId, fromId, toId, startDate, endDate); final FineractEntityToEntityMapping newMap = FineractEntityToEntityMapping.newMap(mapId, fromId, toId, startDate, endDate);
@@ -123,9 +120,7 @@ public class FineractEntityAccessWriteServiceImpl implements FineractEntityAcces
@Override @Override
@Transactional @Transactional
public CommandProcessingResult updateEntityToEntityMapping(Long mapId, JsonCommand command) { public CommandProcessingResult updateEntityToEntityMapping(Long mapId, JsonCommand command) {
try { try {
this.fromApiJsonDeserializer.validateForUpdate(command.json()); this.fromApiJsonDeserializer.validateForUpdate(command.json());
final FineractEntityToEntityMapping mapForUpdate = this.fineractEntityToEntityMappingRepositoryWrapper final FineractEntityToEntityMapping mapForUpdate = this.fineractEntityToEntityMappingRepositoryWrapper
@@ -63,7 +63,7 @@ public class UpdateNpaTasklet implements Tasklet {
resetNPASqlBuilder.append("set is_npa = false").append(", last_modified_by = ?, last_modified_on_utc = ? ").append(" FROM ") resetNPASqlBuilder.append("set is_npa = false").append(", last_modified_by = ?, last_modified_on_utc = ? ").append(" FROM ")
.append(fromPart).append(wherePart); .append(fromPart).append(wherePart);
} }
jdbcTemplate.update(resetNPASqlBuilder.toString(), user.getId(), DateUtils.getOffsetDateTimeOfTenantWithMostPrecision()); jdbcTemplate.update(resetNPASqlBuilder.toString(), user.getId(), DateUtils.getAuditOffsetDateTime());
final StringBuilder updateSqlBuilder = new StringBuilder(900); final StringBuilder updateSqlBuilder = new StringBuilder(900);
@@ -82,8 +82,7 @@ public class UpdateNpaTasklet implements Tasklet {
.append(fromPart).append(wherePart); .append(fromPart).append(wherePart);
} }
final int result = jdbcTemplate.update(updateSqlBuilder.toString(), user.getId(), final int result = jdbcTemplate.update(updateSqlBuilder.toString(), user.getId(), DateUtils.getAuditOffsetDateTime());
DateUtils.getOffsetDateTimeOfTenantWithMostPrecision());
log.debug("{}: Records affected by updateNPA: {}", ThreadLocalContextUtil.getTenant().getName(), result); log.debug("{}: Records affected by updateNPA: {}", ThreadLocalContextUtil.getTenant().getName(), result);
return RepeatStatus.FINISHED; return RepeatStatus.FINISHED;
@@ -67,19 +67,12 @@ public class TFAccessToken extends AbstractPersistableCustom {
} }
public boolean isValid() { public boolean isValid() {
return this.enabled && isDateInTheFuture(getValidTo()) && isDateInThePast(getValidFrom()); // valid_from is in the past inclusive, valid_to is in the future exclusive
return this.enabled && !DateUtils.isAfterTenantDateTime(getValidFrom()) && DateUtils.isAfterTenantDateTime(getValidTo());
} }
public AccessTokenData toTokenData() { public AccessTokenData toTokenData() {
return new AccessTokenData().setToken(this.token).setValidFrom(getValidFrom().atZone(DateUtils.getDateTimeZoneOfTenant())) return new AccessTokenData().setToken(this.token).setValidFrom(getValidFrom().atZone(DateUtils.getDateTimeZoneOfTenant()))
.setValidTo(getValidTo().atZone(DateUtils.getDateTimeZoneOfTenant())); .setValidTo(getValidTo().atZone(DateUtils.getDateTimeZoneOfTenant()));
} }
private boolean isDateInTheFuture(LocalDateTime dateTime) {
return dateTime.isAfter(DateUtils.getLocalDateTimeOfTenant());
}
private boolean isDateInThePast(LocalDateTime dateTime) {
return (dateTime.isBefore(DateUtils.getLocalDateTimeOfTenant()) || dateTime.isEqual(DateUtils.getLocalDateTimeOfTenant()));
}
} }
@@ -157,7 +157,7 @@ public class SpringSecurityPlatformSecurityContext implements PlatformSecurityCo
final LocalDate passwordExpirationDate = passWordLastUpdateDate.plusDays(passwordDurationDays); final LocalDate passwordExpirationDate = passWordLastUpdateDate.plusDays(passwordDurationDays);
if (DateUtils.getLocalDateOfTenant().isAfter(passwordExpirationDate)) { if (DateUtils.isBeforeTenantDate(passwordExpirationDate)) {
return true; return true;
} }
} }
@@ -23,6 +23,7 @@ import java.util.List;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResult; import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction; import org.apache.fineract.portfolio.savings.domain.SavingsAccountTransaction;
@@ -41,7 +42,7 @@ public class InteropTransactionsData extends CommandProcessingResult {
} }
List<InteropTransactionData> trans = account.getTransactions().stream().filter(filter).sorted((t1, t2) -> { List<InteropTransactionData> trans = account.getTransactions().stream().filter(filter).sorted((t1, t2) -> {
int i = t2.getDateOf().compareTo(t1.getDateOf()); int i = DateUtils.compare(t2.getDateOf(), t1.getDateOf());
return i != 0 ? i : Long.signum(t2.getId() - t1.getId()); return i != 0 ? i : Long.signum(t2.getId() - t1.getId());
}).map(InteropTransactionData::build).collect(Collectors.toList()); }).map(InteropTransactionData::build).collect(Collectors.toList());
return new InteropTransactionsData(account.getId(), trans); return new InteropTransactionsData(account.getId(), trans);
@@ -33,6 +33,7 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResult;
import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder; import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException; import org.apache.fineract.infrastructure.core.exception.PlatformDataIntegrityException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.holiday.api.HolidayApiConstants; import org.apache.fineract.organisation.holiday.api.HolidayApiConstants;
import org.apache.fineract.organisation.holiday.data.HolidayDataValidator; import org.apache.fineract.organisation.holiday.data.HolidayDataValidator;
@@ -206,18 +207,15 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit
} }
private void validateInputDates(final LocalDate fromDate, final LocalDate toDate, final LocalDate repaymentsRescheduledTo) { private void validateInputDates(final LocalDate fromDate, final LocalDate toDate, final LocalDate repaymentsRescheduledTo) {
String defaultUserMessage = ""; String defaultUserMessage = "";
if (toDate.isBefore(fromDate)) { if (DateUtils.isBefore(toDate, fromDate)) {
defaultUserMessage = "To Date date cannot be before the From Date."; defaultUserMessage = "To Date date cannot be before the From Date.";
throw new HolidayDateException("to.date.cannot.be.before.from.date", defaultUserMessage, fromDate.toString(), throw new HolidayDateException("to.date.cannot.be.before.from.date", defaultUserMessage, fromDate.toString(),
toDate.toString()); toDate.toString());
} }
if (repaymentsRescheduledTo != null) { if (repaymentsRescheduledTo != null) {
if ((repaymentsRescheduledTo.isEqual(fromDate) || repaymentsRescheduledTo.isEqual(toDate) if (!DateUtils.isBefore(repaymentsRescheduledTo, fromDate) && !DateUtils.isAfter(repaymentsRescheduledTo, toDate)) {
|| (repaymentsRescheduledTo.isAfter(fromDate) && repaymentsRescheduledTo.isBefore(toDate)))) {
defaultUserMessage = "Repayments rescheduled date should be before from date or after to date."; defaultUserMessage = "Repayments rescheduled date should be before from date or after to date.";
throw new HolidayDateException("repayments.rescheduled.date.should.be.before.from.date.or.after.to.date", throw new HolidayDateException("repayments.rescheduled.date.should.be.before.from.date.or.after.to.date",
defaultUserMessage, repaymentsRescheduledTo.toString()); defaultUserMessage, repaymentsRescheduledTo.toString());
@@ -235,13 +233,12 @@ public class HolidayWritePlatformServiceJpaRepositoryImpl implements HolidayWrit
// validate repaymentsRescheduledTo date // validate repaymentsRescheduledTo date
// 1. should be within a 30 days date range. // 1. should be within a 30 days date range.
// 2. Alternative date should not be an exist holiday.//TBD // 2. Alternative date should not be an exist holiday.//TBD
// 3. Holiday should not be on an repaymentsRescheduledTo date of // 3. Holiday should not be on an repaymentsRescheduledTo date of another holiday.//TBD
// another holiday.//TBD
// restricting repaymentsRescheduledTo date to be within 30 days // restricting repaymentsRescheduledTo date to be within 30 days range before or after from date and to
// range // date.
// before or after from date and to date. if (DateUtils.isBefore(repaymentsRescheduledTo, fromDate.minusDays(30))
if (repaymentsRescheduledTo.isBefore(fromDate.minusDays(30)) || repaymentsRescheduledTo.isAfter(toDate.plusDays(30))) { || DateUtils.isAfter(repaymentsRescheduledTo, toDate.plusDays(30))) {
defaultUserMessage = "Repayments Rescheduled to date must be within 30 days before or after from and to dates"; defaultUserMessage = "Repayments Rescheduled to date must be within 30 days before or after from and to dates";
throw new HolidayDateException("repayments.rescheduled.to.must.be.within.range", defaultUserMessage, fromDate.toString(), throw new HolidayDateException("repayments.rescheduled.to.must.be.within.range", defaultUserMessage, fromDate.toString(),
toDate.toString(), repaymentsRescheduledTo.toString()); toDate.toString(), repaymentsRescheduledTo.toString());
@@ -20,10 +20,10 @@ package org.apache.fineract.organisation.teller.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.OffsetDateTime;
import java.time.ZonedDateTime;
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.infrastructure.core.service.SearchParameters; import org.apache.fineract.infrastructure.core.service.SearchParameters;
import org.apache.fineract.organisation.teller.domain.Cashier; import org.apache.fineract.organisation.teller.domain.Cashier;
import org.apache.fineract.organisation.teller.domain.Teller; import org.apache.fineract.organisation.teller.domain.Teller;
@@ -54,7 +54,6 @@ public class CashierTransactionDataValidator {
} }
public void validateSettleCashAndCashOutTransactions(final Long cashierId, String currencyCode, final BigDecimal transactionAmount) { public void validateSettleCashAndCashOutTransactions(final Long cashierId, String currencyCode, final BigDecimal transactionAmount) {
final Integer offset = null; final Integer offset = null;
final Integer limit = null; final Integer limit = null;
final String orderBy = null; final String orderBy = null;
@@ -64,7 +63,7 @@ public class CashierTransactionDataValidator {
final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder); final SearchParameters searchParameters = SearchParameters.forPagination(offset, limit, orderBy, sortOrder);
final CashierTransactionsWithSummaryData cashierTxnWithSummary = this.tellerManagementReadPlatformService final CashierTransactionsWithSummaryData cashierTxnWithSummary = this.tellerManagementReadPlatformService
.retrieveCashierTransactionsWithSummary(cashierId, false, fromDate, toDate, currencyCode, searchParameters); .retrieveCashierTransactionsWithSummary(cashierId, false, fromDate, toDate, currencyCode, searchParameters);
if (cashierTxnWithSummary.getNetCash().subtract(transactionAmount).compareTo(BigDecimal.ZERO) < 0) { if (MathUtil.isGreaterThan(transactionAmount, cashierTxnWithSummary.getNetCash())) {
throw new CashierInsufficientAmountException(); throw new CashierInsufficientAmountException();
} }
} }
@@ -81,16 +80,12 @@ public class CashierTransactionDataValidator {
final LocalDate endDate = cashier.getEndDate(); final LocalDate endDate = cashier.getEndDate();
final LocalDate tellerFromDate = teller.getStartDate(); final LocalDate tellerFromDate = teller.getStartDate();
final LocalDate tellerEndDate = teller.getEndDate(); final LocalDate tellerEndDate = teller.getEndDate();
/** // to validate cashier date range in range of teller date range
* to validate cashier date range in range of teller date range if (DateUtils.isBefore(fromDate, tellerFromDate) || DateUtils.isBefore(endDate, tellerFromDate)
*/ || (tellerEndDate != null && (DateUtils.isAfter(fromDate, tellerEndDate) || DateUtils.isAfter(endDate, tellerEndDate)))) {
if (fromDate.isBefore(tellerFromDate) || endDate.isBefore(tellerFromDate)
|| (tellerEndDate != null && (fromDate.isAfter(tellerEndDate) || endDate.isAfter(tellerEndDate)))) {
throw new CashierDateRangeOutOfTellerDateRangeException(); throw new CashierDateRangeOutOfTellerDateRangeException();
} }
/** // to validate cashier has not been assigned for same duration
* to validate cashier has not been assigned for same duration
*/
String sql = "select count(*) from m_cashiers c where c.staff_id = " + staffId + " AND " + "(('" + fromDate String sql = "select count(*) from m_cashiers c where c.staff_id = " + staffId + " AND " + "(('" + fromDate
+ "' BETWEEN c.start_date AND c.end_date OR '" + endDate + "' BETWEEN c.start_date AND c.end_date )" + "' BETWEEN c.start_date AND c.end_date OR '" + endDate + "' BETWEEN c.start_date AND c.end_date )"
+ " OR ( c.start_date BETWEEN '" + fromDate + "' AND '" + endDate + "' OR c.end_date BETWEEN '" + fromDate + "' AND '" + " OR ( c.start_date BETWEEN '" + fromDate + "' AND '" + endDate + "' OR c.end_date BETWEEN '" + fromDate + "' AND '"
@@ -98,8 +93,8 @@ public class CashierTransactionDataValidator {
if (!cashier.getIsFullDay()) { if (!cashier.getIsFullDay()) {
String startTime = cashier.getStartTime(); String startTime = cashier.getStartTime();
String endTime = cashier.getEndTime(); String endTime = cashier.getEndTime();
sql = sql + " AND ( Time(c.start_time) BETWEEN TIME(?) and TIME('" + endTime + "') or Time(c.end_time) BETWEEN TIME('" sql = sql + " AND ( Time(c.start_time) BETWEEN TIME('" + startTime + "') and TIME('" + endTime
+ startTime + "') and TIME('" + endTime + "')) "; + "') or Time(c.end_time) BETWEEN TIME('" + startTime + "') and TIME('" + endTime + "')) ";
} }
int count = this.jdbcTemplate.queryForObject(sql, Integer.class); // NOSONAR int count = this.jdbcTemplate.queryForObject(sql, Integer.class); // NOSONAR
if (count > 0) { if (count > 0) {
@@ -108,13 +103,13 @@ public class CashierTransactionDataValidator {
} }
public void validateOnLoanDisbursal(AppUser user, String currencyCode, BigDecimal transactionAmount) { public void validateOnLoanDisbursal(AppUser user, String currencyCode, BigDecimal transactionAmount) {
LocalDateTime localDateTime = DateUtils.getLocalDateTimeOfTenant(); LocalDate tenantDate = DateUtils.getLocalDateOfTenant();
OffsetDateTime tenantDateTime = DateUtils.getOffsetDateTimeOfTenant();
if (user.getStaff() != null) { if (user.getStaff() != null) {
String sql = "select c.id from m_cashiers c where c.staff_id = " + user.getStaff().getId() + " AND " String sql = "select c.id from m_cashiers c where c.staff_id = " + user.getStaff().getId() + " AND (case when c.full_day then '"
+ " (case when c.full_day then '" + localDateTime.toLocalDate() + "' BETWEEN c.start_date AND c.end_date " + " else ('" + tenantDate + "' BETWEEN c.start_date AND c.end_date else ('" + tenantDate
+ localDateTime.toLocalDate() + "' BETWEEN c.start_date AND c.end_date and " + " TIME('" + "' BETWEEN c.start_date AND c.end_date and TIME('" + tenantDateTime
+ ZonedDateTime.of(localDateTime, DateUtils.getDateTimeZoneOfTenant()) + "') BETWEEN TIME(c.start_time) AND TIME(c.end_time)) end)";
+ "') BETWEEN TIME(c.start_time) AND TIME(c.end_time) ) end)";
try { try {
Long cashierId = this.jdbcTemplate.queryForObject(sql, Long.class); // NOSONAR Long cashierId = this.jdbcTemplate.queryForObject(sql, Long.class); // NOSONAR
validateSettleCashAndCashOutTransactions(cashierId, currencyCode, transactionAmount); validateSettleCashAndCashOutTransactions(cashierId, currencyCode, transactionAmount);
@@ -122,6 +117,5 @@ public class CashierTransactionDataValidator {
LOG.error("Problem occurred in validateOnLoanDisbursal function", e); LOG.error("Problem occurred in validateOnLoanDisbursal function", e);
} }
} }
} }
} }
@@ -35,6 +35,7 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.teller.exception.InvalidDateInputException; import org.apache.fineract.organisation.teller.exception.InvalidDateInputException;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -113,7 +114,7 @@ public final class TellerCommandFromApiJsonDeserializer {
final String status = this.fromApiJsonHelper.extractStringNamed(STATUS, element); final String status = this.fromApiJsonHelper.extractStringNamed(STATUS, element);
baseDataValidator.reset().parameter(STATUS).value(status).notBlank().notExceedingLengthOf(50); baseDataValidator.reset().parameter(STATUS).value(status).notBlank().notExceedingLengthOf(50);
if (endDate != null && endDate.isBefore(startDate)) { if (endDate != null && DateUtils.isBefore(endDate, startDate)) {
throw new InvalidDateInputException(startDate.toString(), endDate.toString()); throw new InvalidDateInputException(startDate.toString(), endDate.toString());
} }
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
@@ -75,12 +75,12 @@ public class ExecuteStandingInstructionsTasklet implements Tasklet {
LocalDate startDate = data.validFrom(); LocalDate startDate = data.validFrom();
if (frequencyType.isMonthly()) { if (frequencyType.isMonthly()) {
startDate = startDate.withDayOfMonth(data.recurrenceOnDay()); startDate = startDate.withDayOfMonth(data.recurrenceOnDay());
if (startDate.isBefore(data.validFrom())) { if (DateUtils.isBefore(startDate, data.validFrom())) {
startDate = startDate.plusMonths(1); startDate = startDate.plusMonths(1);
} }
} else if (frequencyType.isYearly()) { } else if (frequencyType.isYearly()) {
startDate = startDate.withDayOfMonth(data.recurrenceOnDay()).withMonth(data.recurrenceOnMonth()); startDate = startDate.withDayOfMonth(data.recurrenceOnDay()).withMonth(data.recurrenceOnMonth());
if (startDate.isBefore(data.validFrom())) { if (DateUtils.isBefore(startDate, data.validFrom())) {
startDate = startDate.plusYears(1); startDate = startDate.plusYears(1);
} }
} }
@@ -536,8 +536,7 @@ public class AccountTransfersWritePlatformServiceImpl implements AccountTransfer
BigDecimal overpaid = this.loanReadPlatformService.retrieveTotalPaidInAdvance(fromLoanAccountId).getPaidInAdvance(); BigDecimal overpaid = this.loanReadPlatformService.retrieveTotalPaidInAdvance(fromLoanAccountId).getPaidInAdvance();
final boolean backdatedTxnsAllowedTill = false; final boolean backdatedTxnsAllowedTill = false;
if (overpaid == null || overpaid.compareTo(BigDecimal.ZERO) == 0 ? Boolean.TRUE if (overpaid == null || overpaid.compareTo(BigDecimal.ZERO) == 0 || transactionAmount.floatValue() > overpaid.floatValue()) {
: Boolean.FALSE || transactionAmount.floatValue() > overpaid.floatValue()) {
if (overpaid == null) { if (overpaid == null) {
overpaid = BigDecimal.ZERO; overpaid = BigDecimal.ZERO;
} }
@@ -279,7 +279,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ
periodStartDate = fromDate; periodStartDate = fromDate;
} else { } else {
final LocalDate currentDate = DateUtils.getLocalDateOfTenant(); final LocalDate currentDate = DateUtils.getLocalDateOfTenant();
if (seedDate.isBefore(currentDate.minusYears(1))) { if (DateUtils.isBefore(seedDate, currentDate.minusYears(1))) {
periodStartDate = currentDate.minusYears(1); periodStartDate = currentDate.minusYears(1);
} else { } else {
periodStartDate = recurrenceStartDate; periodStartDate = recurrenceStartDate;
@@ -294,7 +294,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ
if (tillDate != null) { if (tillDate != null) {
if (endDate != null) { if (endDate != null) {
if (endDate.isAfter(tillDate)) { if (DateUtils.isAfter(endDate, tillDate)) {
// to retrieve meeting dates tillspecified date (tillDate) // to retrieve meeting dates tillspecified date (tillDate)
periodEndDate = tillDate; periodEndDate = tillDate;
} }
@@ -302,7 +302,7 @@ public class CalendarReadPlatformServiceImpl implements CalendarReadPlatformServ
// end date is null then fetch meeting dates tillDate // end date is null then fetch meeting dates tillDate
periodEndDate = tillDate; periodEndDate = tillDate;
} }
} else if (endDate == null || endDate.isAfter(currentDate.plusYears(1))) { } else if (endDate == null || DateUtils.isAfter(endDate, currentDate.plusYears(1))) {
periodEndDate = currentDate.plusYears(1); periodEndDate = currentDate.plusYears(1);
} }
return periodEndDate; return periodEndDate;
@@ -80,8 +80,8 @@ public class InternalClientInformationApiResource implements InitializingBean {
final Client client = clientRepositoryWrapper.findOneWithNotFoundDetection(clientId); final Client client = clientRepositoryWrapper.findOneWithNotFoundDetection(clientId);
Map<String, Object> auditFields = new HashMap<>( Map<String, Object> auditFields = new HashMap<>(
Map.of(CREATED_BY, client.getCreatedBy().orElse(null), CREATED_DATE, client.getCreatedDate().orElse(null), LAST_MODIFIED_BY, Map.of(CREATED_BY, client.getCreatedBy().orElse(null), CREATED_DATE, client.getCreatedDateTime(), LAST_MODIFIED_BY,
client.getLastModifiedBy().orElse(null), LAST_MODIFIED_DATE, client.getLastModifiedDate().orElse(null))); client.getLastModifiedBy().orElse(null), LAST_MODIFIED_DATE, client.getLastModifiedDateTime()));
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializer.serialize(settings, auditFields); return this.toApiJsonSerializer.serialize(settings, auditFields);
} }
@@ -36,6 +36,7 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.client.api.ClientApiConstants; import org.apache.fineract.portfolio.client.api.ClientApiConstants;
@Entity @Entity
@@ -116,8 +117,7 @@ public class ClientNonPerson extends AbstractPersistableCustom {
private void validateIncorpValidityTillDate(final Client client, final List<ApiParameterError> dataValidationErrors) { private void validateIncorpValidityTillDate(final Client client, final List<ApiParameterError> dataValidationErrors) {
if (getIncorpValidityTillLocalDate() != null && client.dateOfBirthLocalDate() != null if (getIncorpValidityTillLocalDate() != null && client.dateOfBirthLocalDate() != null
&& client.dateOfBirthLocalDate().isAfter(getIncorpValidityTillLocalDate())) { && DateUtils.isAfter(client.dateOfBirthLocalDate(), getIncorpValidityTillLocalDate())) {
final String defaultUserMessage = "incorpvaliditytill date cannot be after the incorporation date"; final String defaultUserMessage = "incorpvaliditytill date cannot be after the incorporation date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.incorpValidityTill.after.incorp.date", final ApiParameterError error = ApiParameterError.parameterError("error.msg.clients.incorpValidityTill.after.incorp.date",
defaultUserMessage, ClientApiConstants.incorpValidityTillParamName, this.incorpValidityTill); defaultUserMessage, ClientApiConstants.incorpValidityTillParamName, this.incorpValidityTill);
@@ -99,9 +99,9 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors) final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors)
.resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME); .resource(ClientApiConstants.CLIENT_CHARGES_RESOURCE_NAME);
LocalDate activationDate = client.getActivationLocalDate(); LocalDate activationDate = client.getActivationDate();
LocalDate dueDate = clientCharge.getDueLocalDate(); LocalDate dueDate = clientCharge.getDueLocalDate();
if (dueDate.isBefore(activationDate)) { if (DateUtils.isBefore(dueDate, activationDate)) {
baseDataValidator.reset().parameter(ClientApiConstants.dueAsOfDateParamName).value(dueDate.format(fmt)) baseDataValidator.reset().parameter(ClientApiConstants.dueAsOfDateParamName).value(dueDate.format(fmt))
.failWithCodeNoParameterAddedToErrorCode("dueDate.before.activationDate"); .failWithCodeNoParameterAddedToErrorCode("dueDate.before.activationDate");
@@ -275,7 +275,7 @@ public class ClientChargeWritePlatformServiceImpl implements ClientChargeWritePl
if (requiresTransactionDateValidation) { if (requiresTransactionDateValidation) {
validateTransactionDateOnWorkingDay(transactionDate, clientCharge, fmt); validateTransactionDateOnWorkingDay(transactionDate, clientCharge, fmt);
if (client.getActivationLocalDate() != null && transactionDate.isBefore(client.getActivationLocalDate())) { if (DateUtils.isBefore(transactionDate, client.getActivationDate())) {
baseDataValidator.reset().parameter(ClientApiConstants.transactionDateParamName).value(transactionDate.format(fmt)) baseDataValidator.reset().parameter(ClientApiConstants.transactionDateParamName).value(transactionDate.format(fmt))
.failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate"); .failWithCodeNoParameterAddedToErrorCode("transaction.before.activationDate");
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -277,7 +277,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) { if (command.hasParameter(ClientApiConstants.submittedOnDateParamName)) {
submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName); submittedOnDate = command.localDateValueOfParameterNamed(ClientApiConstants.submittedOnDateParamName);
} }
if (active && submittedOnDate.isAfter(activationDate)) { if (active && DateUtils.isAfter(submittedOnDate, activationDate)) {
submittedOnDate = activationDate; submittedOnDate = activationDate;
} }
final Long savingsAccountId = null; final Long savingsAccountId = null;
@@ -743,7 +743,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
CommandProcessingResult commandProcessingResult = CommandProcessingResult.empty(); CommandProcessingResult commandProcessingResult = CommandProcessingResult.empty();
if (client.isActive() && client.savingsProductId() != null) { if (client.isActive() && client.savingsProductId() != null) {
SavingsAccountDataDTO savingsAccountDataDTO = new SavingsAccountDataDTO(client, null, client.savingsProductId(), SavingsAccountDataDTO savingsAccountDataDTO = new SavingsAccountDataDTO(client, null, client.savingsProductId(),
client.getActivationLocalDate(), client.activatedBy(), fmt); client.getActivationDate(), client.activatedBy(), fmt);
commandProcessingResult = this.savingsApplicationProcessWritePlatformService.createActiveApplication(savingsAccountDataDTO); commandProcessingResult = this.savingsApplicationProcessWritePlatformService.createActiveApplication(savingsAccountDataDTO);
if (commandProcessingResult.getSavingsId() != null) { if (commandProcessingResult.getSavingsId() != null) {
this.savingsRepositoryWrapper.findOneWithNotFoundDetection(commandProcessingResult.getSavingsId()); this.savingsRepositoryWrapper.findOneWithNotFoundDetection(commandProcessingResult.getSavingsId());
@@ -851,10 +851,10 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
throw new InvalidClientStateTransitionException("close", "is.under.transfer", errorMessage); throw new InvalidClientStateTransitionException("close", "is.under.transfer", errorMessage);
} }
if (client.isNotPending() && client.getActivationLocalDate() != null && client.getActivationLocalDate().isAfter(closureDate)) { if (client.isNotPending() && DateUtils.isAfter(client.getActivationDate(), closureDate)) {
final String errorMessage = "The client closureDate cannot be before the client ActivationDate."; final String errorMessage = "The client closureDate cannot be before the client ActivationDate.";
throw new InvalidClientStateTransitionException("close", "date.cannot.before.client.actvation.date", errorMessage, throw new InvalidClientStateTransitionException("close", "date.cannot.before.client.actvation.date", errorMessage,
closureDate, client.getActivationLocalDate()); closureDate, client.getActivationDate());
} }
final LegalForm legalForm = LegalForm.fromInt(client.getLegalForm()); final LegalForm legalForm = LegalForm.fromInt(client.getLegalForm());
entityDatatableChecksWritePlatformService.runTheCheck(clientId, EntityTables.CLIENT.getName(), StatusEnum.CLOSE.getCode(), entityDatatableChecksWritePlatformService.runTheCheck(clientId, EntityTables.CLIENT.getName(), StatusEnum.CLOSE.getCode(),
@@ -866,7 +866,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
if (loanStatus.isOpen() || loanStatus.isPendingApproval() || loanStatus.isAwaitingDisbursal()) { if (loanStatus.isOpen() || loanStatus.isPendingApproval() || loanStatus.isAwaitingDisbursal()) {
final String errorMessage = "Client cannot be closed because of non-closed loans."; final String errorMessage = "Client cannot be closed because of non-closed loans.";
throw new InvalidClientStateTransitionException("close", "loan.non-closed", errorMessage); throw new InvalidClientStateTransitionException("close", "loan.non-closed", errorMessage);
} else if (loanStatus.isClosed() && loan.getClosedOnDate().isAfter(closureDate)) { } else if (loanStatus.isClosed() && DateUtils.isAfter(loan.getClosedOnDate(), closureDate)) {
final String errorMessage = "The client closureDate cannot be before the loan closedOnDate."; final String errorMessage = "The client closureDate cannot be before the loan closedOnDate.";
throw new InvalidClientStateTransitionException("close", "date.cannot.before.loan.closed.date", errorMessage, throw new InvalidClientStateTransitionException("close", "date.cannot.before.loan.closed.date", errorMessage,
closureDate, loan.getClosedOnDate()); closureDate, loan.getClosedOnDate());
@@ -976,7 +976,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final String errorMessage = "Only clients pending activation may be withdrawn."; final String errorMessage = "Only clients pending activation may be withdrawn.";
throw new InvalidClientStateTransitionException("rejection", "on.account.not.in.pending.activation.status", errorMessage, throw new InvalidClientStateTransitionException("rejection", "on.account.not.in.pending.activation.status", errorMessage,
rejectionDate, client.getSubmittedOnDate()); rejectionDate, client.getSubmittedOnDate());
} else if (client.getSubmittedOnDate().isAfter(rejectionDate)) { } else if (DateUtils.isAfter(client.getSubmittedOnDate(), rejectionDate)) {
final String errorMessage = "The client rejection date cannot be before the client submitted date."; final String errorMessage = "The client rejection date cannot be before the client submitted date.";
throw new InvalidClientStateTransitionException("rejection", "date.cannot.before.client.submitted.date", errorMessage, throw new InvalidClientStateTransitionException("rejection", "date.cannot.before.client.submitted.date", errorMessage,
rejectionDate, client.getSubmittedOnDate()); rejectionDate, client.getSubmittedOnDate());
@@ -1008,7 +1008,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
final String errorMessage = "Only clients pending activation may be withdrawn."; final String errorMessage = "Only clients pending activation may be withdrawn.";
throw new InvalidClientStateTransitionException("withdrawal", "on.account.not.in.pending.activation.status", errorMessage, throw new InvalidClientStateTransitionException("withdrawal", "on.account.not.in.pending.activation.status", errorMessage,
withdrawalDate, client.getSubmittedOnDate()); withdrawalDate, client.getSubmittedOnDate());
} else if (client.getSubmittedOnDate().isAfter(withdrawalDate)) { } else if (DateUtils.isAfter(client.getSubmittedOnDate(), withdrawalDate)) {
final String errorMessage = "The client withdrawal date cannot be before the client submitted date."; final String errorMessage = "The client withdrawal date cannot be before the client submitted date.";
throw new InvalidClientStateTransitionException("withdrawal", "date.cannot.before.client.submitted.date", errorMessage, throw new InvalidClientStateTransitionException("withdrawal", "date.cannot.before.client.submitted.date", errorMessage,
withdrawalDate, client.getSubmittedOnDate()); withdrawalDate, client.getSubmittedOnDate());
@@ -1034,7 +1034,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
if (!client.isClosed()) { if (!client.isClosed()) {
final String errorMessage = "only closed clients may be reactivated."; final String errorMessage = "only closed clients may be reactivated.";
throw new InvalidClientStateTransitionException("reactivation", "on.nonclosed.account", errorMessage); throw new InvalidClientStateTransitionException("reactivation", "on.nonclosed.account", errorMessage);
} else if (client.getClosureDate().isAfter(reactivateDate)) { } else if (DateUtils.isAfter(client.getClosureDate(), reactivateDate)) {
final String errorMessage = "The client reactivation date cannot be before the client closed date."; final String errorMessage = "The client reactivation date cannot be before the client closed date.";
throw new InvalidClientStateTransitionException("reactivation", "date.cannot.before.client.closed.date", errorMessage, throw new InvalidClientStateTransitionException("reactivation", "date.cannot.before.client.closed.date", errorMessage,
reactivateDate, client.getClosureDate()); reactivateDate, client.getClosureDate());
@@ -1060,7 +1060,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
if (!client.isRejected()) { if (!client.isRejected()) {
final String errorMessage = "only rejected clients may be reactivated."; final String errorMessage = "only rejected clients may be reactivated.";
throw new InvalidClientStateTransitionException("undorejection", "on.nonrejected.account", errorMessage); throw new InvalidClientStateTransitionException("undorejection", "on.nonrejected.account", errorMessage);
} else if (client.getRejectedDate().isAfter(undoRejectDate)) { } else if (DateUtils.isAfter(client.getRejectedDate(), undoRejectDate)) {
final String errorMessage = "The client reactivation date cannot be before the client rejected date."; final String errorMessage = "The client reactivation date cannot be before the client rejected date.";
throw new InvalidClientStateTransitionException("reopened", "date.cannot.before.client.rejected.date", errorMessage, throw new InvalidClientStateTransitionException("reopened", "date.cannot.before.client.rejected.date", errorMessage,
undoRejectDate, client.getRejectedDate()); undoRejectDate, client.getRejectedDate());
@@ -1088,7 +1088,7 @@ public class ClientWritePlatformServiceJpaRepositoryImpl implements ClientWriteP
if (!client.isWithdrawn()) { if (!client.isWithdrawn()) {
final String errorMessage = "only withdrawal clients may be reactivated."; final String errorMessage = "only withdrawal clients may be reactivated.";
throw new InvalidClientStateTransitionException("undoWithdrawal", "on.nonwithdrawal.account", errorMessage); throw new InvalidClientStateTransitionException("undoWithdrawal", "on.nonwithdrawal.account", errorMessage);
} else if (client.getWithdrawalDate().isAfter(undoWithdrawalDate)) { } else if (DateUtils.isAfter(client.getWithdrawalDate(), undoWithdrawalDate)) {
final String errorMessage = "The client reactivation date cannot be before the client withdrawal date."; final String errorMessage = "The client reactivation date cannot be before the client withdrawal date.";
throw new InvalidClientStateTransitionException("reopened", "date.cannot.before.client.withdrawal.date", errorMessage, throw new InvalidClientStateTransitionException("reopened", "date.cannot.before.client.withdrawal.date", errorMessage,
undoWithdrawalDate, client.getWithdrawalDate()); undoWithdrawalDate, client.getWithdrawalDate());
@@ -55,7 +55,7 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
// Get the oldest overdue installment if exists one // Get the oldest overdue installment if exists one
for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) { for (LoanRepaymentScheduleInstallment installment : loan.getRepaymentScheduleInstallments()) {
if (!installment.isObligationsMet()) { if (!installment.isObligationsMet()) {
if (installment.getDueDate().isBefore(businessDate)) { if (DateUtils.isBefore(installment.getDueDate(), businessDate)) {
log.debug("Loan Id: {} with installment {} due date {}", loan.getId(), installment.getInstallmentNumber(), log.debug("Loan Id: {} with installment {} due date {}", loan.getId(), installment.getInstallmentNumber(),
installment.getDueDate()); installment.getDueDate());
outstandingAmount = outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount()); outstandingAmount = outstandingAmount.add(installment.getTotalOutstanding(loanCurrency).getAmount());
@@ -69,13 +69,13 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
boolean isLatestInstallment = Objects.equals(installment.getId(), latestInstallment.getId()); boolean isLatestInstallment = Objects.equals(installment.getId(), latestInstallment.getId());
for (LoanTransaction loanTransaction : chargebackTransactions) { for (LoanTransaction loanTransaction : chargebackTransactions) {
boolean isLoanTransactionIsOnOrAfterInstallmentFromDate = loanTransaction.getTransactionDate().isEqual( boolean isLoanTransactionIsOnOrAfterInstallmentFromDate = DateUtils
installment.getFromDate()) || loanTransaction.getTransactionDate().isAfter(installment.getFromDate()); .isEqual(loanTransaction.getTransactionDate(), installment.getFromDate())
|| DateUtils.isAfter(loanTransaction.getTransactionDate(), installment.getFromDate());
boolean isLoanTransactionIsBeforeNotLastInstallmentDueDate = !isLatestInstallment boolean isLoanTransactionIsBeforeNotLastInstallmentDueDate = !isLatestInstallment
&& loanTransaction.getTransactionDate().isBefore(installment.getDueDate()); && DateUtils.isBefore(loanTransaction.getTransactionDate(), installment.getDueDate());
boolean isLoanTransactionIsOnOrBeforeLastInstallmentDueDate = isLatestInstallment boolean isLoanTransactionIsOnOrBeforeLastInstallmentDueDate = isLatestInstallment
&& (loanTransaction.getTransactionDate().isEqual(installment.getDueDate()) && !DateUtils.isAfter(loanTransaction.getTransactionDate(), installment.getDueDate());
|| loanTransaction.getTransactionDate().isBefore(installment.getDueDate()));
if (isLoanTransactionIsOnOrAfterInstallmentFromDate && (isLoanTransactionIsBeforeNotLastInstallmentDueDate if (isLoanTransactionIsOnOrAfterInstallmentFromDate && (isLoanTransactionIsBeforeNotLastInstallmentDueDate
|| isLoanTransactionIsOnOrBeforeLastInstallmentDueDate)) { || isLoanTransactionIsOnOrBeforeLastInstallmentDueDate)) {
amountAvailable = amountAvailable.subtract(loanTransaction.getAmount()); amountAvailable = amountAvailable.subtract(loanTransaction.getAmount());
@@ -93,11 +93,12 @@ public class LoanDelinquencyDomainServiceImpl implements LoanDelinquencyDomainSe
amountAvailable = installment.getTotalPaid(loanCurrency).getAmount(); amountAvailable = installment.getTotalPaid(loanCurrency).getAmount();
log.debug("Amount available {}", amountAvailable); log.debug("Amount available {}", amountAvailable);
for (LoanTransaction loanTransaction : chargebackTransactions) { for (LoanTransaction loanTransaction : chargebackTransactions) {
boolean isLoanTransactionIsOnOrAfterInstallmentFromDate = loanTransaction.getTransactionDate().isEqual( boolean isLoanTransactionIsOnOrAfterInstallmentFromDate = !DateUtils.isBefore(loanTransaction.getTransactionDate(),
installment.getFromDate()) || loanTransaction.getTransactionDate().isAfter(installment.getFromDate()); installment.getFromDate());
boolean isLoanTransactionIsBeforeInstallmentDueDate = loanTransaction.getTransactionDate() boolean isLoanTransactionIsBeforeInstallmentDueDate = DateUtils.isBefore(loanTransaction.getTransactionDate(),
.isBefore(installment.getDueDate()); installment.getDueDate());
boolean isLoanTransactionIsBeforeBusinessDate = loanTransaction.getTransactionDate().isBefore(businessDate); boolean isLoanTransactionIsBeforeBusinessDate = DateUtils.isBefore(loanTransaction.getTransactionDate(),
businessDate);
if (isLoanTransactionIsOnOrAfterInstallmentFromDate && isLoanTransactionIsBeforeInstallmentDueDate if (isLoanTransactionIsOnOrAfterInstallmentFromDate && isLoanTransactionIsBeforeInstallmentDueDate
&& isLoanTransactionIsBeforeBusinessDate) { && isLoanTransactionIsBeforeBusinessDate) {
log.debug("Loan CB Transaction: {} {} {}", loanTransaction.getId(), loanTransaction.getTransactionDate(), log.debug("Loan CB Transaction: {} {} {}", loanTransaction.getId(), loanTransaction.getTransactionDate(),
@@ -153,7 +153,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final boolean active = command.booleanPrimitiveValueOfParameterNamed(GroupingTypesApiConstants.activeParamName); final boolean active = command.booleanPrimitiveValueOfParameterNamed(GroupingTypesApiConstants.activeParamName);
LocalDate submittedOnDate = DateUtils.getBusinessLocalDate(); LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
if (active && submittedOnDate.isAfter(activationDate)) { if (active && DateUtils.isAfter(submittedOnDate, activationDate)) {
submittedOnDate = activationDate; submittedOnDate = activationDate;
} }
if (command.hasParameter(GroupingTypesApiConstants.submittedOnDateParamName)) { if (command.hasParameter(GroupingTypesApiConstants.submittedOnDateParamName)) {
@@ -616,7 +616,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() + " cannot be closed because of non-closed loans."; final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() + " cannot be closed because of non-closed loans.";
throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", "loan.not.closed", throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", "loan.not.closed",
errorMessage); errorMessage);
} else if (loanStatus.isClosed() && loan.getClosedOnDate().isAfter(closureDate)) { } else if (loanStatus.isClosed() && DateUtils.isAfter(loan.getClosedOnDate(), closureDate)) {
final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() final String errorMessage = groupOrCenter.getGroupLevel().getLevelName()
+ "closureDate cannot be before the loan closedOnDate."; + "closureDate cannot be before the loan closedOnDate.";
throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close",
@@ -640,7 +640,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
+ " cannot be closed with active savings accounts associated."; + " cannot be closed with active savings accounts associated.";
throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close",
"savings.account.not.closed", errorMessage); "savings.account.not.closed", errorMessage);
} else if (saving.isClosed() && saving.getClosedOnDate().isAfter(closureDate)) { } else if (saving.isClosed() && DateUtils.isAfter(saving.getClosedOnDate(), closureDate)) {
final String errorMessage = groupOrCenter.getGroupLevel().getLevelName() final String errorMessage = groupOrCenter.getGroupLevel().getLevelName()
+ " closureDate cannot be before the loan closedOnDate."; + " closureDate cannot be before the loan closedOnDate.";
throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close", throw new InvalidGroupStateTransitionException(groupOrCenter.getGroupLevel().getLevelName(), "close",
@@ -906,7 +906,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
public void validateOfficeOpeningDateisAfterGroupOrCenterOpeningDate(final Office groupOffice, final GroupLevel groupLevel, public void validateOfficeOpeningDateisAfterGroupOrCenterOpeningDate(final Office groupOffice, final GroupLevel groupLevel,
final LocalDate activationDate) { final LocalDate activationDate) {
if (activationDate != null && groupOffice.getOpeningLocalDate().isAfter(activationDate)) { if (activationDate != null && DateUtils.isAfter(groupOffice.getOpeningLocalDate(), activationDate)) {
final String levelName = groupLevel.getLevelName(); final String levelName = groupLevel.getLevelName();
final String errorMessage = levelName + " activation date should be greater than or equal to the parent Office's creation date " final String errorMessage = levelName + " activation date should be greater than or equal to the parent Office's creation date "
+ activationDate.toString(); + activationDate.toString();
@@ -916,10 +916,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
} }
private void checkGroupMembersMeetingSyncWithCenterMeeting(final Long centerId, final Set<Group> groupMembers) { private void checkGroupMembersMeetingSyncWithCenterMeeting(final Long centerId, final Set<Group> groupMembers) {
// Get parent(center) calendar
/**
* Get parent(center) calendar
*/
Calendar ceneterCalendar = null; Calendar ceneterCalendar = null;
final CalendarInstance parentCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( final CalendarInstance parentCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId(
centerId, CalendarEntityType.CENTERS.getValue(), CalendarType.COLLECTION.getValue()); centerId, CalendarEntityType.CENTERS.getValue(), CalendarType.COLLECTION.getValue());
@@ -928,9 +925,7 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
} }
for (final Group group : groupMembers) { for (final Group group : groupMembers) {
/** // Get child(group) calendar
* Get child(group) calendar
*/
Calendar groupCalendar = null; Calendar groupCalendar = null;
final CalendarInstance groupCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId( final CalendarInstance groupCalendarInstance = this.calendarInstanceRepository.findByEntityIdAndEntityTypeIdAndCalendarTypeId(
group.getId(), CalendarEntityType.GROUPS.getValue(), CalendarType.COLLECTION.getValue()); group.getId(), CalendarEntityType.GROUPS.getValue(), CalendarType.COLLECTION.getValue());
@@ -938,19 +933,14 @@ public class GroupingTypesWritePlatformServiceJpaRepositoryImpl implements Group
groupCalendar = groupCalendarInstance.getCalendar(); groupCalendar = groupCalendarInstance.getCalendar();
} }
/** // Group shouldn't have a meeting when no meeting attached for center
* Group shouldn't have a meeting when no meeting attached for center
*/
if (ceneterCalendar == null && groupCalendar != null) { if (ceneterCalendar == null && groupCalendar != null) {
throw new GeneralPlatformDomainRuleException( throw new GeneralPlatformDomainRuleException(
"error.msg.center.associating.group.not.allowed.with.meeting.attached.to.group", "error.msg.center.associating.group.not.allowed.with.meeting.attached.to.group",
"Group with id " + group.getId() + " is already associated with meeting", group.getId()); "Group with id " + group.getId() + " is already associated with meeting", group.getId());
} }
/** // Group meeting recurrence should match with center meeting recurrence
* Group meeting recurrence should match with center meeting recurrence
*/
else if (ceneterCalendar != null && groupCalendar != null) { else if (ceneterCalendar != null && groupCalendar != null) {
if (!ceneterCalendar.getRecurrence().equalsIgnoreCase(groupCalendar.getRecurrence())) { if (!ceneterCalendar.getRecurrence().equalsIgnoreCase(groupCalendar.getRecurrence())) {
throw new GeneralPlatformDomainRuleException("error.msg.center.associating.group.not.allowed.with.different.meeting", throw new GeneralPlatformDomainRuleException("error.msg.center.associating.group.not.allowed.with.different.meeting",
"Group with id " + group.getId() + " meeting recurrence doesnot matched with center meeting recurrence", "Group with id " + group.getId() + " meeting recurrence doesnot matched with center meeting recurrence",
@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import org.apache.fineract.infrastructure.codes.data.CodeValueData; import org.apache.fineract.infrastructure.codes.data.CodeValueData;
import org.apache.fineract.infrastructure.core.data.EnumOptionData; import org.apache.fineract.infrastructure.core.data.EnumOptionData;
import org.apache.fineract.infrastructure.core.service.DateUtils;
/** /**
* Immutable data object representing a InterestRateChart. * Immutable data object representing a InterestRateChart.
@@ -143,7 +144,7 @@ public final class InterestRateChartData {
} }
public boolean isFromDateAfter(final LocalDate compareDate) { public boolean isFromDateAfter(final LocalDate compareDate) {
return (compareDate == null) ? false : this.fromDate.isAfter(compareDate); return compareDate != null && DateUtils.isAfter(this.fromDate, compareDate);
} }
public LocalDate endDate() { public LocalDate endDate() {
@@ -47,6 +47,7 @@ import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper; import org.apache.fineract.infrastructure.core.serialization.FromJsonHelper;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants; import org.apache.fineract.portfolio.interestratechart.InterestRateChartApiConstants;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
@@ -115,10 +116,8 @@ public class InterestRateChartDataValidator {
isPrimaryGroupingByAmount = false; isPrimaryGroupingByAmount = false;
} }
if (fromDate != null && toDate != null) { if (toDate != null && DateUtils.isAfter(fromDate, toDate)) {
if (fromDate.isAfter(toDate)) { baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date");
baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date");
}
} }
// validate chart Slabs - mandatory when creating // validate chart Slabs - mandatory when creating
@@ -177,11 +176,8 @@ public class InterestRateChartDataValidator {
if (isPrimaryGroupingByAmount == null) { if (isPrimaryGroupingByAmount == null) {
isPrimaryGroupingByAmount = false; isPrimaryGroupingByAmount = false;
} }
if (toDate != null && DateUtils.isAfter(fromDate, toDate)) {
if (fromDate != null && toDate != null) { baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date");
if (fromDate.isAfter(toDate)) {
baseDataValidator.parameter(fromDateParamName).value(fromDate).failWithCode("from.date.is.after.to.date");
}
} }
validateChartSlabs(element, baseDataValidator, isPrimaryGroupingByAmount); validateChartSlabs(element, baseDataValidator, isPrimaryGroupingByAmount);
@@ -312,10 +312,7 @@ public class InterestRateChartSlabFields {
public boolean isBigDecimalSame(final BigDecimal obj1, final BigDecimal obj2) { public boolean isBigDecimalSame(final BigDecimal obj1, final BigDecimal obj2) {
if (obj1 == null || obj2 == null) { if (obj1 == null || obj2 == null) {
if (Objects.compare(obj1, obj2, Comparator.nullsFirst(Comparator.naturalOrder())) == 0 ? Boolean.TRUE : Boolean.FALSE) { return Objects.compare(obj1, obj2, Comparator.nullsFirst(Comparator.naturalOrder())) == 0;
return true;
}
return false;
} }
return obj1.compareTo(obj2) == 0; return obj1.compareTo(obj2) == 0;
} }
@@ -326,7 +323,7 @@ public class InterestRateChartSlabFields {
} }
public boolean isAmountRangeProvided() { public boolean isAmountRangeProvided() {
return (this.amountRangeFrom == null) ? false : true; return this.amountRangeFrom != null;
} }
public BigDecimal annualInterestRate() { public BigDecimal annualInterestRate() {
@@ -88,8 +88,8 @@ public class InternalLoanInformationApiResource implements InitializingBean {
final Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId); final Loan loan = loanRepositoryWrapper.findOneWithNotFoundDetection(loanId);
Map<String, Object> auditFields = new HashMap<>( Map<String, Object> auditFields = new HashMap<>(
Map.of(CREATED_BY, loan.getCreatedBy().orElse(null), CREATED_DATE, loan.getCreatedDate().orElse(null), LAST_MODIFIED_BY, Map.of(CREATED_BY, loan.getCreatedBy().orElse(null), CREATED_DATE, loan.getCreatedDateTime(), LAST_MODIFIED_BY,
loan.getLastModifiedBy().orElse(null), LAST_MODIFIED_DATE, loan.getLastModifiedDate().orElse(null))); loan.getLastModifiedBy().orElse(null), LAST_MODIFIED_DATE, loan.getLastModifiedDateTime()));
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializerForMap.serialize(settings, auditFields); return this.toApiJsonSerializerForMap.serialize(settings, auditFields);
} }
@@ -108,8 +108,8 @@ public class InternalLoanInformationApiResource implements InitializingBean {
final LoanTransaction transaction = loanTransactionRepository.findById(transactionId).orElseThrow(); final LoanTransaction transaction = loanTransactionRepository.findById(transactionId).orElseThrow();
Map<String, Object> auditFields = new HashMap<>(Map.of(CREATED_BY, transaction.getCreatedBy().orElse(null), CREATED_DATE, Map<String, Object> auditFields = new HashMap<>(Map.of(CREATED_BY, transaction.getCreatedBy().orElse(null), CREATED_DATE,
transaction.getCreatedDate().orElse(null), LAST_MODIFIED_BY, transaction.getLastModifiedBy().orElse(null), transaction.getCreatedDateTime(), LAST_MODIFIED_BY, transaction.getLastModifiedBy().orElse(null), LAST_MODIFIED_DATE,
LAST_MODIFIED_DATE, transaction.getLastModifiedDate().orElse(null))); transaction.getLastModifiedDateTime()));
final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters()); final ApiRequestJsonSerializationSettings settings = this.apiRequestParameterHelper.process(uriInfo.getQueryParameters());
return this.toApiJsonSerializerForMap.serialize(settings, auditFields); return this.toApiJsonSerializerForMap.serialize(settings, auditFields);
} }
@@ -369,7 +369,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final Integer transactionType, Integer installmentNumber) { final Integer transactionType, Integer installmentNumber) {
boolean isAccountTransfer = true; boolean isAccountTransfer = true;
checkClientOrGroupActive(loan); checkClientOrGroupActive(loan);
if (loan.isChargedOff() && transactionDate.isBefore(loan.getChargedOffOnDate())) { if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loan.getId() + loan.getId()
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan", + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
@@ -465,7 +465,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
boolean isAccountTransfer = true; boolean isAccountTransfer = true;
final Loan loan = this.loanAccountAssembler.assembleFrom(accountId); final Loan loan = this.loanAccountAssembler.assembleFrom(accountId);
checkClientOrGroupActive(loan); checkClientOrGroupActive(loan);
if (loan.isChargedOff() && transactionDate.isBefore(loan.getChargedOffOnDate())) { if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loan.getId() + loan.getId()
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan", + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
@@ -518,7 +518,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final PaymentDetail paymentDetail, final String noteText, final ExternalId txnExternalId, final boolean isLoanToLoanTransfer) { final PaymentDetail paymentDetail, final String noteText, final ExternalId txnExternalId, final boolean isLoanToLoanTransfer) {
final Loan loan = this.loanAccountAssembler.assembleFrom(loanId); final Loan loan = this.loanAccountAssembler.assembleFrom(loanId);
checkClientOrGroupActive(loan); checkClientOrGroupActive(loan);
if (loan.isChargedOff() && transactionDate.isBefore(loan.getChargedOffOnDate())) { if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loan.getId() + loan.getId()
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan", + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
@@ -551,7 +551,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
@Override @Override
public void reverseTransfer(final LoanTransaction loanTransaction) { public void reverseTransfer(final LoanTransaction loanTransaction) {
if (loanTransaction.getLoan().isChargedOff() if (loanTransaction.getLoan().isChargedOff()
&& loanTransaction.getTransactionDate().isBefore(loanTransaction.getLoan().getChargedOffOnDate())) { && DateUtils.isBefore(loanTransaction.getTransactionDate(), loanTransaction.getLoan().getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date",
"Loan transaction: " + loanTransaction.getId() "Loan transaction: " + loanTransaction.getId()
+ " reversal is not allowed before or on the date when the loan got charged-off", + " reversal is not allowed before or on the date when the loan got charged-off",
@@ -614,10 +614,10 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
Set<LoanCharge> loanCharges = loan.getActiveCharges(); Set<LoanCharge> loanCharges = loan.getActiveCharges();
for (LoanRepaymentScheduleInstallment installment : installments) { for (LoanRepaymentScheduleInstallment installment : installments) {
if (installment.getDueDate().isAfter(loan.getMaturityDate())) { if (DateUtils.isAfter(installment.getDueDate(), loan.getMaturityDate())) {
accruedTill = DateUtils.getBusinessLocalDate(); accruedTill = DateUtils.getBusinessLocalDate();
} }
if (!isOrganisationDateEnabled || organisationStartDate.isBefore(installment.getDueDate())) { if (!isOrganisationDateEnabled || DateUtils.isBefore(organisationStartDate, installment.getDueDate())) {
generateLoanScheduleAccrualData(accruedTill, loanScheduleAccrualList, loanId, officeId, accrualStartDate, generateLoanScheduleAccrualData(accruedTill, loanScheduleAccrualList, loanId, officeId, accrualStartDate,
repaymentFrequency, repayEvery, interestCalculatedFrom, loanProductId, currency, currencyData, loanCharges, repaymentFrequency, repayEvery, interestCalculatedFrom, loanProductId, currency, currencyData, loanCharges,
installment); installment);
@@ -641,12 +641,12 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final LocalDate interestCalculatedFrom, final Long loanProductId, final MonetaryCurrency currency, final LocalDate interestCalculatedFrom, final Long loanProductId, final MonetaryCurrency currency,
final CurrencyData currencyData, final Set<LoanCharge> loanCharges, final LoanRepaymentScheduleInstallment installment) { final CurrencyData currencyData, final Set<LoanCharge> loanCharges, final LoanRepaymentScheduleInstallment installment) {
if (!accruedTill.isBefore(installment.getDueDate()) if (!DateUtils.isBefore(accruedTill, installment.getDueDate()) || (DateUtils.isAfter(accruedTill, installment.getFromDate())
|| (accruedTill.isAfter(installment.getFromDate()) && !accruedTill.isAfter(installment.getDueDate()))) { && !DateUtils.isAfter(accruedTill, installment.getDueDate()))) {
BigDecimal dueDateFeeIncome = BigDecimal.ZERO; BigDecimal dueDateFeeIncome = BigDecimal.ZERO;
BigDecimal dueDatePenaltyIncome = BigDecimal.ZERO; BigDecimal dueDatePenaltyIncome = BigDecimal.ZERO;
LocalDate chargesTillDate = installment.getDueDate(); LocalDate chargesTillDate = installment.getDueDate();
if (!accruedTill.isAfter(installment.getDueDate())) { if (!DateUtils.isAfter(accruedTill, installment.getDueDate())) {
chargesTillDate = accruedTill; chargesTillDate = accruedTill;
} }
@@ -685,7 +685,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
@Override @Override
public LoanTransaction creditBalanceRefund(final Loan loan, final LocalDate transactionDate, final BigDecimal transactionAmount, public LoanTransaction creditBalanceRefund(final Loan loan, final LocalDate transactionDate, final BigDecimal transactionAmount,
final String noteText, final ExternalId externalId, PaymentDetail paymentDetail) { final String noteText, final ExternalId externalId, PaymentDetail paymentDetail) {
if (loan.isChargedOff() && transactionDate.isBefore(loan.getChargedOffOnDate())) { if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loan.getId() + loan.getId()
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan", + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
@@ -729,7 +729,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final List<Long> existingReversedTransactionIds = new ArrayList<>(); final List<Long> existingReversedTransactionIds = new ArrayList<>();
final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount); final Money refundAmount = Money.of(loan.getCurrency(), transactionAmount);
if (loan.isChargedOff() && transactionDate.isBefore(loan.getChargedOffOnDate())) { if (loan.isChargedOff() && DateUtils.isBefore(transactionDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loan.getId() + loan.getId()
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan", + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
@@ -768,7 +768,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
@Override @Override
public LoanTransaction foreCloseLoan(Loan loan, final LocalDate foreClosureDate, final String noteText, final ExternalId externalId, public LoanTransaction foreCloseLoan(Loan loan, final LocalDate foreClosureDate, final String noteText, final ExternalId externalId,
Map<String, Object> changes) { Map<String, Object> changes) {
if (loan.isChargedOff() && foreClosureDate.isBefore(loan.getChargedOffOnDate())) { if (loan.isChargedOff() && DateUtils.isBefore(foreClosureDate, loan.getChargedOffOnDate())) {
throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: " throw new GeneralPlatformDomainRuleException("error.msg.transaction.date.cannot.be.earlier.than.charge.off.date", "Loan: "
+ loan.getId() + loan.getId()
+ " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan", + " backdated transaction is not allowed. Transaction date cannot be earlier than the charge-off date of the loan",
@@ -785,7 +785,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
final ScheduleGeneratorDTO scheduleGeneratorDTO = null; final ScheduleGeneratorDTO scheduleGeneratorDTO = null;
final LoanRepaymentScheduleInstallment foreCloseDetail = loan.fetchLoanForeclosureDetail(foreClosureDate); final LoanRepaymentScheduleInstallment foreCloseDetail = loan.fetchLoanForeclosureDetail(foreClosureDate);
if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct() if (loan.isPeriodicAccrualAccountingEnabledOnLoanProduct()
&& (loan.getAccruedTill() == null || !foreClosureDate.isEqual(loan.getAccruedTill()))) { && (loan.getAccruedTill() == null || !DateUtils.isEqual(foreClosureDate, loan.getAccruedTill()))) {
loan.reverseAccrualsAfter(foreClosureDate); loan.reverseAccrualsAfter(foreClosureDate);
Money[] accruedReceivables = loan.getReceivableIncome(foreClosureDate); Money[] accruedReceivables = loan.getReceivableIncome(foreClosureDate);
Money interestPortion = foreCloseDetail.getInterestCharged(currency).minus(accruedReceivables[0]); Money interestPortion = foreCloseDetail.getInterestCharged(currency).minus(accruedReceivables[0]);
@@ -805,7 +805,7 @@ public class LoanAccountDomainServiceJpa implements LoanAccountDomainService {
loan.addLoanTransaction(accrualTransaction); loan.addLoanTransaction(accrualTransaction);
Set<LoanChargePaidBy> accrualCharges = accrualTransaction.getLoanChargesPaid(); Set<LoanChargePaidBy> accrualCharges = accrualTransaction.getLoanChargesPaid();
for (LoanCharge loanCharge : loan.getActiveCharges()) { for (LoanCharge loanCharge : loan.getActiveCharges()) {
boolean isDue = fromDate.isEqual(loan.getDisbursementDate()) boolean isDue = DateUtils.isEqual(fromDate, loan.getDisbursementDate())
? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(fromDate, foreClosureDate) ? loanCharge.isDueForCollectionFromIncludingAndUpToAndIncluding(fromDate, foreClosureDate)
: loanCharge.isDueForCollectionFromAndUpToAndIncluding(fromDate, foreClosureDate); : loanCharge.isDueForCollectionFromAndUpToAndIncluding(fromDate, foreClosureDate);
if (loanCharge.isActive() && !loanCharge.isPaid() && (isDue || loanCharge.isInstalmentFee())) { if (loanCharge.isActive() && !loanCharge.isPaid() && (isDue || loanCharge.isInstalmentFee())) {
@@ -1,30 +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.domain;
import java.util.Comparator;
import org.apache.fineract.portfolio.loanaccount.data.LoanTransactionData;
public class LoanRepaymentDataComparator implements Comparator<LoanTransactionData> {
@Override
public int compare(final LoanTransactionData o1, final LoanTransactionData o2) {
return o2.getDate().compareTo(o1.getDate());
}
}
@@ -29,10 +29,8 @@ public class LoanTermVariationsComparator implements Comparator<LoanTermVariatio
public int compare(final LoanTermVariations o1, final LoanTermVariations o2) { public int compare(final LoanTermVariations o1, final LoanTermVariations o2) {
int compareResult = 0; int compareResult = 0;
final int comparsion = o1.getTermApplicableFrom().compareTo(o2.getTermApplicableFrom()); final int comparsion = o1.getTermApplicableFrom().compareTo(o2.getTermApplicableFrom());
/** // For Terms bearing the same effective date, we sort based on modified date (when available) and new inserted
* For Terms bearing the same effective date, we sort based on modified date (when available) and new inserted // installment
* installment
**/
if (comparsion == 0) { if (comparsion == 0) {
if (o2.getTermType().isDueDateVariation() || o2.getTermType().isInsertInstallment()) { if (o2.getTermType().isDueDateVariation() || o2.getTermType().isInsertInstallment()) {
compareResult = 1; compareResult = 1;
@@ -27,6 +27,7 @@ import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.MathUtil; import org.apache.fineract.infrastructure.core.service.MathUtil;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
@@ -312,7 +313,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
protected boolean isTransactionInAdvanceOfInstallment(final int installmentIndex, protected boolean isTransactionInAdvanceOfInstallment(final int installmentIndex,
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) { final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex); final LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex);
return transactionDate.isBefore(currentInstallment.getDueDate()); return DateUtils.isBefore(transactionDate, currentInstallment.getDueDate());
} }
/** /**
@@ -325,7 +326,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
protected boolean isTransactionALateRepaymentOnInstallment(final int installmentIndex, protected boolean isTransactionALateRepaymentOnInstallment(final int installmentIndex,
final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) { final List<LoanRepaymentScheduleInstallment> installments, final LocalDate transactionDate) {
final LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex); final LoanRepaymentScheduleInstallment currentInstallment = installments.get(installmentIndex);
return transactionDate.isAfter(currentInstallment.getDueDate()); return DateUtils.isAfter(transactionDate, currentInstallment.getDueDate());
} }
private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction, private void recalculateChargeOffTransaction(ChangedTransactionDetail changedTransactionDetail, LoanTransaction loanTransaction,
@@ -477,7 +478,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
LocalDate pastDueDate = null; LocalDate pastDueDate = null;
for (final LoanRepaymentScheduleInstallment currentInstallment : installments) { for (final LoanRepaymentScheduleInstallment currentInstallment : installments) {
pastDueDate = currentInstallment.getDueDate(); pastDueDate = currentInstallment.getDueDate();
if (!currentInstallment.isAdditional() && currentInstallment.getDueDate().isAfter(transactionDate)) { if (!currentInstallment.isAdditional() && DateUtils.isAfter(currentInstallment.getDueDate(), transactionDate)) {
currentInstallment.addToCredits(transactionAmount.getAmount()); currentInstallment.addToCredits(transactionAmount.getAmount());
currentInstallment.addToPrincipal(transactionDate, transactionAmount); currentInstallment.addToPrincipal(transactionDate, transactionAmount);
if (repaidAmount.isGreaterThanZero()) { if (repaidAmount.isGreaterThanZero()) {
@@ -491,7 +492,7 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
// If already exists an additional installment just update the due date and // If already exists an additional installment just update the due date and
// principal from the Loan chargeback / CBR transaction // principal from the Loan chargeback / CBR transaction
} else if (currentInstallment.isAdditional()) { } else if (currentInstallment.isAdditional()) {
if (transactionDate.isAfter(currentInstallment.getDueDate())) { if (DateUtils.isAfter(transactionDate, currentInstallment.getDueDate())) {
currentInstallment.updateDueDate(transactionDate); currentInstallment.updateDueDate(transactionDate);
} }
@@ -669,18 +670,19 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
if (loanCharge.getAmountOutstanding(currency).isGreaterThanZero() && !loanCharge.isDueAtDisbursement()) { if (loanCharge.getAmountOutstanding(currency).isGreaterThanZero() && !loanCharge.isDueAtDisbursement()) {
if (loanCharge.isInstalmentFee()) { if (loanCharge.isInstalmentFee()) {
LoanInstallmentCharge unpaidLoanChargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge(); LoanInstallmentCharge unpaidLoanChargePerInstallment = loanCharge.getUnpaidInstallmentLoanCharge();
if (chargePerInstallment == null || chargePerInstallment.getRepaymentInstallment().getDueDate() if (chargePerInstallment == null || DateUtils.isAfter(chargePerInstallment.getRepaymentInstallment().getDueDate(),
.isAfter(unpaidLoanChargePerInstallment.getRepaymentInstallment().getDueDate())) { unpaidLoanChargePerInstallment.getRepaymentInstallment().getDueDate())) {
installemntCharge = loanCharge; installemntCharge = loanCharge;
chargePerInstallment = unpaidLoanChargePerInstallment; chargePerInstallment = unpaidLoanChargePerInstallment;
} }
} else if (earliestUnpaidCharge == null || loanCharge.getDueLocalDate().isBefore(earliestUnpaidCharge.getDueLocalDate())) { } else if (earliestUnpaidCharge == null
|| DateUtils.isBefore(loanCharge.getDueLocalDate(), earliestUnpaidCharge.getDueLocalDate())) {
earliestUnpaidCharge = loanCharge; earliestUnpaidCharge = loanCharge;
} }
} }
} }
if (earliestUnpaidCharge == null || (chargePerInstallment != null if (earliestUnpaidCharge == null || (chargePerInstallment != null && DateUtils.isAfter(earliestUnpaidCharge.getDueLocalDate(),
&& earliestUnpaidCharge.getDueLocalDate().isAfter(chargePerInstallment.getRepaymentInstallment().getDueDate()))) { chargePerInstallment.getRepaymentInstallment().getDueDate()))) {
earliestUnpaidCharge = installemntCharge; earliestUnpaidCharge = installemntCharge;
} }
@@ -791,20 +793,20 @@ public abstract class AbstractLoanRepaymentScheduleTransactionProcessor implemen
if (loanCharge.isInstalmentFee()) { if (loanCharge.isInstalmentFee()) {
LoanInstallmentCharge paidLoanChargePerInstallment = loanCharge LoanInstallmentCharge paidLoanChargePerInstallment = loanCharge
.getLastPaidOrPartiallyPaidInstallmentLoanCharge(currency); .getLastPaidOrPartiallyPaidInstallmentLoanCharge(currency);
if (chargePerInstallment == null if (chargePerInstallment == null || (paidLoanChargePerInstallment != null
|| (paidLoanChargePerInstallment != null && chargePerInstallment.getRepaymentInstallment().getDueDate() && DateUtils.isBefore(chargePerInstallment.getRepaymentInstallment().getDueDate(),
.isBefore(paidLoanChargePerInstallment.getRepaymentInstallment().getDueDate()))) { paidLoanChargePerInstallment.getRepaymentInstallment().getDueDate()))) {
installemntCharge = loanCharge; installemntCharge = loanCharge;
chargePerInstallment = paidLoanChargePerInstallment; chargePerInstallment = paidLoanChargePerInstallment;
} }
} else if (latestPaidCharge == null || (loanCharge.isPaidOrPartiallyPaid(currency) } else if (latestPaidCharge == null || (loanCharge.isPaidOrPartiallyPaid(currency)
&& loanCharge.getDueLocalDate().isAfter(latestPaidCharge.getDueLocalDate()))) { && DateUtils.isAfter(loanCharge.getDueLocalDate(), latestPaidCharge.getDueLocalDate()))) {
latestPaidCharge = loanCharge; latestPaidCharge = loanCharge;
} }
} }
} }
if (latestPaidCharge == null || (chargePerInstallment != null if (latestPaidCharge == null || (chargePerInstallment != null
&& latestPaidCharge.getDueLocalDate().isAfter(chargePerInstallment.getRepaymentInstallment().getDueDate()))) { && DateUtils.isAfter(latestPaidCharge.getDueLocalDate(), chargePerInstallment.getRepaymentInstallment().getDueDate()))) {
latestPaidCharge = installemntCharge; latestPaidCharge = installemntCharge;
} }
@@ -194,7 +194,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
switch (paymentAllocationType.getDueType()) { switch (paymentAllocationType.getDueType()) {
case PAST_DUE -> { case PAST_DUE -> {
currentInstallment = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff) currentInstallment = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff)
.filter(e -> loanTransaction.getTransactionDate().isAfter(e.getDueDate())) .filter(e -> loanTransaction.isAfter(e.getDueDate()))
.min(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).orElse(null); .min(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).orElse(null);
if (currentInstallment != null) { if (currentInstallment != null) {
LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping( LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
@@ -206,7 +206,7 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
} }
case DUE -> { case DUE -> {
currentInstallment = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff) currentInstallment = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff)
.filter(e -> loanTransaction.getTransactionDate().isEqual(e.getDueDate())) .filter(e -> loanTransaction.isOn(e.getDueDate()))
.min(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).orElse(null); .min(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).orElse(null);
if (currentInstallment != null) { if (currentInstallment != null) {
LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping( LoanTransactionToRepaymentScheduleMapping loanTransactionToRepaymentScheduleMapping = getTransactionMapping(
@@ -222,14 +222,14 @@ public class AdvancedPaymentScheduleTransactionProcessor extends AbstractLoanRep
List<LoanRepaymentScheduleInstallment> currentInstallments = new ArrayList<>(); List<LoanRepaymentScheduleInstallment> currentInstallments = new ArrayList<>();
if (FutureInstallmentAllocationRule.REAMORTIZATION.equals(futureInstallmentAllocationRule)) { if (FutureInstallmentAllocationRule.REAMORTIZATION.equals(futureInstallmentAllocationRule)) {
currentInstallments = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff) currentInstallments = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff)
.filter(e -> loanTransaction.getTransactionDate().isBefore(e.getDueDate())).toList(); .filter(e -> loanTransaction.isBefore(e.getDueDate())).toList();
} else if (FutureInstallmentAllocationRule.NEXT_INSTALLMENT.equals(futureInstallmentAllocationRule)) { } else if (FutureInstallmentAllocationRule.NEXT_INSTALLMENT.equals(futureInstallmentAllocationRule)) {
currentInstallments = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff) currentInstallments = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff)
.filter(e -> loanTransaction.getTransactionDate().isBefore(e.getDueDate())) .filter(e -> loanTransaction.isBefore(e.getDueDate()))
.min(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).stream().toList(); .min(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).stream().toList();
} else if (FutureInstallmentAllocationRule.LAST_INSTALLMENT.equals(futureInstallmentAllocationRule)) { } else if (FutureInstallmentAllocationRule.LAST_INSTALLMENT.equals(futureInstallmentAllocationRule)) {
currentInstallments = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff) currentInstallments = installments.stream().filter(LoanRepaymentScheduleInstallment::isNotFullyPaidOff)
.filter(e -> loanTransaction.getTransactionDate().isBefore(e.getDueDate())) .filter(e -> loanTransaction.isBefore(e.getDueDate()))
.max(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).stream().toList(); .max(Comparator.comparing(LoanRepaymentScheduleInstallment::getInstallmentNumber)).stream().toList();
} }
int numberOfInstallments = currentInstallments.size(); int numberOfInstallments = currentInstallments.size();
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
@@ -102,7 +103,7 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isChargePending) List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isChargePending)
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null .filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate)) || !DateUtils.isAfter(loanCharge.getEffectiveDueDate(), transactionDate))
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList(); .sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
Money calculatedPenaltyCharge = Money.zero(currency); Money calculatedPenaltyCharge = Money.zero(currency);
Money calculatedFeeCharge = Money.zero(currency); Money calculatedFeeCharge = Money.zero(currency);
@@ -141,7 +142,7 @@ public class DuePenFeeIntPriInAdvancePriPenFeeIntLoanRepaymentScheduleTransactio
feeChargesPortion = feeChargesPortion.add(subFeePortion); feeChargesPortion = feeChargesPortion.add(subFeePortion);
Money subInterestPortion; Money subInterestPortion;
if (ignoreDueDateCheck || !transactionDate.isBefore(currentInstallment.getDueDate())) { if (ignoreDueDateCheck || !DateUtils.isBefore(transactionDate, currentInstallment.getDueDate())) {
subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining); subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion); transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
interestPortion = interestPortion.add(subInterestPortion); interestPortion = interestPortion.add(subInterestPortion);
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
@@ -102,7 +103,7 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isChargePending) List<LoanCharge> orderedLoanChargesByDueDate = charges.stream().filter(LoanCharge::isActive).filter(LoanCharge::isChargePending)
.filter(loanCharge -> loanCharge.getEffectiveDueDate() == null .filter(loanCharge -> loanCharge.getEffectiveDueDate() == null
|| !loanCharge.getEffectiveDueDate().isAfter(transactionDate)) || !DateUtils.isAfter(loanCharge.getEffectiveDueDate(), transactionDate))
.sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList(); .sorted(LoanChargeEffectiveDueDateComparator.INSTANCE).toList();
Money calculatedPenaltyCharge = Money.zero(currency); Money calculatedPenaltyCharge = Money.zero(currency);
Money calculatedFeeCharge = Money.zero(currency); Money calculatedFeeCharge = Money.zero(currency);
@@ -129,13 +130,11 @@ public class DuePenIntPriFeeInAdvancePenIntPriFeeLoanRepaymentScheduleTransactio
penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion); penaltyChargesPortion = penaltyChargesPortion.add(subPenaltyPortion);
Money subInterestPortion; Money subInterestPortion;
if (ignoreDueDateCheck || !transactionDate.isBefore(currentInstallment.getDueDate())) { if (ignoreDueDateCheck || !DateUtils.isBefore(transactionDate, currentInstallment.getDueDate())) {
subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining); subInterestPortion = currentInstallment.payInterestComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion); transactionAmountRemaining = transactionAmountRemaining.minus(subInterestPortion);
interestPortion = interestPortion.add(subInterestPortion); interestPortion = interestPortion.add(subInterestPortion);
}
if (ignoreDueDateCheck || !transactionDate.isBefore(currentInstallment.getDueDate())) {
Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining); Money subPrincipalPortion = currentInstallment.payPrincipalComponent(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion); transactionAmountRemaining = transactionAmountRemaining.minus(subPrincipalPortion);
principalPortion = principalPortion.add(subPrincipalPortion); principalPortion = principalPortion.add(subPrincipalPortion);
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
@@ -85,9 +86,7 @@ public class HeavensFamilyLoanRepaymentScheduleTransactionProcessor extends Abst
final LoanRepaymentScheduleInstallment previousInstallment = installments.get(previousInstallmentIndex); final LoanRepaymentScheduleInstallment previousInstallment = installments.get(previousInstallmentIndex);
lastInstallmentDueDate = previousInstallment.getDueDate(); lastInstallmentDueDate = previousInstallment.getDueDate();
isInAdvance = !(transactionDate.isAfter(lastInstallmentDueDate) || transactionDate.isEqual(lastInstallmentDueDate)); return DateUtils.isBefore(transactionDate, lastInstallmentDueDate);
return isInAdvance;
} }
/** /**
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.loanaccount.domain.transactionprocessor.im
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge; import org.apache.fineract.portfolio.loanaccount.domain.LoanCharge;
@@ -174,14 +175,12 @@ public class RBILoanRepaymentScheduleTransactionProcessor extends AbstractLoanRe
private LoanRepaymentScheduleInstallment nearestInstallment(final LocalDate transactionDate, private LoanRepaymentScheduleInstallment nearestInstallment(final LocalDate transactionDate,
final List<LoanRepaymentScheduleInstallment> installments) { final List<LoanRepaymentScheduleInstallment> installments) {
LoanRepaymentScheduleInstallment nearest = installments.get(0); // installments must be sorted by dates
LoanRepaymentScheduleInstallment nearest = installments.get(0);
for (final LoanRepaymentScheduleInstallment installment : installments) { for (final LoanRepaymentScheduleInstallment installment : installments) {
if (installment.getDueDate().isBefore(transactionDate) || installment.getDueDate().isEqual(transactionDate)) { if (DateUtils.isBefore(transactionDate, installment.getDueDate())) {
nearest = installment;
} else if (installment.getDueDate().isAfter(transactionDate)) {
break; break;
} }
nearest = installment;
} }
return nearest; return nearest;
} }
@@ -327,7 +327,7 @@ public class GuarantorDomainServiceImpl implements GuarantorDomainService {
if (loan.isApproved() && !loan.isDisbursed()) { if (loan.isApproved() && !loan.isDisbursed()) {
final List<SavingsAccountTransaction> transactions = new ArrayList<>(); final List<SavingsAccountTransaction> transactions = new ArrayList<>();
for (final SavingsAccountTransaction transaction : savingsAccount.getTransactions()) { for (final SavingsAccountTransaction transaction : savingsAccount.getTransactions()) {
if (!transaction.getTransactionDate().isAfter(loan.getApprovedOnDate())) { if (!DateUtils.isAfter(transaction.getTransactionDate(), loan.getApprovedOnDate())) {
transactions.add(transaction); transactions.add(transaction);
} }
} }
@@ -187,7 +187,7 @@ public class GuarantorWritePlatformServiceJpaRepositoryIImpl implements Guaranto
private void validateGuarantorSavingsAccountActivationDateWithLoanSubmittedOnDate(final Loan loan, private void validateGuarantorSavingsAccountActivationDateWithLoanSubmittedOnDate(final Loan loan,
final SavingsAccount savingsAccount) { final SavingsAccount savingsAccount) {
if (loan.getSubmittedOnDate().isBefore(savingsAccount.getActivationDate())) { if (DateUtils.isBefore(loan.getSubmittedOnDate(), savingsAccount.getActivationDate())) {
throw new GeneralPlatformDomainRuleException( throw new GeneralPlatformDomainRuleException(
"error.msg.guarantor.saving.account.activation.date.is.on.or.before.loan.submitted.on.date", "error.msg.guarantor.saving.account.activation.date.is.on.or.before.loan.submitted.on.date",
"Guarantor saving account activation date [" + savingsAccount.getActivationDate() "Guarantor saving account activation date [" + savingsAccount.getActivationDate()
@@ -27,6 +27,7 @@ import java.util.Set;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService; import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDomainService;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.event.business.domain.loan.LoanRescheduledDueHolidayBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.loan.LoanRescheduledDueHolidayBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
import org.apache.fineract.organisation.holiday.domain.Holiday; import org.apache.fineract.organisation.holiday.domain.Holiday;
@@ -120,16 +121,15 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
// Loop through all loanRepayments // Loop through all loanRepayments
List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments(); List<LoanRepaymentScheduleInstallment> installments = loan.getRepaymentScheduleInstallments();
for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : installments) { for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : installments) {
final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate(); final LocalDate oldDueDate = loanRepaymentScheduleInstallment.getDueDate();
// update from date if it's not same as previous installment's due // update from date if it's not same as previous installment's due
// date. // date.
if (!loanRepaymentScheduleInstallment.getFromDate().isEqual(tmpFromDate)) { if (!DateUtils.isEqual(tmpFromDate, loanRepaymentScheduleInstallment.getFromDate())) {
loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate); loanRepaymentScheduleInstallment.updateFromDate(tmpFromDate);
} }
if (oldDueDate.equals(holiday.getFromDate()) || oldDueDate.isAfter(holiday.getFromDate())) { if (!DateUtils.isBefore(oldDueDate, holiday.getFromDate())) {
// FIXME: AA do we need to apply non-working days. // FIXME: AA do we need to apply non-working days.
// Assuming holiday's repayment reschedule to date cannot be // Assuming holiday's repayment reschedule to date cannot be
// created on a non-working day. // created on a non-working day.
@@ -146,7 +146,7 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
LocalDate adjustedRescheduleToDate = null; LocalDate adjustedRescheduleToDate = null;
final LocalDate rescheduleToDate = holiday.getToDate(); final LocalDate rescheduleToDate = holiday.getToDate();
for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : loan.getRepaymentScheduleInstallments()) { for (final LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment : loan.getRepaymentScheduleInstallments()) {
if (rescheduleToDate.isEqual(loanRepaymentScheduleInstallment.getDueDate())) { if (DateUtils.isEqual(rescheduleToDate, loanRepaymentScheduleInstallment.getDueDate())) {
adjustedRescheduleToDate = rescheduleToDate; adjustedRescheduleToDate = rescheduleToDate;
break; break;
} else { } else {
@@ -160,9 +160,9 @@ public class ApplyHolidaysToLoansTasklet implements Tasklet {
private LocalDate doStandardMonthlyCheck(LocalDate adjustedRescheduleToDate, LocalDate rescheduleToDate, private LocalDate doStandardMonthlyCheck(LocalDate adjustedRescheduleToDate, LocalDate rescheduleToDate,
LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment) { LoanRepaymentScheduleInstallment loanRepaymentScheduleInstallment) {
// Standard Monthly Loan Holiday check // Standard Monthly Loan Holiday check
if (rescheduleToDate.isAfter(loanRepaymentScheduleInstallment.getDueDate()) LocalDate dueDate = loanRepaymentScheduleInstallment.getDueDate();
&& rescheduleToDate.isBefore(loanRepaymentScheduleInstallment.getDueDate().plusDays(30))) { if (DateUtils.isAfter(rescheduleToDate, dueDate) && DateUtils.isBefore(rescheduleToDate, dueDate.plusDays(30))) {
adjustedRescheduleToDate = loanRepaymentScheduleInstallment.getDueDate(); adjustedRescheduleToDate = dueDate;
} }
return adjustedRescheduleToDate; return adjustedRescheduleToDate;
} }

Some files were not shown because too many files have changed in this diff Show More