FINERACT-1955: Timezone handling of Savings account

This commit is contained in:
jmarta
2023-10-24 16:00:57 +02:00
committed by Adam Saghy
parent 6cb8ae0ed3
commit 71afd6dead
74 changed files with 1584 additions and 1431 deletions
@@ -67,7 +67,7 @@ public abstract class AbstractAuditableCustom extends AbstractPersistableCustom
@Override @Override
public Optional<LocalDateTime> getCreatedDate() { public Optional<LocalDateTime> getCreatedDate() {
return null == this.createdDate ? Optional.empty() : Optional.of(this.createdDate); return Optional.ofNullable(this.createdDate);
} }
@Override @Override
@@ -87,7 +87,7 @@ public abstract class AbstractAuditableCustom extends AbstractPersistableCustom
@Override @Override
public Optional<LocalDateTime> getLastModifiedDate() { public Optional<LocalDateTime> getLastModifiedDate() {
return null == this.lastModifiedDate ? Optional.empty() : Optional.of(this.lastModifiedDate); return Optional.ofNullable(this.lastModifiedDate);
} }
@Override @Override
@@ -25,8 +25,10 @@ import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsCons
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass; import jakarta.persistence.MappedSuperclass;
import jakarta.validation.constraints.NotNull;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.util.Optional; import java.util.Optional;
import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
@@ -44,7 +46,7 @@ import org.springframework.data.jpa.domain.AbstractAuditable;
@MappedSuperclass @MappedSuperclass
@Getter @Getter
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor(access = AccessLevel.PROTECTED)
public abstract class AbstractAuditableWithUTCDateTimeCustom extends AbstractPersistableCustom public abstract class AbstractAuditableWithUTCDateTimeCustom extends AbstractPersistableCustom
implements Auditable<Long, Long, OffsetDateTime> { implements Auditable<Long, Long, OffsetDateTime> {
@@ -67,33 +69,36 @@ public abstract class AbstractAuditableWithUTCDateTimeCustom extends AbstractPer
private OffsetDateTime lastModifiedDate; private OffsetDateTime lastModifiedDate;
@Override @Override
@NotNull
public Optional<Long> getCreatedBy() { public Optional<Long> getCreatedBy() {
return Optional.ofNullable(this.createdBy); return Optional.ofNullable(this.createdBy);
} }
@Override @Override
@NotNull
public Optional<OffsetDateTime> getCreatedDate() { public Optional<OffsetDateTime> getCreatedDate() {
if (this.createdDate == null) { return Optional.ofNullable(createdDate);
return Optional.empty(); }
} else {
return Optional.of(this.createdDate @NotNull
.withOffsetSameInstant(DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(this.createdDate.toInstant()))); public OffsetDateTime getCreatedDateTime() {
} return getCreatedDate().orElseGet(DateUtils::getAuditOffsetDateTime);
} }
@Override @Override
@NotNull
public Optional<Long> getLastModifiedBy() { public Optional<Long> getLastModifiedBy() {
return Optional.ofNullable(this.lastModifiedBy); return Optional.ofNullable(this.lastModifiedBy);
} }
@Override @Override
@NotNull
public Optional<OffsetDateTime> getLastModifiedDate() { public Optional<OffsetDateTime> getLastModifiedDate() {
if (this.lastModifiedDate == null) { return Optional.ofNullable(lastModifiedDate);
return Optional.empty();
} else {
return Optional.of(this.lastModifiedDate
.withOffsetSameInstant(DateUtils.getDateTimeZoneOfTenant().getRules().getOffset(this.lastModifiedDate.toInstant())));
}
} }
@NotNull
public OffsetDateTime getLastModifiedDateTime() {
return getLastModifiedDate().orElseGet(DateUtils::getAuditOffsetDateTime);
}
} }
@@ -20,6 +20,7 @@ package org.apache.fineract.infrastructure.core.service;
import static java.time.temporal.ChronoUnit.DAYS; import static java.time.temporal.ChronoUnit.DAYS;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
@@ -33,7 +34,6 @@ import java.util.Locale;
import org.apache.fineract.infrastructure.core.data.ApiParameterError; import org.apache.fineract.infrastructure.core.data.ApiParameterError;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
import org.jetbrains.annotations.NotNull;
public final class DateUtils { public final class DateUtils {
@@ -46,6 +46,8 @@ public final class DateUtils {
} }
// DateTime
public static ZoneId getSystemZoneId() { public static ZoneId getSystemZoneId() {
return ZoneId.systemDefault(); return ZoneId.systemDefault();
} }
@@ -56,27 +58,38 @@ public final class DateUtils {
} }
public static LocalDate getLocalDateOfTenant() { public static LocalDate getLocalDateOfTenant() {
final ZoneId zone = getDateTimeZoneOfTenant(); return LocalDate.now(getDateTimeZoneOfTenant());
return LocalDate.now(zone);
} }
public static LocalDateTime getLocalDateTimeOfTenant() { public static LocalDateTime getLocalDateTimeOfTenant() {
final ZoneId zone = getDateTimeZoneOfTenant(); return getLocalDateTimeOfTenant(null);
return LocalDateTime.now(zone).truncatedTo(ChronoUnit.SECONDS);
} }
public static OffsetDateTime getOffsetDateTimeOfTenant() { public static LocalDateTime getLocalDateTimeOfTenant(ChronoUnit truncate) {
final ZoneId zone = getDateTimeZoneOfTenant(); LocalDateTime now = LocalDateTime.now(getDateTimeZoneOfTenant());
return OffsetDateTime.now(zone).truncatedTo(ChronoUnit.SECONDS); return truncate == null ? now : now.truncatedTo(truncate);
} }
public static OffsetDateTime getOffsetDateTimeOfTenantWithMostPrecision() { public static OffsetDateTime getOffsetDateTimeOfTenantWithMostPrecision() {
final ZoneId zone = getDateTimeZoneOfTenant(); return getOffsetDateTimeOfTenant(null);
return OffsetDateTime.now(zone); }
public static OffsetDateTime getOffsetDateTimeOfTenant() {
return getOffsetDateTimeOfTenant(null);
}
public static OffsetDateTime getOffsetDateTimeOfTenant(ChronoUnit truncate) {
OffsetDateTime now = OffsetDateTime.now(getDateTimeZoneOfTenant());
return truncate == null ? now : now.truncatedTo(truncate);
} }
public static LocalDateTime getLocalDateTimeOfSystem() { public static LocalDateTime getLocalDateTimeOfSystem() {
return LocalDateTime.now(ZoneId.systemDefault()).truncatedTo(ChronoUnit.SECONDS); return getLocalDateTimeOfSystem(null);
}
public static LocalDateTime getLocalDateTimeOfSystem(ChronoUnit truncate) {
LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault());
return truncate == null ? now : now.truncatedTo(truncate);
} }
public static LocalDateTime getAuditLocalDateTime() { public static LocalDateTime getAuditLocalDateTime() {
@@ -87,22 +100,176 @@ public final class DateUtils {
return OffsetDateTime.now(ZoneOffset.UTC); return OffsetDateTime.now(ZoneOffset.UTC);
} }
public static boolean isDateInTheFuture(final LocalDate localDate) { public static int compare(LocalDateTime first, LocalDateTime second) {
return localDate.isAfter(getBusinessLocalDate()); return compare(first, second, null);
} }
public static int compare(LocalDateTime first, LocalDateTime second, ChronoUnit truncate) {
if (first == null) {
return second == null ? 0 : -1;
}
if (second == null) {
return 1;
}
return truncate == null ? first.compareTo(second) : first.truncatedTo(truncate).compareTo(second.truncatedTo(truncate));
}
public static boolean isEqual(LocalDateTime first, LocalDateTime second) {
return isEqual(first, second, null);
}
public static boolean isEqual(LocalDateTime first, LocalDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate) == 0;
}
public static boolean isEqualTenantDateTime(LocalDateTime dateTime) {
return isEqualTenantDateTime(dateTime, null);
}
public static boolean isEqualTenantDateTime(LocalDateTime dateTime, ChronoUnit truncate) {
return isEqual(dateTime, getLocalDateTimeOfTenant(), truncate);
}
public static boolean isEqualSystemDateTime(LocalDateTime dateTime) {
return isEqualSystemDateTime(dateTime, null);
}
public static boolean isEqualSystemDateTime(LocalDateTime dateTime, ChronoUnit truncate) {
return isEqual(dateTime, getLocalDateTimeOfSystem(), truncate);
}
public static boolean isBefore(LocalDateTime first, LocalDateTime second) {
return isBefore(first, second, null);
}
public static boolean isBefore(LocalDateTime first, LocalDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate) < 0;
}
public static boolean isBeforeTenantDateTime(LocalDateTime dateTime) {
return isBeforeTenantDateTime(dateTime, null);
}
public static boolean isBeforeTenantDateTime(LocalDateTime dateTime, ChronoUnit truncate) {
return isBefore(dateTime, getLocalDateTimeOfTenant(), truncate);
}
public static boolean isBeforeSystemDateTime(LocalDateTime dateTime) {
return isBeforeSystemDateTime(dateTime, null);
}
public static boolean isBeforeSystemDateTime(LocalDateTime dateTime, ChronoUnit truncate) {
return isBefore(dateTime, getLocalDateTimeOfSystem(), truncate);
}
public static boolean isAfter(LocalDateTime first, LocalDateTime second) {
return isAfter(first, second, null);
}
public static boolean isAfter(LocalDateTime first, LocalDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate) > 0;
}
public static boolean isAfterTenantDateTime(LocalDateTime dateTime) {
return isAfterTenantDateTime(dateTime, null);
}
public static boolean isAfterTenantDateTime(LocalDateTime dateTime, ChronoUnit truncate) {
return isAfter(dateTime, getLocalDateTimeOfTenant(), truncate);
}
public static boolean isAfterSystemDateTime(LocalDateTime dateTime) {
return isAfterSystemDateTime(dateTime, null);
}
public static boolean isAfterSystemDateTime(LocalDateTime dateTime, ChronoUnit truncate) {
return isAfter(dateTime, getLocalDateTimeOfSystem(), truncate);
}
public static int compare(OffsetDateTime first, OffsetDateTime second) {
return compare(first, second, null);
}
public static int compare(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate) {
if (first == null) {
return second == null ? 0 : -1;
}
if (second == null) {
return 1;
}
return truncate == null ? first.compareTo(second) : first.truncatedTo(truncate).compareTo(second.truncatedTo(truncate));
}
public static boolean isEqual(OffsetDateTime first, OffsetDateTime second) {
return isEqual(first, second, null);
}
public static boolean isEqual(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate) == 0;
}
public static boolean isEqualTenantDateTime(OffsetDateTime dateTime) {
return isEqualTenantDateTime(dateTime, null);
}
public static boolean isEqualTenantDateTime(OffsetDateTime dateTime, ChronoUnit truncate) {
return isEqual(dateTime, getOffsetDateTimeOfTenant(), truncate);
}
public static boolean isBefore(OffsetDateTime first, OffsetDateTime second) {
return isBefore(first, second, null);
}
public static boolean isBefore(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate) < 0;
}
public static boolean isBeforeTenantDateTime(OffsetDateTime dateTime) {
return isBeforeTenantDateTime(dateTime, null);
}
public static boolean isBeforeTenantDateTime(OffsetDateTime dateTime, ChronoUnit truncate) {
return isBefore(dateTime, getOffsetDateTimeOfTenant(), truncate);
}
public static boolean isAfter(OffsetDateTime first, OffsetDateTime second) {
return isAfter(first, second, null);
}
public static boolean isAfter(OffsetDateTime first, OffsetDateTime second, ChronoUnit truncate) {
return compare(first, second, truncate) > 0;
}
public static boolean isAfterTenantDateTime(OffsetDateTime dateTime) {
return isAfterTenantDateTime(dateTime, null);
}
public static boolean isAfterTenantDateTime(OffsetDateTime dateTime, ChronoUnit truncate) {
return isAfter(dateTime, getOffsetDateTimeOfTenant(), truncate);
}
// Date
public static LocalDate getBusinessLocalDate() { public static LocalDate getBusinessLocalDate() {
return ThreadLocalContextUtil.getBusinessDate(); return ThreadLocalContextUtil.getBusinessDate();
} }
public static long getDifferenceInDays(final LocalDate localDateBefore, final LocalDate localDateAfter) {
return DAYS.between(localDateBefore, localDateAfter);
}
public static int compareToBusinessDate(LocalDate date) { public static int compareToBusinessDate(LocalDate date) {
return compare(date, getBusinessLocalDate()); return compare(date, getBusinessLocalDate());
} }
public static boolean isEqualTenantDate(LocalDate date) {
return isEqual(date, getLocalDateOfTenant());
}
public static boolean isBeforeTenantDate(LocalDate date) {
return isBefore(date, getLocalDateOfTenant());
}
public static boolean isAfterTenantDate(LocalDate date) {
return isAfter(date, getLocalDateOfTenant());
}
public static boolean isEqualBusinessDate(LocalDate date) { public static boolean isEqualBusinessDate(LocalDate date) {
return isEqual(date, getBusinessLocalDate()); return isEqual(date, getBusinessLocalDate());
} }
@@ -115,8 +282,12 @@ public final class DateUtils {
return isAfter(date, getBusinessLocalDate()); return isAfter(date, getBusinessLocalDate());
} }
public static boolean isDateInTheFuture(final LocalDate localDate) {
return isAfterBusinessDate(localDate);
}
public static int compare(LocalDate first, LocalDate second) { public static int compare(LocalDate first, LocalDate second) {
return first == null ? (second == null ? 0 : -1) : first.compareTo(second); return first == null ? (second == null ? 0 : -1) : (second == null ? 1 : first.compareTo(second));
} }
public static boolean isEqual(LocalDate first, LocalDate second) { public static boolean isEqual(LocalDate first, LocalDate second) {
@@ -131,6 +302,12 @@ public final class DateUtils {
return first != null && (second == null || first.isAfter(second)); return first != null && (second == null || first.isAfter(second));
} }
public static long getDifferenceInDays(final LocalDate localDateBefore, final LocalDate localDateAfter) {
return DAYS.between(localDateBefore, localDateAfter);
}
// Parse, format
public static LocalDate parseLocalDate(String stringDate) { public static LocalDate parseLocalDate(String stringDate) {
return parseLocalDate(stringDate, null); return parseLocalDate(stringDate, null);
} }
@@ -259,7 +259,7 @@ public final class Group extends AbstractPersistableCustom {
} }
public boolean isActivatedAfter(final LocalDate submittedOn) { public boolean isActivatedAfter(final LocalDate submittedOn) {
return getActivationLocalDate().isAfter(submittedOn); return DateUtils.isAfter(getActivationDate(), submittedOn);
} }
public boolean isNotActive() { public boolean isNotActive() {
@@ -267,11 +267,7 @@ public final class Group extends AbstractPersistableCustom {
} }
public boolean isActive() { public boolean isActive() {
return this.status != null ? GroupingTypeStatus.fromInt(this.status).isActive() : false; return this.status != null && GroupingTypeStatus.fromInt(this.status).isActive();
}
private boolean isDateInTheFuture(final LocalDate localDate) {
return localDate.isAfter(DateUtils.getBusinessLocalDate());
} }
public boolean isNotPending() { public boolean isNotPending() {
@@ -316,7 +312,7 @@ public final class Group extends AbstractPersistableCustom {
final String dateFormatAsInput = command.dateFormat(); final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale(); final String localeAsInput = command.locale();
if (command.isChangeInLocalDateParameterNamed(GroupingTypesApiConstants.activationDateParamName, getActivationLocalDate())) { if (command.isChangeInLocalDateParameterNamed(GroupingTypesApiConstants.activationDateParamName, getActivationDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(GroupingTypesApiConstants.activationDateParamName); final String valueAsInput = command.stringValueOfParameterNamed(GroupingTypesApiConstants.activationDateParamName);
actualChanges.put(GroupingTypesApiConstants.activationDateParamName, valueAsInput); actualChanges.put(GroupingTypesApiConstants.activationDateParamName, valueAsInput);
actualChanges.put(GroupingTypesApiConstants.dateFormatParamName, dateFormatAsInput); actualChanges.put(GroupingTypesApiConstants.dateFormatParamName, dateFormatAsInput);
@@ -347,7 +343,7 @@ public final class Group extends AbstractPersistableCustom {
return this.submittedOnDate; return this.submittedOnDate;
} }
public LocalDate getActivationLocalDate() { public LocalDate getActivationDate() {
return this.activationDate; return this.activationDate;
} }
@@ -494,7 +490,7 @@ public final class Group extends AbstractPersistableCustom {
} }
public boolean isChildGroup() { public boolean isChildGroup() {
return this.parent == null ? false : true; return this.parent != null;
} }
@@ -503,18 +499,17 @@ public final class Group extends AbstractPersistableCustom {
} }
public void close(final AppUser currentUser, final CodeValue closureReason, final LocalDate closureDate) { public void close(final AppUser currentUser, final CodeValue closureReason, final LocalDate closureDate) {
if (isClosed()) { if (isClosed()) {
final String errorMessage = "Group with identifier " + getId() + " is alread closed."; final String errorMessage = "Group with identifier " + getId() + " is alread closed.";
throw new InvalidGroupStateTransitionException(this.groupLevel.getLevelName(), "close", "already.closed", errorMessage, throw new InvalidGroupStateTransitionException(this.groupLevel.getLevelName(), "close", "already.closed", errorMessage,
getId()); getId());
} }
if (isNotPending() && getActivationLocalDate().isAfter(closureDate)) { if (isNotPending() && DateUtils.isAfter(getActivationDate(), closureDate)) {
final String errorMessage = "The Group closure Date " + closureDate + " cannot be before the group Activation Date " final String errorMessage = "The Group closure Date " + closureDate + " cannot be before the group Activation Date "
+ getActivationLocalDate() + "."; + getActivationDate() + ".";
throw new InvalidGroupStateTransitionException(this.groupLevel.getLevelName(), "close", throw new InvalidGroupStateTransitionException(this.groupLevel.getLevelName(), "close",
"date.cannot.before.group.actvation.date", errorMessage, closureDate, getActivationLocalDate()); "date.cannot.before.group.actvation.date", errorMessage, closureDate, getActivationDate());
} }
this.closureReason = closureReason; this.closureReason = closureReason;
@@ -644,9 +639,7 @@ public final class Group 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 on date cannot be in the future."; final String defaultUserMessage = "Submitted on date cannot be in the future.";
final String globalisationMessageCode = "error.msg.group.submittedOnDate.in.the.future"; final String globalisationMessageCode = "error.msg.group.submittedOnDate.in.the.future";
final ApiParameterError error = ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage, final ApiParameterError error = ApiParameterError.parameterError(globalisationMessageCode, defaultUserMessage,
@@ -654,31 +647,26 @@ public final class Group extends AbstractPersistableCustom {
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 on date cannot be after the activation date"; final String defaultUserMessage = "Submitted on date cannot be after the activation date";
final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.submittedOnDate.after.activation.date", final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.submittedOnDate.after.activation.date",
defaultUserMessage, GroupingTypesApiConstants.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, GroupingTypesApiConstants.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getActivationDate() != null && 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.group.activationDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.group.activationDate.in.the.future",
defaultUserMessage, GroupingTypesApiConstants.activationDateParamName, getActivationLocalDate()); defaultUserMessage, GroupingTypesApiConstants.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 = "Activation date cannot be a date before the office opening date."; final String defaultUserMessage = "Activation date cannot be a date before the office opening date.";
final ApiParameterError error = ApiParameterError.parameterError( final ApiParameterError error = ApiParameterError.parameterError(
"error.msg.group.activationDate.cannot.be.before.office.activation.date", defaultUserMessage, "error.msg.group.activationDate.cannot.be.before.office.activation.date", defaultUserMessage,
GroupingTypesApiConstants.activationDateParamName, getActivationLocalDate()); GroupingTypesApiConstants.activationDateParamName, getActivationDate());
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
} }
@@ -402,7 +402,7 @@ public final class SavingsAccountTransactionData implements Serializable {
} }
public boolean occursOn(final LocalDate occursOnDate) { public boolean occursOn(final LocalDate occursOnDate) {
return getTransactionDate().isEqual(occursOnDate); return DateUtils.isEqual(occursOnDate, getTransactionDate());
} }
public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy, public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy,
@@ -416,7 +416,7 @@ public final class SavingsAccountTransactionData implements Serializable {
LocalDate balanceStartDate = getTransactionDate(); LocalDate balanceStartDate = getTransactionDate();
LocalDate balanceEndDate = getEndOfBalanceLocalDate(); LocalDate balanceEndDate = getEndOfBalanceLocalDate();
if (boundedBy.startDate().isAfter(balanceStartDate)) { if (DateUtils.isAfter(boundedBy.startDate(), balanceStartDate)) {
balanceStartDate = boundedBy.startDate(); balanceStartDate = boundedBy.startDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -432,7 +432,7 @@ public final class SavingsAccountTransactionData implements Serializable {
} }
} }
if (balanceEndDate.isAfter(boundedBy.endDate())) { if (DateUtils.isAfter(balanceEndDate, boundedBy.endDate())) {
balanceEndDate = boundedBy.endDate(); balanceEndDate = boundedBy.endDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -479,7 +479,7 @@ public final class SavingsAccountTransactionData implements Serializable {
public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) { public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
// balance end date should not be before transaction date // balance end date should not be before transaction date
if (endOfBalanceDate != null && endOfBalanceDate.isBefore(this.transactionDate)) { if (endOfBalanceDate != null && DateUtils.isBefore(endOfBalanceDate, this.transactionDate)) {
this.balanceEndDate = this.transactionDate; this.balanceEndDate = this.transactionDate;
} else if (endOfBalanceDate != null) { } else if (endOfBalanceDate != null) {
this.balanceEndDate = endOfBalanceDate; this.balanceEndDate = endOfBalanceDate;
@@ -501,7 +501,7 @@ public final class SavingsAccountTransactionData implements Serializable {
public boolean isFeeCharge() { public boolean isFeeCharge() {
final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy(); final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
return (isPayCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false; return isPayCharge() && chargePaidBy != null && chargePaidBy.isFeeCharge();
} }
public void setChargesPaidByData(final SavingsAccountChargesPaidByData savingsAccountChargesPaidByData) { public void setChargesPaidByData(final SavingsAccountChargesPaidByData savingsAccountChargesPaidByData) {
@@ -518,7 +518,7 @@ public final class SavingsAccountTransactionData implements Serializable {
public boolean isPenaltyCharge() { public boolean isPenaltyCharge() {
final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy(); final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
return (isPayCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false; return isPayCharge() && chargePaidBy != null && chargePaidBy.isPenaltyCharge();
} }
public boolean isWaiveFeeChargeAndNotReversed() { public boolean isWaiveFeeChargeAndNotReversed() {
@@ -527,7 +527,7 @@ public final class SavingsAccountTransactionData implements Serializable {
public boolean isWaiveFeeCharge() { public boolean isWaiveFeeCharge() {
final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy(); final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
return (isWaiveCharge() && chargePaidBy != null) ? chargePaidBy.isFeeCharge() : false; return isWaiveCharge() && chargePaidBy != null && chargePaidBy.isFeeCharge();
} }
public boolean isWaiveCharge() { public boolean isWaiveCharge() {
@@ -540,7 +540,7 @@ public final class SavingsAccountTransactionData implements Serializable {
public boolean isWaivePenaltyCharge() { public boolean isWaivePenaltyCharge() {
final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy(); final SavingsAccountChargesPaidByData chargePaidBy = getSavingsAccountChargePaidBy();
return (isWaiveCharge() && chargePaidBy != null) ? chargePaidBy.isPenaltyCharge() : false; return isWaiveCharge() && chargePaidBy != null && chargePaidBy.isPenaltyCharge();
} }
private SavingsAccountChargesPaidByData getSavingsAccountChargePaidBy() { private SavingsAccountChargesPaidByData getSavingsAccountChargePaidBy() {
@@ -572,7 +572,7 @@ public final class SavingsAccountTransactionData implements Serializable {
thisTransactionData.put("id", getId()); thisTransactionData.put("id", getId());
thisTransactionData.put("officeId", officeId); thisTransactionData.put("officeId", officeId);
thisTransactionData.put("type", transactionType); thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", Boolean.valueOf(isReversed())); thisTransactionData.put("reversed", isReversed());
thisTransactionData.put("date", getTransactionDate()); thisTransactionData.put("date", getTransactionDate());
thisTransactionData.put("currency", currencyData); thisTransactionData.put("currency", currencyData);
thisTransactionData.put("amount", this.amount); thisTransactionData.put("amount", this.amount);
@@ -582,10 +582,8 @@ public final class SavingsAccountTransactionData implements Serializable {
thisTransactionData.put("paymentTypeId", this.paymentDetailData.getPaymentType().getId()); thisTransactionData.put("paymentTypeId", this.paymentDetailData.getPaymentType().getId());
} }
/*** // Sending data in a map, though in savings we currently expect a transaction to always repay a single charge
* Sending data in a map, though in savings we currently expect a transaction to always repay a single charge // (or may repay a part of a single charge too)
* (or may repay a part of a single charge too)
***/
if (!this.chargesPaidByData.isEmpty()) { if (!this.chargesPaidByData.isEmpty()) {
final List<Map<String, Object>> savingsChargesPaidData = new ArrayList<>(); final List<Map<String, Object>> savingsChargesPaidData = new ArrayList<>();
for (final SavingsAccountChargesPaidByData chargePaidBy : this.chargesPaidByData) { for (final SavingsAccountChargesPaidByData chargePaidBy : this.chargesPaidByData) {
@@ -25,6 +25,7 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval; import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
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.account.service.AccountTransfersReadPlatformService; import org.apache.fineract.portfolio.account.service.AccountTransfersReadPlatformService;
@@ -55,8 +56,7 @@ public final class SavingsHelper {
LocalDate periodEndDate = periodStartDate; LocalDate periodEndDate = periodStartDate;
LocalDate actualPeriodStartDate = periodStartDate; LocalDate actualPeriodStartDate = periodStartDate;
while (!periodStartDate.isAfter(interestPostingUpToDate) && !periodEndDate.isAfter(interestPostingUpToDate)) { while (!DateUtils.isAfter(periodStartDate, interestPostingUpToDate) && !DateUtils.isAfter(periodEndDate, interestPostingUpToDate)) {
final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType, final LocalDate interestPostingLocalDate = determineInterestPostingPeriodEndDateFrom(periodStartDate, postingPeriodType,
interestPostingUpToDate, financialYearBeginningMonth); interestPostingUpToDate, financialYearBeginningMonth);
@@ -64,8 +64,7 @@ public final class SavingsHelper {
if (!postInterestAsOn.isEmpty()) { if (!postInterestAsOn.isEmpty()) {
for (LocalDate transactiondate : postInterestAsOn) { for (LocalDate transactiondate : postInterestAsOn) {
if (periodStartDate.isBefore(transactiondate) if (DateUtils.isAfter(transactiondate, periodStartDate) && !DateUtils.isAfter(transactiondate, periodEndDate)) {
&& (periodEndDate.isAfter(transactiondate) || periodEndDate.isEqual(transactiondate))) {
periodEndDate = transactiondate.minusDays(1); periodEndDate = transactiondate.minusDays(1);
actualPeriodStartDate = periodEndDate; actualPeriodStartDate = periodEndDate;
break; break;
@@ -75,7 +74,7 @@ public final class SavingsHelper {
postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate)); postingPeriods.add(LocalDateInterval.create(periodStartDate, periodEndDate));
if (actualPeriodStartDate.isEqual(periodEndDate)) { if (DateUtils.isEqual(actualPeriodStartDate, periodEndDate)) {
periodEndDate = actualPeriodStartDate.plusDays(1); periodEndDate = actualPeriodStartDate.plusDays(1);
periodStartDate = actualPeriodStartDate.plusDays(1); periodStartDate = actualPeriodStartDate.plusDays(1);
} else { } else {
@@ -128,7 +127,7 @@ public final class SavingsHelper {
break; break;
case QUATERLY: case QUATERLY:
for (LocalDate quarterlyDate : quarterlyDates) { for (LocalDate quarterlyDate : quarterlyDates) {
if (quarterlyDate.isAfter(periodStartDate)) { if (DateUtils.isAfter(quarterlyDate, periodStartDate)) {
periodEndDate = quarterlyDate; periodEndDate = quarterlyDate;
isEndDateSet = true; isEndDateSet = true;
break; break;
@@ -141,7 +140,7 @@ public final class SavingsHelper {
break; break;
case BIANNUAL: case BIANNUAL:
for (LocalDate biannualDate : biannualDates) { for (LocalDate biannualDate : biannualDates) {
if (biannualDate.isAfter(periodStartDate)) { if (DateUtils.isAfter(biannualDate, periodStartDate)) {
periodEndDate = biannualDate; periodEndDate = biannualDate;
isEndDateSet = true; isEndDateSet = true;
break; break;
@@ -21,6 +21,7 @@ package org.apache.fineract.portfolio.savings.domain.interest;
import java.math.BigDecimal; import java.math.BigDecimal;
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.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;
@@ -59,7 +60,7 @@ public class CompoundInterestHelper {
// account and if already transfered then it includes in interest // account and if already transfered then it includes in interest
// calculation. // calculation.
if (!(postingPeriod.isInterestTransfered() || !interestTransferEnabled if (!(postingPeriod.isInterestTransfered() || !interestTransferEnabled
|| (lockUntil != null && !postingPeriod.dateOfPostingTransaction().isAfter(lockUntil)))) { || (lockUntil != null && !DateUtils.isAfter(postingPeriod.dateOfPostingTransaction(), lockUntil)))) {
compoundInterestValues.setcompoundedInterest(BigDecimal.ZERO); compoundInterestValues.setcompoundedInterest(BigDecimal.ZERO);
} }
} }
@@ -22,6 +22,7 @@ import java.math.BigDecimal;
import java.math.MathContext; import java.math.MathContext;
import java.time.LocalDate; import java.time.LocalDate;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval; import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.Money; import org.apache.fineract.organisation.monetary.domain.Money;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
@@ -136,7 +137,6 @@ public class EndOfDayBalance {
* @return * @return
*/ */
public EndOfDayBalance upTo(final LocalDateInterval compoundingPeriodInterval, final LocalDate upToInterestCalculationDate) { public EndOfDayBalance upTo(final LocalDateInterval compoundingPeriodInterval, final LocalDate upToInterestCalculationDate) {
Money startingBalance = this.openingBalance; Money startingBalance = this.openingBalance;
LocalDate balanceStartDate = this.date; LocalDate balanceStartDate = this.date;
@@ -144,7 +144,7 @@ public class EndOfDayBalance {
int daysOfBalance = this.numberOfDays; int daysOfBalance = this.numberOfDays;
if (this.date.isBefore(compoundingPeriodInterval.startDate())) { if (DateUtils.isBefore(this.date, compoundingPeriodInterval.startDate())) {
balanceStartDate = compoundingPeriodInterval.startDate(); balanceStartDate = compoundingPeriodInterval.startDate();
startingBalance = this.endOfDayBalance; startingBalance = this.endOfDayBalance;
final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, oldBalanceEndDate); final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, oldBalanceEndDate);
@@ -152,12 +152,12 @@ public class EndOfDayBalance {
} }
LocalDate balanceEndDate = balanceStartDate.plusDays(daysOfBalance - 1); LocalDate balanceEndDate = balanceStartDate.plusDays(daysOfBalance - 1);
if (balanceEndDate.isAfter(compoundingPeriodInterval.endDate())) { if (DateUtils.isAfter(balanceEndDate, compoundingPeriodInterval.endDate())) {
balanceEndDate = compoundingPeriodInterval.endDate(); balanceEndDate = compoundingPeriodInterval.endDate();
final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate);
daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate(); daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate();
} }
if (balanceEndDate.isAfter(upToInterestCalculationDate)) { if (DateUtils.isAfter(balanceEndDate, upToInterestCalculationDate)) {
balanceEndDate = upToInterestCalculationDate; balanceEndDate = upToInterestCalculationDate;
final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval balancePeriodInterval = LocalDateInterval.create(balanceStartDate, balanceEndDate);
daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate(); daysOfBalance = balancePeriodInterval.daysInPeriodInclusiveOfEndDate();
@@ -167,7 +167,6 @@ public class EndOfDayBalance {
} }
public boolean contains(final LocalDateInterval compoundingPeriodInterval) { public boolean contains(final LocalDateInterval compoundingPeriodInterval) {
final LocalDate balanceUpToDate = this.date.plusDays(this.numberOfDays - 1); final LocalDate balanceUpToDate = this.date.plusDays(this.numberOfDays - 1);
final LocalDateInterval balanceInterval = LocalDateInterval.create(this.date, balanceUpToDate); final LocalDateInterval balanceInterval = LocalDateInterval.create(this.date, balanceUpToDate);
@@ -132,7 +132,7 @@ public final class PostingPeriod {
LocalDate balanceEndDate = periodInterval.endDate(); LocalDate balanceEndDate = periodInterval.endDate();
Integer numberOfDaysOfBalance = periodInterval.daysInPeriodInclusiveOfEndDate(); Integer numberOfDaysOfBalance = periodInterval.daysInPeriodInclusiveOfEndDate();
if (balanceEndDate.isAfter(upToInterestCalculationDate)) { if (DateUtils.isAfter(balanceEndDate, upToInterestCalculationDate)) {
balanceEndDate = upToInterestCalculationDate; balanceEndDate = upToInterestCalculationDate;
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -206,7 +206,7 @@ public final class PostingPeriod {
LocalDate balanceEndDate = periodInterval.endDate(); LocalDate balanceEndDate = periodInterval.endDate();
Integer numberOfDaysOfBalance = periodInterval.daysInPeriodInclusiveOfEndDate(); Integer numberOfDaysOfBalance = periodInterval.daysInPeriodInclusiveOfEndDate();
if (balanceEndDate.isAfter(upToInterestCalculationDate)) { if (DateUtils.isAfter(balanceEndDate, upToInterestCalculationDate)) {
balanceEndDate = upToInterestCalculationDate; balanceEndDate = upToInterestCalculationDate;
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -328,23 +328,21 @@ public final class PostingPeriod {
compoundingPeriods.add(compoundingPeriod); compoundingPeriods.add(compoundingPeriod);
break; break;
case MONTHLY: case MONTHLY:
final LocalDate postingPeriodEndDate = postingPeriodInterval.endDate(); final LocalDate postingPeriodEndDate = postingPeriodInterval.endDate();
LocalDate periodStartDate = postingPeriodInterval.startDate(); LocalDate periodStartDate = postingPeriodInterval.startDate();
LocalDate periodEndDate = periodStartDate; LocalDate periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(postingPeriodEndDate) && !periodEndDate.isAfter(postingPeriodEndDate)) { while (!DateUtils.isAfter(periodStartDate, postingPeriodEndDate)
&& !DateUtils.isAfter(periodEndDate, postingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate, periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate,
financialYearBeginningMonth); financialYearBeginningMonth);
if (periodEndDate.isAfter(postingPeriodEndDate)) { if (DateUtils.isAfter(periodEndDate, postingPeriodEndDate)) {
periodEndDate = postingPeriodEndDate; periodEndDate = postingPeriodEndDate;
} }
final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate); final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate);
if (postingPeriodInterval.contains(compoundingPeriodInterval)) { if (postingPeriodInterval.contains(compoundingPeriodInterval)) {
compoundingPeriod = MonthlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances, compoundingPeriod = MonthlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances,
upToInterestCalculationDate); upToInterestCalculationDate);
compoundingPeriods.add(compoundingPeriod); compoundingPeriods.add(compoundingPeriod);
@@ -364,17 +362,16 @@ public final class PostingPeriod {
periodStartDate = postingPeriodInterval.startDate(); periodStartDate = postingPeriodInterval.startDate();
periodEndDate = periodStartDate; periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(qPostingPeriodEndDate) && !periodEndDate.isAfter(qPostingPeriodEndDate)) { while (!DateUtils.isAfter(periodStartDate, qPostingPeriodEndDate)
&& !DateUtils.isAfter(periodEndDate, qPostingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate, periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate,
financialYearBeginningMonth); financialYearBeginningMonth);
if (periodEndDate.isAfter(qPostingPeriodEndDate)) { if (DateUtils.isAfter(periodEndDate, qPostingPeriodEndDate)) {
periodEndDate = qPostingPeriodEndDate; periodEndDate = qPostingPeriodEndDate;
} }
final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate); final LocalDateInterval compoundingPeriodInterval = LocalDateInterval.create(periodStartDate, periodEndDate);
if (postingPeriodInterval.contains(compoundingPeriodInterval)) { if (postingPeriodInterval.contains(compoundingPeriodInterval)) {
compoundingPeriod = QuarterlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances, compoundingPeriod = QuarterlyCompoundingPeriod.create(compoundingPeriodInterval, allEndOfDayBalances,
upToInterestCalculationDate); upToInterestCalculationDate);
compoundingPeriods.add(compoundingPeriod); compoundingPeriods.add(compoundingPeriod);
@@ -390,11 +387,11 @@ public final class PostingPeriod {
periodStartDate = postingPeriodInterval.startDate(); periodStartDate = postingPeriodInterval.startDate();
periodEndDate = periodStartDate; periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(bPostingPeriodEndDate) && !periodEndDate.isAfter(bPostingPeriodEndDate)) { while (!DateUtils.isAfter(periodStartDate, bPostingPeriodEndDate)
&& !DateUtils.isAfter(periodEndDate, bPostingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate, periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate,
financialYearBeginningMonth); financialYearBeginningMonth);
if (periodEndDate.isAfter(bPostingPeriodEndDate)) { if (DateUtils.isAfter(periodEndDate, bPostingPeriodEndDate)) {
periodEndDate = bPostingPeriodEndDate; periodEndDate = bPostingPeriodEndDate;
} }
@@ -416,11 +413,12 @@ public final class PostingPeriod {
periodStartDate = postingPeriodInterval.startDate(); periodStartDate = postingPeriodInterval.startDate();
periodEndDate = periodStartDate; periodEndDate = periodStartDate;
while (!periodStartDate.isAfter(aPostingPeriodEndDate) && !periodEndDate.isAfter(aPostingPeriodEndDate)) { while (!DateUtils.isAfter(periodStartDate, aPostingPeriodEndDate)
&& !DateUtils.isAfter(periodEndDate, aPostingPeriodEndDate)) {
periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate, periodEndDate = determineInterestPeriodEndDateFrom(periodStartDate, interestPeriodType, upToInterestCalculationDate,
financialYearBeginningMonth); financialYearBeginningMonth);
if (periodEndDate.isAfter(aPostingPeriodEndDate)) { if (DateUtils.isAfter(periodEndDate, aPostingPeriodEndDate)) {
periodEndDate = aPostingPeriodEndDate; periodEndDate = aPostingPeriodEndDate;
} }
@@ -483,7 +481,7 @@ public final class PostingPeriod {
case ANNUAL: case ANNUAL:
periodEndDate = periodStartDate.withMonth(previousMonth); periodEndDate = periodStartDate.withMonth(previousMonth);
periodEndDate = periodEndDate.with(TemporalAdjusters.lastDayOfMonth()); periodEndDate = periodEndDate.with(TemporalAdjusters.lastDayOfMonth());
if (periodEndDate.isBefore(periodStartDate)) { if (DateUtils.isBefore(periodEndDate, periodStartDate)) {
periodEndDate = periodEndDate.plusYears(1); periodEndDate = periodEndDate.plusYears(1);
} }
break; break;
@@ -23,6 +23,7 @@ import java.time.LocalDate;
import lombok.Getter; import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval; import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
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;
@@ -54,7 +55,7 @@ public class SavingsAccountTransactionDetailsForPostingPeriod {
} }
public boolean occursOn(final LocalDate occursOnDate) { public boolean occursOn(final LocalDate occursOnDate) {
return getTransactionDate().isEqual(occursOnDate); return DateUtils.isEqual(occursOnDate, getTransactionDate());
} }
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) { public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
@@ -92,7 +93,6 @@ public class SavingsAccountTransactionDetailsForPostingPeriod {
} }
public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) { public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) {
final MonetaryCurrency currency = openingBalance.getCurrency(); final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy(); Money endOfDayBalance = openingBalance.copy();
@@ -101,7 +101,7 @@ public class SavingsAccountTransactionDetailsForPostingPeriod {
LocalDate balanceStartDate = getTransactionDate(); LocalDate balanceStartDate = getTransactionDate();
LocalDate balanceEndDate = getEndOfBalanceDate(); LocalDate balanceEndDate = getEndOfBalanceDate();
if (boundedBy.startDate().isAfter(balanceStartDate)) { if (DateUtils.isAfter(boundedBy.startDate(), balanceStartDate)) {
balanceStartDate = boundedBy.startDate(); balanceStartDate = boundedBy.startDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -121,7 +121,7 @@ public class SavingsAccountTransactionDetailsForPostingPeriod {
} }
} }
if (balanceEndDate.isAfter(boundedBy.endDate())) { if (DateUtils.isAfter(balanceEndDate, boundedBy.endDate())) {
balanceEndDate = boundedBy.endDate(); balanceEndDate = boundedBy.endDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -1609,7 +1609,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date."; final String errorMessage = "The date on which a loan is submitted cannot be earlier than client's activation date.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage, throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.client.activation.date", errorMessage,
getSubmittedOnDate()); getSubmittedOnDate());
} else if (this.group != null && DateUtils.isBefore(getSubmittedOnDate(), this.group.getActivationLocalDate())) { } else if (this.group != null && DateUtils.isBefore(getSubmittedOnDate(), this.group.getActivationDate())) {
final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date."; final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage,
getSubmittedOnDate()); getSubmittedOnDate());
@@ -2124,7 +2124,7 @@ public class Loan extends AbstractAuditableWithUTCDateTimeCustom {
if (this.group != null && this.group.isActivatedAfter(submittedOn)) { if (this.group != null && this.group.isActivatedAfter(submittedOn)) {
final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date."; final String errorMessage = "The date on which a loan is submitted cannot be earlier than groups's activation date.";
throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, submittedOn, throw new InvalidLoanStateTransitionException("submittal", "cannot.be.before.group.activation.date", errorMessage, submittedOn,
group.getActivationLocalDate()); group.getActivationDate());
} }
if (DateUtils.isAfter(submittedOn, getExpectedDisbursedOnLocalDate())) { if (DateUtils.isAfter(submittedOn, getExpectedDisbursedOnLocalDate())) {
@@ -850,6 +850,7 @@ public class LoanTransaction extends AbstractAuditableWithUTCDateTimeCustom {
this.manuallyAdjustedOrReversed = true; this.manuallyAdjustedOrReversed = true;
} }
@Override
public OffsetDateTime getCreatedDateTime() { public OffsetDateTime getCreatedDateTime() {
return (this.getCreatedDate().isPresent() ? this.getCreatedDate().get() : DateUtils.getOffsetDateTimeOfTenantWithMostPrecision()); return (this.getCreatedDate().isPresent() ? this.getCreatedDate().get() : DateUtils.getOffsetDateTimeOfTenantWithMostPrecision());
} }
@@ -119,7 +119,7 @@ public class SmsCampaign extends AbstractPersistableCustom {
private SmsCampaign(final String campaignName, final Integer campaignType, final Integer triggerType, final Report businessRuleId, private SmsCampaign(final String campaignName, final Integer campaignType, final Integer triggerType, final Report businessRuleId,
final Long providerId, final String paramValue, final String message, final LocalDate submittedOnDate, final Long providerId, final String paramValue, final String message, final LocalDate submittedOnDate,
final AppUser submittedBy, final String recurrence, final LocalDateTime localDateTime, final boolean isNotification) { final AppUser submittedBy, final String recurrence, final LocalDateTime recurrenceStartDate, final boolean isNotification) {
this.campaignName = campaignName; this.campaignName = campaignName;
this.campaignType = campaignType; this.campaignType = campaignType;
this.triggerType = SmsCampaignTriggerType.fromInt(triggerType).getValue(); this.triggerType = SmsCampaignTriggerType.fromInt(triggerType).getValue();
@@ -131,18 +131,12 @@ public class SmsCampaign extends AbstractPersistableCustom {
this.submittedOnDate = submittedOnDate; this.submittedOnDate = submittedOnDate;
this.submittedBy = submittedBy; this.submittedBy = submittedBy;
this.recurrence = recurrence; this.recurrence = recurrence;
LocalDateTime recurrenceStartDate = LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant());
this.isVisible = true; this.isVisible = true;
if (localDateTime != null) { this.recurrenceStartDate = recurrenceStartDate;
this.recurrenceStartDate = localDateTime;
} else {
this.recurrenceStartDate = recurrenceStartDate;
}
this.isNotification = isNotification; this.isNotification = isNotification;
} }
public static SmsCampaign instance(final AppUser submittedBy, final Report report, final JsonCommand command) { public static SmsCampaign instance(final AppUser submittedBy, final Report report, final JsonCommand command) {
final String campaignName = command.stringValueOfParameterNamed(SmsCampaignValidator.campaignName); final String campaignName = command.stringValueOfParameterNamed(SmsCampaignValidator.campaignName);
final Long campaignType = command.longValueOfParameterNamed(SmsCampaignValidator.campaignType); final Long campaignType = command.longValueOfParameterNamed(SmsCampaignValidator.campaignType);
final Long triggerType = command.longValueOfParameterNamed(SmsCampaignValidator.triggerType); final Long triggerType = command.longValueOfParameterNamed(SmsCampaignValidator.triggerType);
@@ -164,10 +158,11 @@ public class SmsCampaign extends AbstractPersistableCustom {
} }
String recurrence = null; String recurrence = null;
LocalDateTime recurrenceStartDate = LocalDateTime.now(DateUtils.getDateTimeZoneOfTenant()); LocalDateTime recurrenceStartDate = null;
if (SmsCampaignTriggerType.fromInt(triggerType.intValue()).isSchedule()) { if (SmsCampaignTriggerType.fromInt(triggerType.intValue()).isSchedule()) {
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
String dateTimeFormat; String dateTimeFormat;
recurrenceStartDate = DateUtils.getLocalDateTimeOfTenant();
if (command.hasParameter(SmsCampaignValidator.dateTimeFormat)) { if (command.hasParameter(SmsCampaignValidator.dateTimeFormat)) {
dateTimeFormat = command.stringValueOfParameterNamed(SmsCampaignValidator.dateTimeFormat); dateTimeFormat = command.stringValueOfParameterNamed(SmsCampaignValidator.dateTimeFormat);
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(dateTimeFormat).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(dateTimeFormat).withLocale(locale);
@@ -177,8 +172,6 @@ public class SmsCampaign extends AbstractPersistableCustom {
} }
recurrence = constructRecurrence(command); recurrence = constructRecurrence(command);
} }
} else {
recurrenceStartDate = null;
} }
return new SmsCampaign(campaignName, campaignType.intValue(), triggerType.intValue(), report, providerId, paramValue, message, return new SmsCampaign(campaignName, campaignType.intValue(), triggerType.intValue(), report, providerId, paramValue, message,
@@ -186,7 +179,6 @@ public class SmsCampaign 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(SmsCampaignValidator.campaignName, this.campaignName)) { if (command.isChangeInStringParameterNamed(SmsCampaignValidator.campaignName, this.campaignName)) {
@@ -255,7 +247,6 @@ public class SmsCampaign 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.";
@@ -297,7 +288,6 @@ public class SmsCampaign 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.";
@@ -384,66 +374,55 @@ public class SmsCampaign extends AbstractPersistableCustom {
} }
private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) { private void validateActivationDate(final List<ApiParameterError> dataValidationErrors) {
if (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, SmsCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, SmsCampaignValidator.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.campaign.submittedOnDate.after.activation.date", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.submittedOnDate.after.activation.date",
defaultUserMessage, SmsCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, SmsCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
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.campaign.activationDate.in.the.future", final ApiParameterError error = ApiParameterError.parameterError("error.msg.campaign.activationDate.in.the.future",
defaultUserMessage, SmsCampaignValidator.activationDateParamName, getActivationLocalDate()); defaultUserMessage, SmsCampaignValidator.activationDateParamName, getActivationDate());
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
} }
private void validateReactivationDate(final List<ApiParameterError> dataValidationErrors) { private void validateReactivationDate(final List<ApiParameterError> dataValidationErrors) {
if (getActivationLocalDate() != null && isDateInTheFuture(getActivationLocalDate())) { if (DateUtils.isDateInTheFuture(getActivationDate())) {
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, SmsCampaignValidator.activationDateParamName, getActivationLocalDate()); defaultUserMessage, SmsCampaignValidator.activationDateParamName, getActivationDate());
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
if (getActivationLocalDate() != null && getSubmittedOnDate() != null && getSubmittedOnDate().isAfter(getActivationLocalDate())) { if (getActivationDate() != null && DateUtils.isAfter(getSubmittedOnDate(), getActivationDate())) {
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, SmsCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, SmsCampaignValidator.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, SmsCampaignValidator.submittedOnDateParamName, this.submittedOnDate); defaultUserMessage, SmsCampaignValidator.submittedOnDateParamName, this.submittedOnDate);
dataValidationErrors.add(error); dataValidationErrors.add(error);
} }
} }
private void validateClosureDate(final List<ApiParameterError> dataValidationErrors) { private void validateClosureDate(final List<ApiParameterError> dataValidationErrors) {
if (getClosureDate() != null && isDateInTheFuture(getClosureDate())) { if (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, SmsCampaignValidator.closureDateParamName, this.closureDate); defaultUserMessage, SmsCampaignValidator.closureDateParamName, this.closureDate);
@@ -461,14 +440,10 @@ public class SmsCampaign extends AbstractPersistableCustom {
return this.closureDate; return this.closureDate;
} }
public LocalDate getActivationLocalDate() { public LocalDate getActivationDate() {
return this.approvedOnDate; return this.approvedOnDate;
} }
private boolean isDateInTheFuture(final LocalDate localDate) {
return localDate.isAfter(DateUtils.getBusinessLocalDate());
}
public Report getBusinessRuleId() { public Report getBusinessRuleId() {
return this.businessRuleId; return this.businessRuleId;
} }
@@ -365,7 +365,7 @@ public class SmsCampaignDomainServiceImpl implements SmsCampaignDomainService {
smsParams.put("depositAmount", savingsAccountTransaction.getAmount(savingsAccount.getCurrency())); smsParams.put("depositAmount", savingsAccountTransaction.getAmount(savingsAccount.getCurrency()));
smsParams.put("balance", savingsAccount.getWithdrawableBalance()); smsParams.put("balance", savingsAccount.getWithdrawableBalance());
smsParams.put("officeId", client.getOffice().getId()); smsParams.put("officeId", client.getOffice().getId());
smsParams.put("transactionDate", savingsAccountTransaction.getTransactionLocalDate().format(dateFormatter)); smsParams.put("transactionDate", savingsAccountTransaction.getTransactionDate().format(dateFormatter));
smsParams.put("savingsTransactionId", savingsAccountTransaction.getId()); smsParams.put("savingsTransactionId", savingsAccountTransaction.getId());
if (client.getStaff() != null) { if (client.getStaff() != null) {
@@ -30,7 +30,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.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@@ -54,12 +53,10 @@ 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.GeneralPlatformDomainRuleException; import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
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.ReportRepository; import org.apache.fineract.infrastructure.dataqueries.domain.ReportRepository;
@@ -107,16 +104,16 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
@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.smsCampaignValidator.validateCreate(command.json()); this.smsCampaignValidator.validateCreate(command.json());
final Long runReportId = command.longValueOfParameterNamed(SmsCampaignValidator.runReportId); final Long runReportId = command.longValueOfParameterNamed(SmsCampaignValidator.runReportId);
Report report = this.reportRepository.findById(runReportId).orElseThrow(() -> new ReportNotFoundException(runReportId)); Report report = this.reportRepository.findById(runReportId).orElseThrow(() -> new ReportNotFoundException(runReportId));
LocalDateTime tenantDateTime = DateUtils.getLocalDateTimeOfTenant();
SmsCampaign smsCampaign = SmsCampaign.instance(currentUser, report, command); SmsCampaign smsCampaign = SmsCampaign.instance(currentUser, report, command);
if (smsCampaign.getRecurrenceStartDate() != null LocalDateTime recurrenceStartDate = smsCampaign.getRecurrenceStartDate();
&& smsCampaign.getRecurrenceStartDate().isBefore(DateUtils.getLocalDateTimeOfTenant())) { if (recurrenceStartDate != null && DateUtils.isBefore(recurrenceStartDate, tenantDateTime)) {
throw new GeneralPlatformDomainRuleException("error.msg.campaign.recurrenceStartDate.in.the.past", throw new GeneralPlatformDomainRuleException("error.msg.campaign.recurrenceStartDate.in.the.past",
"Recurrence start date cannot be the past date.", smsCampaign.getRecurrenceStartDate()); "Recurrence start date cannot be the past date.", recurrenceStartDate);
} }
this.smsCampaignRepository.saveAndFlush(smsCampaign); this.smsCampaignRepository.saveAndFlush(smsCampaign);
@@ -380,17 +377,15 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
if (smsCampaign.isDirect()) { if (smsCampaign.isDirect()) {
insertDirectCampaignIntoSmsOutboundTable(smsCampaign); insertDirectCampaignIntoSmsOutboundTable(smsCampaign);
} else if (smsCampaign.isSchedule()) { } else if (smsCampaign.isSchedule()) {
// if recurrence start date is in the future calculate next trigger date if not use recurrence start date us
/** // 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 (smsCampaign.getRecurrenceStartDateTime().isBefore(tenantDateTime())) { LocalDateTime tenantDateTime = DateUtils.getLocalDateTimeOfTenant();
nextTriggerDate = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), smsCampaign.getRecurrenceStartDate(), LocalDateTime recurrenceStartDate = smsCampaign.getRecurrenceStartDate();
DateUtils.getLocalDateTimeOfTenant()); if (DateUtils.isBefore(recurrenceStartDate, tenantDateTime)) {
nextTriggerDate = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), recurrenceStartDate, tenantDateTime);
} else { } else {
nextTriggerDate = smsCampaign.getRecurrenceStartDate(); nextTriggerDate = recurrenceStartDate;
} }
smsCampaign.setNextTriggerDate(nextTriggerDate); smsCampaign.setNextTriggerDate(nextTriggerDate);
@@ -517,7 +512,6 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
@Transactional @Transactional
@Override @Override
public CommandProcessingResult reactivateSmsCampaign(final Long campaignId, JsonCommand command) { public CommandProcessingResult reactivateSmsCampaign(final Long campaignId, JsonCommand command) {
this.smsCampaignValidator.validateActivation(command.json()); this.smsCampaignValidator.validateActivation(command.json());
final AppUser currentUser = this.context.authenticatedUser(); final AppUser currentUser = this.context.authenticatedUser();
@@ -532,21 +526,16 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
if (smsCampaign.isDirect()) { if (smsCampaign.isDirect()) {
insertDirectCampaignIntoSmsOutboundTable(smsCampaign); insertDirectCampaignIntoSmsOutboundTable(smsCampaign);
} else if (smsCampaign.isSchedule()) { } else if (smsCampaign.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 (smsCampaign.getRecurrenceStartDateTime().isBefore(tenantDateTime())) { LocalDateTime tenantDateTime = DateUtils.getLocalDateTimeOfTenant();
nextTriggerDate = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), smsCampaign.getRecurrenceStartDate(), LocalDateTime recurrenceStartDate = smsCampaign.getRecurrenceStartDate();
DateUtils.getLocalDateTimeOfTenant()); if (DateUtils.isBefore(recurrenceStartDate, tenantDateTime)) {
nextTriggerDate = CalendarUtils.getNextRecurringDate(smsCampaign.getRecurrence(), recurrenceStartDate, tenantDateTime);
} else { } else {
nextTriggerDate = smsCampaign.getRecurrenceStartDate(); nextTriggerDate = recurrenceStartDate;
} }
// to get time of tenant
final LocalDateTime getTime = smsCampaign.getRecurrenceStartDateTime();
smsCampaign.setNextTriggerDate(nextTriggerDate); smsCampaign.setNextTriggerDate(nextTriggerDate);
} }
this.smsCampaignRepository.saveAndFlush(smsCampaign); this.smsCampaignRepository.saveAndFlush(smsCampaign);
@@ -555,21 +544,7 @@ public class SmsCampaignWritePlatformServiceJpaImpl implements SmsCampaignWriteP
} }
private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause) { private void handleDataIntegrityIssues(final JsonCommand command, final Throwable realCause) {
throw new PlatformDataIntegrityException("error.msg.sms.campaign.unknown.data.integrity.issue", throw new PlatformDataIntegrityException("error.msg.sms.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;
}
} }
@@ -61,7 +61,7 @@ public class CustomAuditingHandler extends AuditingHandler {
private DateTimeProvider fetchDateTimeProvider(Object bean) { private DateTimeProvider fetchDateTimeProvider(Object bean) {
if (bean instanceof AbstractAuditableWithUTCDateTimeCustom) { if (bean instanceof AbstractAuditableWithUTCDateTimeCustom) {
return CustomDateTimeProvider.TENANT; return CustomDateTimeProvider.UTC;
} else { } else {
return CustomDateTimeProvider.INSTANCE; return CustomDateTimeProvider.INSTANCE;
} }
@@ -18,9 +18,6 @@
*/ */
package org.apache.fineract.infrastructure.core.auditing; package org.apache.fineract.infrastructure.core.auditing;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Optional; import java.util.Optional;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
@@ -29,7 +26,7 @@ import org.springframework.data.auditing.DateTimeProvider;
public enum CustomDateTimeProvider implements DateTimeProvider { public enum CustomDateTimeProvider implements DateTimeProvider {
INSTANCE, TENANT; INSTANCE, UTC;
/* /*
* (non-Javadoc) * (non-Javadoc)
@@ -42,10 +39,10 @@ public enum CustomDateTimeProvider implements DateTimeProvider {
switch (this) { switch (this) {
case INSTANCE -> { case INSTANCE -> {
return Optional.of(LocalDateTime.now(ZoneId.systemDefault())); return Optional.of(DateUtils.getLocalDateTimeOfSystem());
} }
case TENANT -> { case UTC -> {
return Optional.of(OffsetDateTime.now(DateUtils.getDateTimeZoneOfTenant())); return Optional.of(DateUtils.getAuditOffsetDateTime());
} }
} }
throw new UnsupportedOperationException(this + " is not supported!"); throw new UnsupportedOperationException(this + " is not supported!");
@@ -113,7 +113,7 @@ public class InteropAccountData extends CommandProcessingResult {
return new InteropAccountData(account.getExternalId().getValue(), product.getId().toString(), product.getName(), return new InteropAccountData(account.getExternalId().getValue(), product.getId().toString(), product.getName(),
product.getShortName(), account.getCurrency().getCode(), account.getAccountBalance(), account.getWithdrawableBalance(), product.getShortName(), account.getCurrency().getCode(), account.getAccountBalance(), account.getWithdrawableBalance(),
account.getStatus(), subStatus, account.getAccountType(), account.depositAccountType(), account.getActivationLocalDate(), account.getStatus(), subStatus, account.getAccountType(), account.depositAccountType(), account.getActivationDate(),
calcStatusUpdateOn(account), account.getWithdrawnOnDate(), account.retrieveLastTransactionDate(), ids, calcStatusUpdateOn(account), account.getWithdrawnOnDate(), account.retrieveLastTransactionDate(), ids,
account.getClient().getId()); account.getClient().getId());
} }
@@ -125,8 +125,8 @@ public class InteropAccountData extends CommandProcessingResult {
if (account.getWithdrawnOnDate() != null) { if (account.getWithdrawnOnDate() != null) {
return account.getWithdrawnOnDate(); return account.getWithdrawnOnDate();
} }
if (account.getActivationLocalDate() != null) { if (account.getActivationDate() != null) {
return account.getActivationLocalDate(); return account.getActivationDate();
} }
if (account.getRejectedOnDate() != null) { if (account.getRejectedOnDate() != null) {
return account.getRejectedOnDate(); return account.getRejectedOnDate();
@@ -112,8 +112,8 @@ public class InteropTransactionData extends CommandProcessingResult {
String currency = savingsAccount.getCurrency().getCode(); String currency = savingsAccount.getCurrency().getCode();
BigDecimal runningBalance = transaction.getRunningBalance(savingsAccount.getCurrency()).getAmount(); BigDecimal runningBalance = transaction.getRunningBalance(savingsAccount.getCurrency()).getAmount();
LocalDate bookingDateTime = transaction.getTransactionLocalDate(); LocalDate bookingDateTime = transaction.getTransactionDate();
LocalDate endOfBalanceLocalDate = transaction.getEndOfBalanceLocalDate(); LocalDate endOfBalanceLocalDate = transaction.getEndOfBalanceDate();
LocalDate valueDateTime = endOfBalanceLocalDate == null ? bookingDateTime : endOfBalanceLocalDate; LocalDate valueDateTime = endOfBalanceLocalDate == null ? bookingDateTime : endOfBalanceLocalDate;
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@@ -30,6 +30,7 @@ import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime; import java.time.LocalDateTime;
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.savings.domain.SavingsAccount; import org.apache.fineract.portfolio.savings.domain.SavingsAccount;
@Entity @Entity
@@ -67,18 +68,17 @@ public class InteropIdentifier extends AbstractPersistableCustom {
protected InteropIdentifier() {} protected InteropIdentifier() {}
public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String value, String subType, public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String value, String subType,
@NotNull String createdBy, @NotNull LocalDateTime createdOn) { @NotNull String createdBy) {
this.account = account; this.account = account;
this.type = type; this.type = type;
this.value = value; this.value = value;
this.subType = subType; this.subType = subType;
this.createdBy = createdBy; this.createdBy = createdBy;
this.createdOn = createdOn; this.createdOn = DateUtils.getAuditLocalDateTime();
} }
public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String createdBy, public InteropIdentifier(@NotNull SavingsAccount account, @NotNull InteropIdentifierType type, @NotNull String createdBy) {
@NotNull LocalDateTime createdOn) { this(account, type, null, null, createdBy);
this(account, type, null, null, createdBy, createdOn);
} }
public SavingsAccount getAccount() { public SavingsAccount getAccount() {
@@ -243,7 +243,7 @@ public class InteropServiceImpl implements InteropService {
return true; return true;
} }
java.time.LocalDateTime transactionDate = t.getTransactionLocalDate().atStartOfDay(ZoneId.systemDefault()).toLocalDateTime(); java.time.LocalDateTime transactionDate = t.getTransactionDate().atStartOfDay(ZoneId.systemDefault()).toLocalDateTime();
return (transactionsTo == null || transactionsTo.compareTo(transactionDate) > 0) && (transactionsFrom == null return (transactionsTo == null || transactionsTo.compareTo(transactionDate) > 0) && (transactionsFrom == null
|| transactionsFrom.compareTo(transactionDate.withHour(23).withMinute(59).withSecond(59)) <= 0); || transactionsFrom.compareTo(transactionDate.withHour(23).withMinute(59).withSecond(59)) <= 0);
}; };
@@ -276,16 +276,15 @@ public class InteropServiceImpl implements InteropService {
@Override @Override
public InteropIdentifierAccountResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue, public InteropIdentifierAccountResponseData registerAccountIdentifier(@NotNull InteropIdentifierType idType, @NotNull String idValue,
String subIdOrType, @NotNull JsonCommand command) { String subIdOrType, @NotNull JsonCommand command) {
InteropIdentifierRequestData request = dataValidator.validateAndParseCreateIdentifier(idType, idValue, subIdOrType, command); InteropIdentifierRequestData request = dataValidator.validateAndParseCreateIdentifier(idType, idValue, subIdOrType, command);
// TODO: error handling // TODO: error handling
SavingsAccount savingsAccount = validateAndGetSavingAccount(request.getAccountId()); SavingsAccount savingsAccount = validateAndGetSavingAccount(request.getAccountId());
try { try {
AppUser createdBy = getLoginUser(); AppUser createdBy = securityContext.authenticatedUser();
InteropIdentifier identifier = new InteropIdentifier(savingsAccount, request.getIdType(), request.getIdValue(), InteropIdentifier identifier = new InteropIdentifier(savingsAccount, request.getIdType(), request.getIdValue(),
request.getSubIdOrType(), createdBy.getUsername(), DateUtils.getLocalDateTimeOfTenant()); request.getSubIdOrType(), createdBy.getUsername());
identifierRepository.saveAndFlush(identifier); identifierRepository.saveAndFlush(identifier);
@@ -401,10 +400,9 @@ public class InteropServiceImpl implements InteropService {
PaymentDetail paymentDetail = instance(findPaymentType(), savingsAccount.getExternalId().getValue(), null, getRoutingCode(), PaymentDetail paymentDetail = instance(findPaymentType(), savingsAccount.getExternalId().getValue(), null, getRoutingCode(),
transferCode, null); transferCode, null);
SavingsAccountTransaction holdTransaction = SavingsAccountTransaction.holdAmount(savingsAccount, savingsAccount.office(), SavingsAccountTransaction holdTransaction = SavingsAccountTransaction.holdAmount(savingsAccount, savingsAccount.office(),
paymentDetail, transactionDate, Money.of(savingsAccount.getCurrency(), total), DateUtils.getLocalDateTimeOfTenant(), paymentDetail, transactionDate, Money.of(savingsAccount.getCurrency(), total), false);
getLoginUser(), false);
MonetaryCurrency accountCurrency = savingsAccount.getCurrency().copy(); MonetaryCurrency accountCurrency = savingsAccount.getCurrency().copy();
holdTransaction.updateRunningBalance( holdTransaction.setRunningBalance(
Money.of(accountCurrency, savingsAccount.getWithdrawableBalance().subtract(holdTransaction.getAmount()))); Money.of(accountCurrency, savingsAccount.getWithdrawableBalance().subtract(holdTransaction.getAmount())));
holdTransaction.updateCumulativeBalanceAndDates(accountCurrency, transactionDate); holdTransaction.updateCumulativeBalanceAndDates(accountCurrency, transactionDate);
@@ -454,8 +452,8 @@ public class InteropServiceImpl implements InteropService {
} }
if (holdTransaction.getReleaseIdOfHoldAmountTransaction() == null) { if (holdTransaction.getReleaseIdOfHoldAmountTransaction() == null) {
SavingsAccountTransaction releaseTransaction = savingsAccountTransactionRepository.saveAndFlush( SavingsAccountTransaction releaseTransaction = savingsAccountTransactionRepository
releaseAmount(holdTransaction, transactionDate, DateUtils.getLocalDateTimeOfSystem(), getLoginUser())); .saveAndFlush(releaseAmount(holdTransaction, transactionDate));
holdTransaction.updateReleaseId(releaseTransaction.getId()); holdTransaction.updateReleaseId(releaseTransaction.getId());
savingsAccount.releaseOnHoldAmount(holdTransaction.getAmount()); savingsAccount.releaseOnHoldAmount(holdTransaction.getAmount());
savingsAccount.addTransaction(releaseTransaction); savingsAccount.addTransaction(releaseTransaction);
@@ -493,11 +491,10 @@ public class InteropServiceImpl implements InteropService {
SavingsAccountTransaction holdTransaction = findTransaction(savingsAccount, request.getTransferCode(), AMOUNT_HOLD.getValue()); SavingsAccountTransaction holdTransaction = findTransaction(savingsAccount, request.getTransferCode(), AMOUNT_HOLD.getValue());
if (holdTransaction != null && holdTransaction.getReleaseIdOfHoldAmountTransaction() == null) { if (holdTransaction != null && holdTransaction.getReleaseIdOfHoldAmountTransaction() == null) {
SavingsAccountTransaction releaseTransaction = releaseAmount(holdTransaction, transactionDate, SavingsAccountTransaction releaseTransaction = releaseAmount(holdTransaction, transactionDate);
DateUtils.getLocalDateTimeOfSystem(), getLoginUser());
MonetaryCurrency accountCurrency = savingsAccount.getCurrency().copy(); MonetaryCurrency accountCurrency = savingsAccount.getCurrency().copy();
releaseTransaction.updateRunningBalance( releaseTransaction
Money.of(accountCurrency, savingsAccount.getWithdrawableBalance().add(holdTransaction.getAmount()))); .setRunningBalance(Money.of(accountCurrency, savingsAccount.getWithdrawableBalance().add(holdTransaction.getAmount())));
releaseTransaction.updateCumulativeBalanceAndDates(accountCurrency, transactionDate); releaseTransaction.updateCumulativeBalanceAndDates(accountCurrency, transactionDate);
releaseTransaction = savingsAccountTransactionRepository.saveAndFlush(releaseTransaction); releaseTransaction = savingsAccountTransactionRepository.saveAndFlush(releaseTransaction);
holdTransaction.updateReleaseId(releaseTransaction.getId()); holdTransaction.updateReleaseId(releaseTransaction.getId());
@@ -652,10 +649,6 @@ public class InteropServiceImpl implements InteropService {
return identifierRepository.findOneByTypeAndValueAndSubType(idType, idValue, subIdOrType); return identifierRepository.findOneByTypeAndValueAndSubType(idType, idValue, subIdOrType);
} }
private AppUser getLoginUser() {
return securityContext.getAuthenticatedUserIfPresent();
}
/* /*
* Guaranteed to throw an exception no matter what the data integrity issue is. * Guaranteed to throw an exception no matter what the data integrity issue is.
*/ */
@@ -34,6 +34,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.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
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.calendar.CalendarConstants.CalendarSupportedParameters; import org.apache.fineract.portfolio.calendar.CalendarConstants.CalendarSupportedParameters;
import org.apache.fineract.portfolio.calendar.domain.Calendar; import org.apache.fineract.portfolio.calendar.domain.Calendar;
import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType; import org.apache.fineract.portfolio.calendar.domain.CalendarEntityType;
@@ -78,7 +79,7 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
Group centerOrGroup = null; Group centerOrGroup = null;
if (command.getGroupId() != null) { if (command.getGroupId() != null) {
centerOrGroup = this.groupRepository.findOneWithNotFoundDetection(command.getGroupId()); centerOrGroup = this.groupRepository.findOneWithNotFoundDetection(command.getGroupId());
entityActivationDate = centerOrGroup.getActivationLocalDate(); entityActivationDate = centerOrGroup.getActivationDate();
entityType = centerOrGroup.isCenter() ? CalendarEntityType.CENTERS : CalendarEntityType.GROUPS; entityType = centerOrGroup.isCenter() ? CalendarEntityType.CENTERS : CalendarEntityType.GROUPS;
entityId = command.getGroupId(); entityId = command.getGroupId();
} else if (command.getLoanId() != null) { } else if (command.getLoanId() != null) {
@@ -88,7 +89,7 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
entityId = command.getLoanId(); entityId = command.getLoanId();
} else if (command.getClientId() != null) { } else if (command.getClientId() != null) {
final Client client = this.clientRepository.findOneWithNotFoundDetection(command.getClientId()); final Client client = this.clientRepository.findOneWithNotFoundDetection(command.getClientId());
entityActivationDate = client.getActivationLocalDate(); entityActivationDate = client.getActivationDate();
entityType = CalendarEntityType.CLIENTS; entityType = CalendarEntityType.CLIENTS;
entityId = command.getClientId(); entityId = command.getClientId();
} }
@@ -98,7 +99,7 @@ public class CalendarWritePlatformServiceJpaRepositoryImpl implements CalendarWr
final List<ApiParameterError> dataValidationErrors = new ArrayList<>(); final List<ApiParameterError> dataValidationErrors = new ArrayList<>();
final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("calendar"); final DataValidatorBuilder baseDataValidator = new DataValidatorBuilder(dataValidationErrors).resource("calendar");
if (entityActivationDate == null || newCalendar.getStartDateLocalDate().isBefore(entityActivationDate)) { if (entityActivationDate == null || DateUtils.isBefore(newCalendar.getStartDateLocalDate(), entityActivationDate)) {
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(command.extractLocale()); final DateTimeFormatter formatter = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(command.extractLocale());
String dateAsString = ""; String dateAsString = "";
if (entityActivationDate != null) { if (entityActivationDate != null) {
@@ -230,8 +230,7 @@ public class InterestRateChart extends AbstractPersistableCustom {
if (!existingChart.equals(this)) { if (!existingChart.equals(this)) {
if (this.chartFields.isOverlapping(existingChart.chartFields)) { if (this.chartFields.isOverlapping(existingChart.chartFields)) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates", baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates",
existingChart.getFromDateAsLocalDate(), existingChart.getEndDateAsLocalDate(), this.getFromDateAsLocalDate(), existingChart.getFromDate(), existingChart.getEndDate(), this.getFromDate(), this.getEndDate());
this.getEndDateAsLocalDate());
} }
} }
} }
@@ -318,12 +317,12 @@ public class InterestRateChart extends AbstractPersistableCustom {
return chartSlabs.remove(chartSlab); return chartSlabs.remove(chartSlab);
} }
public LocalDate getFromDateAsLocalDate() { public LocalDate getFromDate() {
return this.chartFields.getFromDateAsLocalDate(); return this.chartFields.getFromDate();
} }
public LocalDate getEndDateAsLocalDate() { public LocalDate getEndDate() {
return this.chartFields.getEndDateAsLocalDate(); return this.chartFields.getEndDate();
} }
private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) { private void throwExceptionIfValidationWarningsExist(final List<ApiParameterError> dataValidationErrors) {
@@ -88,7 +88,7 @@ public class InterestRateChartFields {
final String localeAsInput = command.locale(); final String localeAsInput = command.locale();
final String dateFormat = command.dateFormat(); final String dateFormat = command.dateFormat();
if (command.isChangeInLocalDateParameterNamed(fromDateParamName, getFromDateAsLocalDate())) { if (command.isChangeInLocalDateParameterNamed(fromDateParamName, getFromDate())) {
final String newValueAsString = command.stringValueOfParameterNamed(fromDateParamName); final String newValueAsString = command.stringValueOfParameterNamed(fromDateParamName);
actualChanges.put(fromDateParamName, newValueAsString); actualChanges.put(fromDateParamName, newValueAsString);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
@@ -96,7 +96,7 @@ public class InterestRateChartFields {
this.fromDate = command.localDateValueOfParameterNamed(fromDateParamName); this.fromDate = command.localDateValueOfParameterNamed(fromDateParamName);
} }
if (command.isChangeInLocalDateParameterNamed(endDateParamName, getEndDateAsLocalDate())) { if (command.isChangeInLocalDateParameterNamed(endDateParamName, getEndDate())) {
final String newValueAsString = command.stringValueOfParameterNamed(endDateParamName); final String newValueAsString = command.stringValueOfParameterNamed(endDateParamName);
actualChanges.put(endDateParamName, newValueAsString); actualChanges.put(endDateParamName, newValueAsString);
actualChanges.put(localeParamName, localeAsInput); actualChanges.put(localeParamName, localeAsInput);
@@ -116,31 +116,27 @@ public class InterestRateChartFields {
} }
public boolean isFromDateAfterToDate() { public boolean isFromDateAfterToDate() {
return isFromDateAfter(getEndDateAsLocalDate()); return isFromDateAfter(getEndDate());
} }
public boolean isFromDateAfter(LocalDate compare) { public boolean isFromDateAfter(LocalDate compare) {
final LocalDate fromDate = getFromDateAsLocalDate(); return compare != null && DateUtils.isAfter(getFromDate(), compare);
if (fromDate != null && compare != null) {
return fromDate.isAfter(compare);
}
return false;
} }
public LocalDate getFromDateAsLocalDate() { public LocalDate getFromDate() {
return this.fromDate; return this.fromDate;
} }
public LocalDate getEndDateAsLocalDate() { public LocalDate getEndDate() {
return this.endDate; return this.endDate;
} }
public boolean isOverlapping(InterestRateChartFields that) { public boolean isOverlapping(InterestRateChartFields that) {
final LocalDate thisFromDate = this.getFromDateAsLocalDate(); final LocalDate thisFromDate = this.getFromDate();
LocalDate thisEndDate = this.getEndDateAsLocalDate(); LocalDate thisEndDate = this.getEndDate();
thisEndDate = thisEndDate == null ? DateUtils.getBusinessLocalDate() : thisEndDate; thisEndDate = thisEndDate == null ? DateUtils.getBusinessLocalDate() : thisEndDate;
final LocalDate thatFromDate = that.getFromDateAsLocalDate(); final LocalDate thatFromDate = that.getFromDate();
LocalDate thatEndDate = that.getEndDateAsLocalDate(); LocalDate thatEndDate = that.getEndDate();
thatEndDate = thatEndDate == null ? DateUtils.getBusinessLocalDate() : thatEndDate; thatEndDate = thatEndDate == null ? DateUtils.getBusinessLocalDate() : thatEndDate;
final LocalDateInterval thisInterval = LocalDateInterval.create(thisFromDate, thisEndDate); final LocalDateInterval thisInterval = LocalDateInterval.create(thisFromDate, thisEndDate);
@@ -153,8 +149,8 @@ public class InterestRateChartFields {
} }
public boolean isApplicableChartFor(final LocalDate target) { public boolean isApplicableChartFor(final LocalDate target) {
final LocalDate endDate = this.endDate == null ? DateUtils.getBusinessLocalDate() : this.getEndDateAsLocalDate(); final LocalDate endDate = this.endDate == null ? DateUtils.getBusinessLocalDate() : this.getEndDate();
final LocalDateInterval interval = LocalDateInterval.create(getFromDateAsLocalDate(), endDate); final LocalDateInterval interval = LocalDateInterval.create(getFromDate(), endDate);
return interval.contains(target); return interval.contains(target);
} }
@@ -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.getTransactionLocalDate().isAfter(loan.getApprovedOnDate())) { if (!transaction.getTransactionDate().isAfter(loan.getApprovedOnDate())) {
transactions.add(transaction); transactions.add(transaction);
} }
} }
@@ -187,12 +187,12 @@ 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.getActivationLocalDate())) { if (loan.getSubmittedOnDate().isBefore(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.getActivationLocalDate() "Guarantor saving account activation date [" + savingsAccount.getActivationDate()
+ "] is on or before the loan submitted on date [" + loan.getSubmittedOnDate() + "]", + "] is on or before the loan submitted on date [" + loan.getSubmittedOnDate() + "]",
savingsAccount.getActivationLocalDate(), loan.getSubmittedOnDate()); savingsAccount.getActivationDate(), loan.getSubmittedOnDate());
} }
} }
@@ -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;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData; import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartData;
import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartSlabData; import org.apache.fineract.portfolio.interestratechart.data.InterestRateChartSlabData;
@@ -159,12 +160,11 @@ public final class DepositAccountInterestRateChartData {
if (this.chartSlabs == null) { if (this.chartSlabs == null) {
this.chartSlabs = new ArrayList<>(); this.chartSlabs = new ArrayList<>();
} }
this.chartSlabs.add(chartSlab); this.chartSlabs.add(chartSlab);
} }
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() {
@@ -20,10 +20,8 @@ package org.apache.fineract.portfolio.savings.data;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.useradministration.domain.AppUser;
public class SavingsAccountTransactionDTO { public class SavingsAccountTransactionDTO {
@@ -31,24 +29,9 @@ public class SavingsAccountTransactionDTO {
private final LocalDate transactionDate; private final LocalDate transactionDate;
private final BigDecimal transactionAmount; private final BigDecimal transactionAmount;
private final PaymentDetail paymentDetail; private final PaymentDetail paymentDetail;
private final LocalDateTime createdDate;
private final Long savingsAccountId; private final Long savingsAccountId;
private final AppUser appUser;
private final Integer depositAccountType; private final Integer depositAccountType;
public SavingsAccountTransactionDTO(final DateTimeFormatter formatter, final LocalDate transactionDate,
final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final LocalDateTime createdDate, final AppUser appUser,
final Integer depositAccountType) {
this.formatter = formatter;
this.transactionDate = transactionDate;
this.transactionAmount = transactionAmount;
this.paymentDetail = paymentDetail;
this.createdDate = createdDate;
this.savingsAccountId = null;
this.appUser = appUser;
this.depositAccountType = depositAccountType;
}
/** /**
* This constructor is used for bulk deposit transactions * This constructor is used for bulk deposit transactions
* *
@@ -56,19 +39,15 @@ public class SavingsAccountTransactionDTO {
* @param transactionDate * @param transactionDate
* @param transactionAmount * @param transactionAmount
* @param paymentDetail * @param paymentDetail
* @param createdDate
* @param savingsAccountId * @param savingsAccountId
*/ */
public SavingsAccountTransactionDTO(DateTimeFormatter formatter, LocalDate transactionDate, BigDecimal transactionAmount, public SavingsAccountTransactionDTO(DateTimeFormatter formatter, LocalDate transactionDate, BigDecimal transactionAmount,
PaymentDetail paymentDetail, LocalDateTime createdDate, Long savingsAccountId, AppUser appUser, PaymentDetail paymentDetail, Long savingsAccountId, final Integer depositAccountType) {
final Integer depositAccountType) {
this.formatter = formatter; this.formatter = formatter;
this.transactionDate = transactionDate; this.transactionDate = transactionDate;
this.transactionAmount = transactionAmount; this.transactionAmount = transactionAmount;
this.paymentDetail = paymentDetail; this.paymentDetail = paymentDetail;
this.createdDate = createdDate;
this.savingsAccountId = savingsAccountId; this.savingsAccountId = savingsAccountId;
this.appUser = appUser;
this.depositAccountType = depositAccountType; this.depositAccountType = depositAccountType;
} }
@@ -88,18 +67,10 @@ public class SavingsAccountTransactionDTO {
return this.paymentDetail; return this.paymentDetail;
} }
public LocalDateTime getCreatedDate() {
return this.createdDate;
}
public Long getSavingsAccountId() { public Long getSavingsAccountId() {
return this.savingsAccountId; return this.savingsAccountId;
} }
public AppUser getAppUser() {
return this.appUser;
}
public Integer getAccountType() { public Integer getAccountType() {
return this.depositAccountType; return this.depositAccountType;
} }
@@ -37,7 +37,6 @@ import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type; import java.lang.reflect.Type;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
@@ -83,7 +82,6 @@ public class SavingsAccountTransactionDataValidator {
} }
public void validateTransactionWithPivotDate(final LocalDate transactionDate, final SavingsAccount savingsAccount) { public void validateTransactionWithPivotDate(final LocalDate transactionDate, final SavingsAccount savingsAccount) {
final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig(); final boolean backdatedTxnsAllowedTill = this.configurationDomainService.retrievePivotDateConfig();
final boolean isRelaxingDaysConfigOn = this.configurationDomainService.isRelaxingDaysConfigForPivotDateEnabled(); final boolean isRelaxingDaysConfigOn = this.configurationDomainService.isRelaxingDaysConfigForPivotDateEnabled();
@@ -94,14 +92,13 @@ public class SavingsAccountTransactionDataValidator {
if (isRelaxingDaysConfigOn) { if (isRelaxingDaysConfigOn) {
pivotDate = pivotDate.minusDays(this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate()); pivotDate = pivotDate.minusDays(this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate());
} }
if (pivotDate.isAfter(transactionDate)) { if (DateUtils.isAfter(pivotDate, transactionDate)) {
throw new TransactionBeforePivotDateNotAllowed(transactionDate, pivotDate); throw new TransactionBeforePivotDateNotAllowed(transactionDate, pivotDate);
} }
} }
} }
public void validate(final JsonCommand command) { public void validate(final JsonCommand command) {
final String json = command.json(); final String json = command.json();
if (StringUtils.isBlank(json)) { if (StringUtils.isBlank(json)) {
@@ -296,7 +293,7 @@ public class SavingsAccountTransactionDataValidator {
} }
// compare two dates now // compare two dates now
if (lastTransactionDate != null && transactionDate.isBefore(lastTransactionDate)) { if (DateUtils.isBefore(transactionDate, lastTransactionDate)) {
baseDataValidator.parameter(SavingsApiConstants.dateParamName).value(lastTransactionDate).failWithCode( baseDataValidator.parameter(SavingsApiConstants.dateParamName).value(lastTransactionDate).failWithCode(
"validation.msg.date.can.not.be.before.last.transaction.date", "Amount can be put on hold only after last transaction"); "validation.msg.date.can.not.be.before.last.transaction.date", "Amount can be put on hold only after last transaction");
} }
@@ -304,8 +301,7 @@ public class SavingsAccountTransactionDataValidator {
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
} }
public SavingsAccountTransaction validateReleaseAmountAndAssembleForm(final SavingsAccountTransaction holdTransaction, public SavingsAccountTransaction validateReleaseAmountAndAssembleForm(final SavingsAccountTransaction holdTransaction) {
final AppUser createdUser) {
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(SAVINGS_ACCOUNT_RESOURCE_NAME); .resource(SAVINGS_ACCOUNT_RESOURCE_NAME);
@@ -326,10 +322,8 @@ public class SavingsAccountTransactionDataValidator {
} }
throwExceptionIfValidationWarningsExist(dataValidationErrors); throwExceptionIfValidationWarningsExist(dataValidationErrors);
LocalDateTime createdDate = DateUtils.getLocalDateTimeOfSystem();
LocalDate transactionDate = DateUtils.getBusinessLocalDate(); LocalDate transactionDate = DateUtils.getBusinessLocalDate();
SavingsAccountTransaction transaction = SavingsAccountTransaction.releaseAmount(holdTransaction, transactionDate, createdDate, SavingsAccountTransaction transaction = SavingsAccountTransaction.releaseAmount(holdTransaction, transactionDate);
createdUser);
return transaction; return transaction;
} }
@@ -69,7 +69,6 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.exception.InvalidJsonException; import org.apache.fineract.infrastructure.core.exception.InvalidJsonException;
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException; import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
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.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext; import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.staff.domain.Staff; import org.apache.fineract.organisation.staff.domain.Staff;
@@ -358,7 +357,7 @@ public class DepositAccountAssembler {
if (account != null) { if (account != null) {
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.validateNewApplicationState(DateUtils.getBusinessLocalDate(), depositAccountType.resourceName()); account.validateNewApplicationState(depositAccountType.resourceName());
} }
return account; return account;
@@ -448,7 +447,6 @@ public class DepositAccountAssembler {
public Collection<SavingsAccountTransactionDTO> assembleBulkMandatorySavingsAccountTransactionDTOs(final JsonCommand command, public Collection<SavingsAccountTransactionDTO> assembleBulkMandatorySavingsAccountTransactionDTOs(final JsonCommand command,
final PaymentDetail paymentDetail) { final PaymentDetail paymentDetail) {
AppUser user = getAppUserIfPresent();
final String json = command.json(); final String json = command.json();
if (StringUtils.isBlank(json)) { if (StringUtils.isBlank(json)) {
throw new InvalidJsonException(); throw new InvalidJsonException();
@@ -478,7 +476,7 @@ public class DepositAccountAssembler {
detail = this.paymentDetailAssembler.fetchPaymentDetail(savingsTransactionElement); detail = this.paymentDetailAssembler.fetchPaymentDetail(savingsTransactionElement);
} }
final SavingsAccountTransactionDTO savingsAccountTransactionDTO = new SavingsAccountTransactionDTO(formatter, final SavingsAccountTransactionDTO savingsAccountTransactionDTO = new SavingsAccountTransactionDTO(formatter,
transactionDate, dueAmount, detail, DateUtils.getLocalDateTimeOfSystem(), savingsId, user, depositAccountType); transactionDate, dueAmount, detail, savingsId, depositAccountType);
savingsAccountTransactions.add(savingsAccountTransactionDTO); savingsAccountTransactions.add(savingsAccountTransactionDTO);
} }
} }
@@ -486,13 +484,4 @@ public class DepositAccountAssembler {
return savingsAccountTransactions; return savingsAccountTransactions;
} }
private AppUser getAppUserIfPresent() {
AppUser user = null;
if (this.context != null) {
user = this.context.getAuthenticatedUserIfPresent();
}
return user;
}
} }
@@ -42,19 +42,18 @@ public interface DepositAccountDomainService {
BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isRegularTransaction); BigDecimal transactionAmount, PaymentDetail paymentDetail, boolean isRegularTransaction);
Long handleFDAccountClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, Long handleFDAccountClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command,
LocalDate tenantsTodayDate, Map<String, Object> changes);
@Transactional
Long handleFDAccountMaturityClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, LocalDate tenantsTodayDate,
DateTimeFormatter fmt, LocalDate closedDate, Integer onAccountClosureId, Long toSavingsId, String transferDescription,
Map<String, Object> changes); Map<String, Object> changes);
@Transactional
Long handleFDAccountMaturityClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, DateTimeFormatter fmt,
LocalDate closedDate, Integer onAccountClosureId, Long toSavingsId, String transferDescription, Map<String, Object> changes);
Long handleRDAccountClosure(RecurringDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, Long handleRDAccountClosure(RecurringDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command,
LocalDate tenantsTodayDate, Map<String, Object> changes); Map<String, Object> changes);
Long handleFDAccountPreMatureClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, Long handleFDAccountPreMatureClosure(FixedDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command,
LocalDate tenantsTodayDate, Map<String, Object> changes); Map<String, Object> changes);
Long handleRDAccountPreMatureClosure(RecurringDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command, Long handleRDAccountPreMatureClosure(RecurringDepositAccount account, PaymentDetail paymentDetail, AppUser user, JsonCommand command,
LocalDate tenantsTodayDate, Map<String, Object> changes); Map<String, Object> changes);
} }
@@ -40,8 +40,6 @@ import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.domain.ExternalId; import org.apache.fineract.infrastructure.core.domain.ExternalId;
import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException; import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRuleException;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.organisation.monetary.domain.ApplicationCurrencyRepositoryWrapper;
import org.apache.fineract.portfolio.account.PortfolioAccountType; import org.apache.fineract.portfolio.account.PortfolioAccountType;
import org.apache.fineract.portfolio.account.data.AccountTransferDTO; import org.apache.fineract.portfolio.account.data.AccountTransferDTO;
import org.apache.fineract.portfolio.account.domain.AccountTransferType; import org.apache.fineract.portfolio.account.domain.AccountTransferType;
@@ -69,9 +67,7 @@ import org.springframework.transaction.annotation.Transactional;
@Service @Service
public class DepositAccountDomainServiceJpa implements DepositAccountDomainService { public class DepositAccountDomainServiceJpa implements DepositAccountDomainService {
private final PlatformSecurityContext context;
private final SavingsAccountRepositoryWrapper savingsAccountRepository; private final SavingsAccountRepositoryWrapper savingsAccountRepository;
private final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper;
private final JournalEntryWritePlatformService journalEntryWritePlatformService; private final JournalEntryWritePlatformService journalEntryWritePlatformService;
private final AccountNumberGenerator accountNumberGenerator; private final AccountNumberGenerator accountNumberGenerator;
private final DepositAccountAssembler depositAccountAssembler; private final DepositAccountAssembler depositAccountAssembler;
@@ -82,18 +78,14 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
private final CalendarInstanceRepository calendarInstanceRepository; private final CalendarInstanceRepository calendarInstanceRepository;
@Autowired @Autowired
public DepositAccountDomainServiceJpa(final PlatformSecurityContext context, public DepositAccountDomainServiceJpa(final SavingsAccountRepositoryWrapper savingsAccountRepository,
final SavingsAccountRepositoryWrapper savingsAccountRepository,
final ApplicationCurrencyRepositoryWrapper applicationCurrencyRepositoryWrapper,
final JournalEntryWritePlatformService journalEntryWritePlatformService, final AccountNumberGenerator accountNumberGenerator, final JournalEntryWritePlatformService journalEntryWritePlatformService, final AccountNumberGenerator accountNumberGenerator,
final DepositAccountAssembler depositAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService, final DepositAccountAssembler depositAccountAssembler, final SavingsAccountDomainService savingsAccountDomainService,
final AccountTransfersWritePlatformService accountTransfersWritePlatformService, final AccountTransfersWritePlatformService accountTransfersWritePlatformService,
final ConfigurationDomainService configurationDomainService, final ConfigurationDomainService configurationDomainService,
final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository, final AccountNumberFormatRepositoryWrapper accountNumberFormatRepository,
final CalendarInstanceRepository calendarInstanceRepository) { final CalendarInstanceRepository calendarInstanceRepository) {
this.context = context;
this.savingsAccountRepository = savingsAccountRepository; this.savingsAccountRepository = savingsAccountRepository;
this.applicationCurrencyRepositoryWrapper = applicationCurrencyRepositoryWrapper;
this.journalEntryWritePlatformService = journalEntryWritePlatformService; this.journalEntryWritePlatformService = journalEntryWritePlatformService;
this.accountNumberGenerator = accountNumberGenerator; this.accountNumberGenerator = accountNumberGenerator;
this.depositAccountAssembler = depositAccountAssembler; this.depositAccountAssembler = depositAccountAssembler;
@@ -135,7 +127,6 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
public SavingsAccountTransaction handleRDDeposit(final RecurringDepositAccount account, final DateTimeFormatter fmt, public SavingsAccountTransaction handleRDDeposit(final RecurringDepositAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final boolean isRegularTransaction) { final boolean isRegularTransaction) {
AppUser user = getAppUserIfPresent();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
@@ -151,7 +142,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final boolean isAnyActivationChargesDue = isAnyActivationChargesDue(account); final boolean isAnyActivationChargesDue = isAnyActivationChargesDue(account);
if (isAnyActivationChargesDue) { if (isAnyActivationChargesDue) {
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, user); account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
this.savingsAccountRepository.saveAndFlush(account); this.savingsAccountRepository.saveAndFlush(account);
} }
account.handleScheduleInstallments(deposit); account.handleScheduleInstallments(deposit);
@@ -193,8 +184,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Transactional @Transactional
@Override @Override
public Long handleFDAccountClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, public Long handleFDAccountClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) { final JsonCommand command, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
@@ -204,9 +194,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final boolean isPreMatureClosure = false; final boolean isPreMatureClosure = false;
final Set<Long> existingTransactionIds = new HashSet<>(); final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
/*** // Update account transactionIds for post journal entries.
* Update account transactionIds for post journal entries.
*/
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
/* /*
* final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, * final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate,
@@ -254,7 +242,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
savingsTransactionId = withdrawal.getId(); savingsTransactionId = withdrawal.getId();
} }
account.close(user, command, tenantsTodayDate, changes); account.close(user, command, changes);
this.savingsAccountRepository.save(account); this.savingsAccountRepository.save(account);
return savingsTransactionId; return savingsTransactionId;
@@ -263,8 +251,8 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Transactional @Transactional
@Override @Override
public Long handleFDAccountMaturityClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, public Long handleFDAccountMaturityClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final LocalDate tenantsTodayDate, final DateTimeFormatter fmt, final LocalDate closedDate, final Integer onAccountClosureId, final DateTimeFormatter fmt, final LocalDate closedDate, final Integer onAccountClosureId, final Long toSavingsId,
final Long toSavingsId, final String transferDescription, Map<String, Object> changes) { final String transferDescription, Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -329,7 +317,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
} }
// if(!processMaturityInstructionOnly) // if(!processMaturityInstructionOnly)
// account.close(user, command, tenantsTodayDate, changes); // account.close(user, command, changes);
this.savingsAccountRepository.save(account); this.savingsAccountRepository.save(account);
@@ -341,7 +329,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Transactional @Transactional
@Override @Override
public Long handleRDAccountClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, public Long handleRDAccountClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) { final JsonCommand command, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -352,9 +340,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final boolean isPreMatureClosure = false; final boolean isPreMatureClosure = false;
final Set<Long> existingTransactionIds = new HashSet<>(); final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
/*** // Update account transactionIds for post journal entries.
* Update account transactionIds for post journal entries.
*/
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
@@ -383,7 +369,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence()); Integer frequency = CalendarUtils.getInterval(calendar.getRecurrence());
frequency = frequency == -1 ? 1 : frequency; frequency = frequency == -1 ? 1 : frequency;
reinvestedDeposit.generateSchedule(frequencyType, frequency, calendar); reinvestedDeposit.generateSchedule(frequencyType, frequency, calendar);
reinvestedDeposit.processAccountUponActivation(fmt, user, postReversals); reinvestedDeposit.processAccountUponActivation(fmt, postReversals);
reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, reinvestedDeposit.updateMaturityDateAndAmount(mc, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth); financialYearBeginningMonth);
this.savingsAccountRepository.save(reinvestedDeposit); this.savingsAccountRepository.save(reinvestedDeposit);
@@ -411,7 +397,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
savingsTransactionId = withdrawal.getId(); savingsTransactionId = withdrawal.getId();
} }
account.close(user, command, tenantsTodayDate, changes); account.close(user, command, changes);
this.savingsAccountRepository.save(account); this.savingsAccountRepository.save(account);
@@ -456,7 +442,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Transactional @Transactional
@Override @Override
public Long handleFDAccountPreMatureClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user, public Long handleFDAccountPreMatureClosure(final FixedDepositAccount account, final PaymentDetail paymentDetail, final AppUser user,
final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) { final JsonCommand command, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -467,9 +453,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
final boolean isPreMatureClosure = true; final boolean isPreMatureClosure = true;
final Set<Long> existingTransactionIds = new HashSet<>(); final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
/*** // Update account transactionIds for post journal entries.
* Update account transactionIds for post journal entries.
*/
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
@@ -502,7 +486,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
savingsTransactionId = withdrawal.getId(); savingsTransactionId = withdrawal.getId();
} }
account.prematureClosure(user, command, tenantsTodayDate, changes); account.prematureClosure(user, command, changes);
this.savingsAccountRepository.save(account); this.savingsAccountRepository.save(account);
@@ -513,7 +497,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
@Transactional @Transactional
@Override @Override
public Long handleRDAccountPreMatureClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail, public Long handleRDAccountPreMatureClosure(final RecurringDepositAccount account, final PaymentDetail paymentDetail,
final AppUser user, final JsonCommand command, final LocalDate tenantsTodayDate, final Map<String, Object> changes) { final AppUser user, final JsonCommand command, final Map<String, Object> changes) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -558,7 +542,7 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
savingsTransactionId = withdrawal.getId(); savingsTransactionId = withdrawal.getId();
} }
account.prematureClosure(user, command, tenantsTodayDate, changes); account.prematureClosure(user, command, changes);
this.savingsAccountRepository.save(account); this.savingsAccountRepository.save(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, isAccountTransfer);
return savingsTransactionId; return savingsTransactionId;
@@ -592,12 +576,4 @@ public class DepositAccountDomainServiceJpa implements DepositAccountDomainServi
} }
} }
} }
private AppUser getAppUserIfPresent() {
AppUser user = null;
if (this.context != null) {
user = this.context.getAuthenticatedUserIfPresent();
}
return user;
}
} }
@@ -103,12 +103,12 @@ public class DepositAccountInterestRateChart extends AbstractPersistableCustom {
return null; return null;
} }
public LocalDate getFromDateAsLocalDate() { public LocalDate getFromDate() {
return this.chartFields.getFromDateAsLocalDate(); return this.chartFields.getFromDate();
} }
public LocalDate getEndDateAsLocalDate() { public LocalDate getEndDate() {
return this.chartFields.getEndDateAsLocalDate(); return this.chartFields.getEndDate();
} }
public Long savingsAccountId() { public Long savingsAccountId() {
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.portfolio.savings.domain; package org.apache.fineract.portfolio.savings.domain;
import static org.apache.fineract.infrastructure.core.service.DateUtils.getSystemZoneId;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@@ -28,8 +30,9 @@ import jakarta.persistence.Table;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import java.time.OffsetDateTime;
import org.apache.fineract.infrastructure.core.service.DateUtils; import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
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.guarantor.domain.GuarantorFundingTransaction; import org.apache.fineract.portfolio.loanaccount.guarantor.domain.GuarantorFundingTransaction;
@@ -37,7 +40,7 @@ import org.apache.fineract.portfolio.savings.DepositAccountOnHoldTransactionType
@Entity @Entity
@Table(name = "m_deposit_account_on_hold_transaction") @Table(name = "m_deposit_account_on_hold_transaction")
public class DepositAccountOnHoldTransaction extends AbstractPersistableCustom { public class DepositAccountOnHoldTransaction extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne @ManyToOne
@JoinColumn(name = "savings_account_id", nullable = true) @JoinColumn(name = "savings_account_id", nullable = true)
@@ -55,8 +58,9 @@ public class DepositAccountOnHoldTransaction extends AbstractPersistableCustom {
@Column(name = "is_reversed", nullable = false) @Column(name = "is_reversed", nullable = false)
private boolean reversed; private boolean reversed;
@Column(name = "created_date", nullable = false) @Deprecated
private LocalDateTime createdDate; @Column(name = "created_date", nullable = true)
private LocalDateTime createdDateToRemove;
@OneToOne(cascade = CascadeType.ALL, mappedBy = "depositAccountOnHoldTransaction", optional = true, orphanRemoval = true) @OneToOne(cascade = CascadeType.ALL, mappedBy = "depositAccountOnHoldTransaction", optional = true, orphanRemoval = true)
private GuarantorFundingTransaction guarantorFundingTransaction; private GuarantorFundingTransaction guarantorFundingTransaction;
@@ -69,7 +73,7 @@ public class DepositAccountOnHoldTransaction extends AbstractPersistableCustom {
this.amount = amount; this.amount = amount;
this.transactionType = transactionType.getValue(); this.transactionType = transactionType.getValue();
this.transactionDate = transactionDate; this.transactionDate = transactionDate;
this.createdDate = DateUtils.getLocalDateTimeOfSystem(); this.createdDateToRemove = null; // #audit backward compatibility deprecated
this.reversed = reversed; this.reversed = reversed;
} }
@@ -87,14 +91,30 @@ public class DepositAccountOnHoldTransaction extends AbstractPersistableCustom {
reversed); reversed);
} }
public SavingsAccount getSavingsAccount() {
return savingsAccount;
}
public BigDecimal getAmount() { public BigDecimal getAmount() {
return this.amount; return this.amount;
} }
public Money getAmountMoney(final MonetaryCurrency currency) { public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount); return Money.of(currency, this.amount);
} }
public DepositAccountOnHoldTransactionType getTransactionType() {
return DepositAccountOnHoldTransactionType.fromInt(this.transactionType);
}
public LocalDate getTransactionDate() {
return this.transactionDate;
}
public MonetaryCurrency getCurrency() {
return getSavingsAccount().getCurrency();
}
public void reverseTransaction() { public void reverseTransaction() {
this.reversed = true; this.reversed = true;
if (this.getTransactionType().isHold()) { if (this.getTransactionType().isHold()) {
@@ -104,12 +124,10 @@ public class DepositAccountOnHoldTransaction extends AbstractPersistableCustom {
} }
} }
public DepositAccountOnHoldTransactionType getTransactionType() { @Override
return DepositAccountOnHoldTransactionType.fromInt(this.transactionType); public Optional<OffsetDateTime> getCreatedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getCreatedDate()
.orElse(createdDateToRemove == null ? null : createdDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
} }
public LocalDate getTransactionDate() {
return this.transactionDate;
}
} }
@@ -224,7 +224,7 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
this.depositAmount = depositAmount; this.depositAmount = depositAmount;
} }
public LocalDate getMaturityLocalDate() { public LocalDate getMaturityDate() {
return this.maturityDate; return this.maturityDate;
} }
@@ -247,7 +247,7 @@ public class DepositAccountTermAndPreClosure extends AbstractPersistableCustom {
} }
Integer actualDepositPeriod = this.depositPeriod; Integer actualDepositPeriod = this.depositPeriod;
if (depositFromDate == null || getMaturityLocalDate() == null || interestPostingUpToDate.isEqual(getMaturityLocalDate())) { if (depositFromDate == null || getMaturityDate() == null || DateUtils.isEqual(interestPostingUpToDate, getMaturityDate())) {
return actualDepositPeriod; return actualDepositPeriod;
} }
@@ -197,18 +197,8 @@ public class FixedDepositAccount extends SavingsAccount {
String refNo = null; String refNo = null;
final Money transactionAmountMoney = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount()); final Money transactionAmountMoney = Money.of(getCurrency(), this.accountTermAndPreClosure.depositAmount());
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null, final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null,
this.accountSubmittedOrActivationDate(), transactionAmountMoney, DateUtils.getLocalDateTimeOfSystem(), null, refNo); // TODO: this.accountSubmittedOrActivationDate(), transactionAmountMoney, refNo);
// verify transaction.setRunningBalance(transactionAmountMoney);
// if
// it
// is
// ok
// to
// pass
// null
// for
// AppUser
transaction.updateRunningBalance(transactionAmountMoney);
transaction.updateCumulativeBalanceAndDates(this.getCurrency(), interestCalculatedUpto()); transaction.updateCumulativeBalanceAndDates(this.getCurrency(), interestCalculatedUpto());
allTransactions.add(transaction); allTransactions.add(transaction);
updateMaturityDateAndAmount(mc, allTransactions, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd, updateMaturityDateAndAmount(mc, allTransactions, isPreMatureClosure, isSavingsInterestPostingAtCurrentPeriodEnd,
@@ -260,8 +250,7 @@ public class FixedDepositAccount extends SavingsAccount {
} }
} }
final LocalDate todayDate = DateUtils.getBusinessLocalDate(); if (!DateUtils.isDateInTheFuture(maturityDate())) {
if (!this.maturityDate().isAfter(todayDate)) {
// update account status // update account status
this.status = SavingsAccountStatusType.MATURED.getValue(); this.status = SavingsAccountStatusType.MATURED.getValue();
postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth); postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
@@ -338,14 +327,12 @@ public class FixedDepositAccount extends SavingsAccount {
} }
this.summary.updateFromInterestPeriodSummaries(this.currency, allPostingPeriods); this.summary.updateFromInterestPeriodSummaries(this.currency, allPostingPeriods);
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods, this.getLockedInUntilLocalDate(), this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods, getLockedInUntilDate(),
isTransferInterestToOtherAccount()); isTransferInterestToOtherAccount());
return allPostingPeriods; return allPostingPeriods;
} }
public void prematureClosure(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate, public void prematureClosure(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges) {
final Map<String, Object> actualChanges) {
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(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.preMatureCloseAction); .resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.preMatureCloseAction);
@@ -362,14 +349,13 @@ public class FixedDepositAccount extends SavingsAccount {
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
if (closedDate.isBefore(getActivationLocalDate())) { if (DateUtils.isBefore(closedDate, getActivationDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.activation.date"); .failWithCode("must.be.after.activation.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (isAccountLocked(closedDate)) { if (isAccountLocked(closedDate)) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.lockin.period"); .failWithCode("must.be.after.lockin.period");
@@ -377,16 +363,14 @@ public class FixedDepositAccount extends SavingsAccount {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (maturityDate() != null && DateUtils.isAfter(closedDate, maturityDate())) {
if (closedDate.isAfter(maturityDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.before.maturity.date"); .failWithCode("must.be.before.maturity.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (DateUtils.isAfterBusinessDate(closedDate)) {
if (closedDate.isAfter(tenantsTodayDate)) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("cannot.be.a.future.date"); .failWithCode("cannot.be.a.future.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
@@ -437,9 +421,7 @@ public class FixedDepositAccount extends SavingsAccount {
return Money.of(this.currency, this.accountTermAndPreClosure.depositAmount()); return Money.of(this.currency, this.accountTermAndPreClosure.depositAmount());
} }
public void close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate, public void close(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges) {
final Map<String, Object> actualChanges) {
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(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction); .resource(FIXED_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction);
@@ -456,21 +438,21 @@ public class FixedDepositAccount extends SavingsAccount {
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
if (closedDate.isBefore(getActivationLocalDate())) { if (DateUtils.isBefore(closedDate, getActivationDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.activation.date"); .failWithCode("must.be.after.activation.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (closedDate.isBefore(maturityDate())) { if (DateUtils.isBefore(closedDate, maturityDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.account.maturity.date"); .failWithCode("must.be.after.account.maturity.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (closedDate.isAfter(tenantsTodayDate)) { if (DateUtils.isAfterBusinessDate(closedDate)) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("cannot.be.a.future.date"); .failWithCode("cannot.be.a.future.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
@@ -548,7 +530,8 @@ public class FixedDepositAccount extends SavingsAccount {
LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction(); LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction();
interestPostingTransactionDate = interestPostingTransactionDate.isAfter(interestPostingUpToDate) ? interestPostingUpToDate interestPostingTransactionDate = DateUtils.isAfter(interestPostingTransactionDate, interestPostingUpToDate)
? interestPostingUpToDate
: interestPostingTransactionDate; : interestPostingTransactionDate;
final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();
@@ -669,14 +652,14 @@ public class FixedDepositAccount extends SavingsAccount {
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) { private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
LocalDate interestPostingUpToDate = interestPostingDate; LocalDate interestPostingUpToDate = interestPostingDate;
final LocalDate uptoMaturityDate = interestCalculatedUpto(); final LocalDate uptoMaturityDate = interestCalculatedUpto();
if (uptoMaturityDate != null && uptoMaturityDate.isBefore(interestPostingDate)) { if (uptoMaturityDate != null && DateUtils.isBefore(uptoMaturityDate, interestPostingDate)) {
interestPostingUpToDate = uptoMaturityDate; interestPostingUpToDate = uptoMaturityDate;
} }
return interestPostingUpToDate; return interestPostingUpToDate;
} }
public LocalDate maturityDate() { public LocalDate maturityDate() {
return this.accountTermAndPreClosure.getMaturityLocalDate(); return this.accountTermAndPreClosure.getMaturityDate();
} }
public BigDecimal maturityAmount() { public BigDecimal maturityAmount() {
@@ -695,8 +678,8 @@ public class FixedDepositAccount extends SavingsAccount {
} }
@Override @Override
public Map<String, Object> activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { public Map<String, Object> activate(final AppUser currentUser, final JsonCommand command) {
final Map<String, Object> actualChanges = super.activate(currentUser, command, tenantsTodayDate); final Map<String, Object> actualChanges = super.activate(currentUser, command);
// if (isAccountLocked(calculateMaturityDate())) { // if (isAccountLocked(calculateMaturityDate())) {
// final List<ApiParameterError> dataValidationErrors = new // final List<ApiParameterError> dataValidationErrors = new
@@ -770,8 +753,8 @@ public class FixedDepositAccount extends SavingsAccount {
} }
if (this.chart != null) { if (this.chart != null) {
final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate(); final LocalDate chartFromDate = this.chart.getFromDate();
LocalDate chartEndDate = this.chart.getEndDateAsLocalDate(); LocalDate chartEndDate = this.chart.getEndDate();
chartEndDate = chartEndDate == null ? DateUtils.getBusinessLocalDate() : chartEndDate; chartEndDate = chartEndDate == null ? DateUtils.getBusinessLocalDate() : chartEndDate;
final LocalDateInterval chartInterval = LocalDateInterval.create(chartFromDate, chartEndDate); final LocalDateInterval chartInterval = LocalDateInterval.create(chartFromDate, chartEndDate);
@@ -783,7 +766,7 @@ public class FixedDepositAccount extends SavingsAccount {
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(), BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(depositAmount, depositStartDate(),
calculateMaturityDate(), this.client); calculateMaturityDate(), this.client);
if (applicableInterestRate.compareTo(BigDecimal.ZERO) == 0 ? Boolean.TRUE : Boolean.FALSE) { if (applicableInterestRate.compareTo(BigDecimal.ZERO) == 0) {
baseDataValidator.reset() baseDataValidator.reset()
.failWithCodeNoParameterAddedToErrorCode("no.applicable.interest.rate.is.found.based.on.amount.and.deposit.period"); .failWithCodeNoParameterAddedToErrorCode("no.applicable.interest.rate.is.found.based.on.amount.and.deposit.period");
} }
@@ -258,8 +258,8 @@ public class FixedDepositProduct extends SavingsProduct {
if (!existingChart.equals(comparingChart)) { if (!existingChart.equals(comparingChart)) {
if (existingChart.chartFields().isOverlapping(comparingChart.chartFields())) { if (existingChart.chartFields().isOverlapping(comparingChart.chartFields())) {
baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates", baseDataValidator.failWithCodeNoParameterAddedToErrorCode("chart.overlapping.from.and.end.dates",
existingChart.getFromDateAsLocalDate(), existingChart.getEndDateAsLocalDate(), existingChart.getFromDate(), existingChart.getEndDate(), comparingChart.getFromDate(),
comparingChart.getFromDateAsLocalDate(), comparingChart.getEndDateAsLocalDate()); comparingChart.getEndDate());
} }
} }
} }
@@ -286,7 +286,7 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
final LocalDate todayDate = DateUtils.getBusinessLocalDate(); final LocalDate todayDate = DateUtils.getBusinessLocalDate();
if (!this.maturityDate().isAfter(todayDate)) { if (!DateUtils.isAfter(this.maturityDate(), todayDate)) {
// update account status // update account status
this.status = SavingsAccountStatusType.MATURED.getValue(); this.status = SavingsAccountStatusType.MATURED.getValue();
postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, todayDate, postReversals); postMaturityInterest(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, todayDate, postReversals);
@@ -368,7 +368,7 @@ public class RecurringDepositAccount extends SavingsAccount {
allPostingPeriods.add(postingPeriod); allPostingPeriods.add(postingPeriod);
} }
this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods, this.getLockedInUntilLocalDate(), this.savingsHelper.calculateInterestForAllPostingPeriods(this.currency, allPostingPeriods, this.getLockedInUntilDate(),
isTransferInterestToOtherAccount()); isTransferInterestToOtherAccount());
// this.summary.updateFromInterestPeriodSummaries(this.currency, // this.summary.updateFromInterestPeriodSummaries(this.currency,
// allPostingPeriods); // allPostingPeriods);
@@ -381,8 +381,8 @@ public class RecurringDepositAccount extends SavingsAccount {
allTransactions.addAll(retreiveOrderedNonInterestPostingTransactions()); allTransactions.addAll(retreiveOrderedNonInterestPostingTransactions());
LocalDate latestTransactionDate = null; LocalDate latestTransactionDate = null;
for (final SavingsAccountTransaction installment : allTransactions) { for (final SavingsAccountTransaction installment : allTransactions) {
if (latestTransactionDate == null || latestTransactionDate.isBefore(installment.getTransactionLocalDate())) { if (latestTransactionDate == null || DateUtils.isBefore(latestTransactionDate, installment.getTransactionDate())) {
latestTransactionDate = installment.getTransactionLocalDate(); latestTransactionDate = installment.getTransactionDate();
} }
} }
String refNo = null; String refNo = null;
@@ -390,11 +390,11 @@ public class RecurringDepositAccount extends SavingsAccount {
for (RecurringDepositScheduleInstallment installment : depositScheduleInstallments()) { for (RecurringDepositScheduleInstallment installment : depositScheduleInstallments()) {
if (installment.isPrincipalNotCompleted(getCurrency())) { if (installment.isPrincipalNotCompleted(getCurrency())) {
LocalDate dueDate = installment.dueDate(); LocalDate dueDate = installment.dueDate();
if (latestTransactionDate != null && dueDate.isBefore(latestTransactionDate)) { if (DateUtils.isBefore(dueDate, latestTransactionDate)) {
dueDate = latestTransactionDate; dueDate = latestTransactionDate;
} }
final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null, dueDate, final SavingsAccountTransaction transaction = SavingsAccountTransaction.deposit(null, office(), null, dueDate,
installment.getDepositAmountOutstanding(getCurrency()), DateUtils.getLocalDateTimeOfSystem(), null, refNo); installment.getDepositAmountOutstanding(getCurrency()), refNo);
allTransactions.add(transaction); allTransactions.add(transaction);
} }
} }
@@ -414,7 +414,7 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
runningBalance = runningBalance.plus(transactionAmount); runningBalance = runningBalance.plus(transactionAmount);
transaction.updateRunningBalance(runningBalance); transaction.setRunningBalance(runningBalance);
} }
} }
// loop over transactions in reverse // loop over transactions in reverse
@@ -423,7 +423,7 @@ public class RecurringDepositAccount extends SavingsAccount {
final SavingsAccountTransaction transaction = allTransactions.get(i); final SavingsAccountTransaction transaction = allTransactions.get(i);
if (transaction.isNotReversed() && !transaction.isInterestPostingAndNotReversed()) { if (transaction.isNotReversed() && !transaction.isInterestPostingAndNotReversed()) {
transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate); transaction.updateCumulativeBalanceAndDates(this.currency, endOfBalanceDate);
endOfBalanceDate = transaction.transactionLocalDate().minusDays(1); endOfBalanceDate = transaction.getTransactionDate().minusDays(1);
} }
} }
return allTransactions; return allTransactions;
@@ -437,9 +437,7 @@ public class RecurringDepositAccount extends SavingsAccount {
return depositStartDate; return depositStartDate;
} }
public void prematureClosure(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate, public void prematureClosure(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges) {
final Map<String, Object> actualChanges) {
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(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.preMatureCloseAction); .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + DepositsApiConstants.preMatureCloseAction);
@@ -456,14 +454,13 @@ public class RecurringDepositAccount extends SavingsAccount {
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
if (closedDate.isBefore(getActivationLocalDate())) { if (DateUtils.isBefore(closedDate, getActivationDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.activation.date"); .failWithCode("must.be.after.activation.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (isAccountLocked(closedDate)) { if (isAccountLocked(closedDate)) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.lockin.period"); .failWithCode("must.be.after.lockin.period");
@@ -471,16 +468,14 @@ public class RecurringDepositAccount extends SavingsAccount {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (maturityDate() != null && DateUtils.isAfter(closedDate, maturityDate())) {
if (closedDate.isAfter(maturityDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.before.maturity.date"); .failWithCode("must.be.before.maturity.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (DateUtils.isAfterBusinessDate(closedDate)) {
if (closedDate.isAfter(tenantsTodayDate)) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("cannot.be.a.future.date"); .failWithCode("cannot.be.a.future.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
@@ -541,14 +536,14 @@ public class RecurringDepositAccount extends SavingsAccount {
return Money.of(this.currency, this.minRequiredOpeningBalance); return Money.of(this.currency, this.minRequiredOpeningBalance);
} }
protected void processAccountUponActivation(final DateTimeFormatter fmt, final AppUser user, final boolean postReversals) { protected void processAccountUponActivation(final DateTimeFormatter fmt, final boolean postReversals) {
final Money minRequiredOpeningBalance = Money.of(this.currency, this.minRequiredOpeningBalance); final Money minRequiredOpeningBalance = Money.of(this.currency, this.minRequiredOpeningBalance);
final boolean backdatedTxnsAllowedTill = false; final boolean backdatedTxnsAllowedTill = false;
String refNo = null; String refNo = null;
final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate(); final Long relaxingDaysConfigForPivotDate = this.configurationDomainService.retrieveRelaxingDaysConfigForPivotDate();
if (minRequiredOpeningBalance.isGreaterThanZero()) { if (minRequiredOpeningBalance.isGreaterThanZero()) {
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, getActivationLocalDate(), final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, getActivationDate(),
minRequiredOpeningBalance.getAmount(), null, DateUtils.getLocalDateTimeOfSystem(), user, accountType); minRequiredOpeningBalance.getAmount(), null, null, accountType);
deposit(transactionDTO, backdatedTxnsAllowedTill, relaxingDaysConfigForPivotDate, refNo); deposit(transactionDTO, backdatedTxnsAllowedTill, relaxingDaysConfigForPivotDate, refNo);
// update existing transactions so derived balance fields are // update existing transactions so derived balance fields are
@@ -557,9 +552,7 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
} }
public void close(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate, public void close(final AppUser currentUser, final JsonCommand command, final Map<String, Object> actualChanges) {
final Map<String, Object> actualChanges) {
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(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction); .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME + SavingsApiConstants.closeAction);
@@ -576,21 +569,21 @@ public class RecurringDepositAccount extends SavingsAccount {
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern(command.dateFormat()).withLocale(locale);
final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName); final LocalDate closedDate = command.localDateValueOfParameterNamed(SavingsApiConstants.closedOnDateParamName);
if (closedDate.isBefore(getActivationLocalDate())) { if (DateUtils.isBefore(closedDate, getActivationDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.activation.date"); .failWithCode("must.be.after.activation.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (maturityDate() != null && closedDate.isBefore(maturityDate())) { if (maturityDate() != null && DateUtils.isBefore(closedDate, maturityDate())) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("must.be.after.account.maturity.date"); .failWithCode("must.be.after.account.maturity.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
} }
if (closedDate.isAfter(tenantsTodayDate)) { if (DateUtils.isAfterBusinessDate(closedDate)) {
baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate) baseDataValidator.reset().parameter(SavingsApiConstants.closedOnDateParamName).value(closedDate)
.failWithCode("cannot.be.a.future.date"); .failWithCode("cannot.be.a.future.date");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
@@ -659,7 +652,8 @@ public class RecurringDepositAccount extends SavingsAccount {
for (final PostingPeriod interestPostingPeriod : postingPeriods) { for (final PostingPeriod interestPostingPeriod : postingPeriods) {
LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction(); LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction();
interestPostingTransactionDate = interestPostingTransactionDate.isAfter(interestPostingUpToDate) ? interestPostingUpToDate interestPostingTransactionDate = DateUtils.isAfter(interestPostingTransactionDate, interestPostingUpToDate)
? interestPostingUpToDate
: interestPostingTransactionDate; : interestPostingTransactionDate;
final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();
@@ -777,14 +771,14 @@ public class RecurringDepositAccount extends SavingsAccount {
private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) { private LocalDate interestPostingUpToDate(final LocalDate interestPostingDate) {
LocalDate interestPostingUpToDate = interestPostingDate; LocalDate interestPostingUpToDate = interestPostingDate;
final LocalDate uptoMaturityDate = interestCalculatedUpto(); final LocalDate uptoMaturityDate = interestCalculatedUpto();
if (uptoMaturityDate != null && uptoMaturityDate.isBefore(interestPostingDate)) { if (uptoMaturityDate != null && DateUtils.isBefore(uptoMaturityDate, interestPostingDate)) {
interestPostingUpToDate = uptoMaturityDate; interestPostingUpToDate = uptoMaturityDate;
} }
return interestPostingUpToDate; return interestPostingUpToDate;
} }
public LocalDate maturityDate() { public LocalDate maturityDate() {
return this.accountTermAndPreClosure.getMaturityLocalDate(); return this.accountTermAndPreClosure.getMaturityDate();
} }
public BigDecimal maturityAmount() { public BigDecimal maturityAmount() {
@@ -813,11 +807,10 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
@Override @Override
public Map<String, Object> activate(final AppUser currentUser, final JsonCommand command, final LocalDate tenantsTodayDate) { public Map<String, Object> activate(final AppUser currentUser, final JsonCommand command) {
final Map<String, Object> actualChanges = super.activate(currentUser, command);
final Map<String, Object> actualChanges = super.activate(currentUser, command, tenantsTodayDate); if (accountTermAndPreClosure.isAfterExpectedFirstDepositDate(getActivationDate())) {
if (accountTermAndPreClosure.isAfterExpectedFirstDepositDate(getActivationLocalDate())) {
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(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
@@ -889,13 +882,12 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
public void handleScheduleInstallments(final SavingsAccountTransaction transaction) { public void handleScheduleInstallments(final SavingsAccountTransaction transaction) {
final LocalDate transactionDate = transaction.getTransactionDate();
final LocalDate transactionDate = transaction.transactionLocalDate();
Money transactionAmountUnprocessed = transaction.getAmount(getCurrency()); Money transactionAmountUnprocessed = transaction.getAmount(getCurrency());
for (RecurringDepositScheduleInstallment currentInstallment : depositScheduleInstallments()) { for (RecurringDepositScheduleInstallment currentInstallment : depositScheduleInstallments()) {
if (currentInstallment.isNotFullyPaidOff() && transactionAmountUnprocessed.isGreaterThanZero()) { if (currentInstallment.isNotFullyPaidOff() && transactionAmountUnprocessed.isGreaterThanZero()) {
if (!this.adjustAdvanceTowardsFuturePayments() && currentInstallment.dueDate().isAfter(transactionDate)) { if (!this.adjustAdvanceTowardsFuturePayments() && DateUtils.isBefore(transactionDate, currentInstallment.dueDate())) {
transactionAmountUnprocessed = Money.zero(getCurrency()); transactionAmountUnprocessed = Money.zero(getCurrency());
} }
transactionAmountUnprocessed = handleInstallmentTransaction(currentInstallment, transactionAmountUnprocessed, transactionAmountUnprocessed = handleInstallmentTransaction(currentInstallment, transactionAmountUnprocessed,
@@ -906,7 +898,6 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
public void updateScheduleInstallments() { public void updateScheduleInstallments() {
// reset all installments to process from the beginning // reset all installments to process from the beginning
for (RecurringDepositScheduleInstallment currentInstallment : depositScheduleInstallments()) { for (RecurringDepositScheduleInstallment currentInstallment : depositScheduleInstallments()) {
currentInstallment.resetDerivedFields(); currentInstallment.resetDerivedFields();
@@ -923,8 +914,7 @@ public class RecurringDepositAccount extends SavingsAccount {
// reset all installments to process from the beginning, also update // reset all installments to process from the beginning, also update
// deposit amount as necessary // deposit amount as necessary
for (RecurringDepositScheduleInstallment currentInstallment : depositScheduleInstallments()) { for (RecurringDepositScheduleInstallment currentInstallment : depositScheduleInstallments()) {
if (currentInstallment.dueDate().isAfter(depositAmountupdatedFromDate) if (!DateUtils.isAfter(depositAmountupdatedFromDate, currentInstallment.dueDate())) {
|| currentInstallment.dueDate().isEqual(depositAmountupdatedFromDate)) {
currentInstallment.updateDepositAmountAndResetDerivedFields(newDepositAmount); currentInstallment.updateDepositAmountAndResetDerivedFields(newDepositAmount);
} else { } else {
currentInstallment.resetDerivedFields(); currentInstallment.resetDerivedFields();
@@ -959,19 +949,14 @@ public class RecurringDepositAccount extends SavingsAccount {
*/ */
protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex, protected boolean isTransactionInAdvanceOfInstallment(final int currentInstallmentIndex,
final List<RecurringDepositScheduleInstallment> installments, final LocalDate transactionDate) { final List<RecurringDepositScheduleInstallment> installments, final LocalDate transactionDate) {
final RecurringDepositScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex); final RecurringDepositScheduleInstallment currentInstallment = installments.get(currentInstallmentIndex);
return DateUtils.isBefore(transactionDate, currentInstallment.dueDate());
return transactionDate.isBefore(currentInstallment.dueDate());
} }
private Money handleInstallmentTransaction(final RecurringDepositScheduleInstallment currentInstallment, private Money handleInstallmentTransaction(final RecurringDepositScheduleInstallment currentInstallment,
final Money transactionAmountUnprocessed, final LocalDate transactionDate) { final Money transactionAmountUnprocessed, final LocalDate transactionDate) {
Money transactionAmountRemaining = transactionAmountUnprocessed; Money transactionAmountRemaining = transactionAmountUnprocessed;
Money depositAmountPortion = Money.zero(transactionAmountRemaining.getCurrency()); Money depositAmountPortion = currentInstallment.payInstallment(transactionDate, transactionAmountRemaining);
depositAmountPortion = currentInstallment.payInstallment(transactionDate, transactionAmountRemaining);
transactionAmountRemaining = transactionAmountRemaining.minus(depositAmountPortion); transactionAmountRemaining = transactionAmountRemaining.minus(depositAmountPortion);
return transactionAmountRemaining; return transactionAmountRemaining;
@@ -984,11 +969,11 @@ public class RecurringDepositAccount extends SavingsAccount {
private boolean isBeforeMaturityDate(final LocalDate compareDate) { private boolean isBeforeMaturityDate(final LocalDate compareDate) {
final LocalDate maturityDate = this.maturityDate(); final LocalDate maturityDate = this.maturityDate();
return maturityDate == null ? true : compareDate.isBefore(maturityDate); return maturityDate == null || DateUtils.isBefore(compareDate, maturityDate);
} }
private boolean isBeforeDepositStartDate(LocalDate compareDate) { private boolean isBeforeDepositStartDate(LocalDate compareDate) {
return compareDate.isBefore(depositStartDate()); return DateUtils.isBefore(compareDate, depositStartDate());
} }
public void validateDomainRules() { public void validateDomainRules() {
@@ -1003,7 +988,6 @@ public class RecurringDepositAccount extends SavingsAccount {
} }
private void validateDomainRules(final DataValidatorBuilder baseDataValidator) { private void validateDomainRules(final DataValidatorBuilder baseDataValidator) {
final boolean isMinTermGreaterThanMax = this.accountTermAndPreClosure.depositTermDetail() final boolean isMinTermGreaterThanMax = this.accountTermAndPreClosure.depositTermDetail()
.isMinDepositTermGreaterThanMaxDepositTerm(); .isMinDepositTermGreaterThanMaxDepositTerm();
// deposit period should be within min and max deposit term // deposit period should be within min and max deposit term
@@ -1063,8 +1047,8 @@ public class RecurringDepositAccount extends SavingsAccount {
.resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME); .resource(RECURRING_DEPOSIT_ACCOUNT_RESOURCE_NAME);
LocalDate maturityDate = calculateMaturityDate(); LocalDate maturityDate = calculateMaturityDate();
if (this.chart != null) { if (this.chart != null) {
final LocalDate chartFromDate = this.chart.getFromDateAsLocalDate(); final LocalDate chartFromDate = this.chart.getFromDate();
LocalDate chartEndDate = this.chart.getEndDateAsLocalDate(); LocalDate chartEndDate = this.chart.getEndDate();
chartEndDate = chartEndDate == null ? DateUtils.getBusinessLocalDate() : chartEndDate; chartEndDate = chartEndDate == null ? DateUtils.getBusinessLocalDate() : chartEndDate;
final LocalDateInterval chartInterval = LocalDateInterval.create(chartFromDate, chartEndDate); final LocalDateInterval chartInterval = LocalDateInterval.create(chartFromDate, chartEndDate);
@@ -1080,7 +1064,7 @@ public class RecurringDepositAccount extends SavingsAccount {
BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(maturityAmount, depositStartDate(), maturityDate, BigDecimal applicableInterestRate = this.chart.getApplicableInterestRate(maturityAmount, depositStartDate(), maturityDate,
this.client); this.client);
if (applicableInterestRate.compareTo(BigDecimal.ZERO) == 0 ? Boolean.TRUE : Boolean.FALSE) { if (applicableInterestRate.compareTo(BigDecimal.ZERO) == 0) {
baseDataValidator.reset() baseDataValidator.reset()
.failWithCodeNoParameterAddedToErrorCode("no.applicable.interest.rate.is.found.based.on.amount.and.deposit.period"); .failWithCodeNoParameterAddedToErrorCode("no.applicable.interest.rate.is.found.based.on.amount.and.deposit.period");
} }
@@ -1092,13 +1076,13 @@ public class RecurringDepositAccount extends SavingsAccount {
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
} }
/** /*
* final boolean recurringFrequencyBeforeDepositPeriod = recurringFrequencyBeforeDepositPeriod(); * final boolean recurringFrequencyBeforeDepositPeriod = recurringFrequencyBeforeDepositPeriod();
* *
* if (!recurringFrequencyBeforeDepositPeriod) { * if (!recurringFrequencyBeforeDepositPeriod) {
* baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode( * baseDataValidator.reset().failWithCodeNoParameterAddedToErrorCode(
* "recurring.frequency.not.before.deposit.period"); } * "recurring.frequency.not.before.deposit.period"); }
**/ */
} }
public boolean isReinvestOnClosure() { public boolean isReinvestOnClosure() {
@@ -1151,10 +1135,8 @@ public class RecurringDepositAccount extends SavingsAccount {
private boolean firstDepositDateBeforeAccountSubmittedOrActivationDate() { private boolean firstDepositDateBeforeAccountSubmittedOrActivationDate() {
final LocalDate expectedFirstDepositLocalDate = accountTermAndPreClosure.getExpectedFirstDepositOnDate(); final LocalDate expectedFirstDepositLocalDate = accountTermAndPreClosure.getExpectedFirstDepositOnDate();
if (expectedFirstDepositLocalDate == null) { return expectedFirstDepositLocalDate != null
return false; && DateUtils.isBefore(expectedFirstDepositLocalDate, accountSubmittedOrActivationDate());
}
return expectedFirstDepositLocalDate.isBefore(accountSubmittedOrActivationDate());
} }
public void setDatesFrom(final LocalDate now) { public void setDatesFrom(final LocalDate now) {
@@ -1192,7 +1174,7 @@ public class RecurringDepositAccount extends SavingsAccount {
int installmentNumber = 1; int installmentNumber = 1;
final LocalDate maturityDate = calcualteScheduleTillDate(frequency, recurringEvery); final LocalDate maturityDate = calcualteScheduleTillDate(frequency, recurringEvery);
final BigDecimal depositAmount = this.recurringDetail.mandatoryRecommendedDepositAmount(); final BigDecimal depositAmount = this.recurringDetail.mandatoryRecommendedDepositAmount();
while (maturityDate.isAfter(installmentDate)) { while (DateUtils.isBefore(installmentDate, maturityDate)) {
final RecurringDepositScheduleInstallment installment = RecurringDepositScheduleInstallment.installment(this, installmentNumber, final RecurringDepositScheduleInstallment installment = RecurringDepositScheduleInstallment.installment(this, installmentNumber,
installmentDate, depositAmount); installmentDate, depositAmount);
addDepositScheduleInstallment(installment); addDepositScheduleInstallment(installment);
@@ -1226,7 +1208,7 @@ public class RecurringDepositAccount extends SavingsAccount {
public void updateOverduePayments(final LocalDate todayDate) { public void updateOverduePayments(final LocalDate todayDate) {
LocalDate overdueUptoDate = this.maturityDate(); LocalDate overdueUptoDate = this.maturityDate();
if (overdueUptoDate == null || overdueUptoDate.isAfter(todayDate)) { if (overdueUptoDate == null || DateUtils.isAfter(overdueUptoDate, todayDate)) {
overdueUptoDate = todayDate; overdueUptoDate = todayDate;
} }
@@ -1234,7 +1216,7 @@ public class RecurringDepositAccount extends SavingsAccount {
int noOfOverdueInstallments = 0; int noOfOverdueInstallments = 0;
Money totalOverdueAmount = Money.zero(getCurrency()); Money totalOverdueAmount = Money.zero(getCurrency());
for (RecurringDepositScheduleInstallment installment : installments) { for (RecurringDepositScheduleInstallment installment : installments) {
if (installment.isNotFullyPaidOff() && overdueUptoDate.isAfter(installment.dueDate())) { if (installment.isNotFullyPaidOff() && DateUtils.isAfter(overdueUptoDate, installment.dueDate())) {
noOfOverdueInstallments++; noOfOverdueInstallments++;
totalOverdueAmount = totalOverdueAmount.plus(installment.getDepositAmountOutstanding(getCurrency())); totalOverdueAmount = totalOverdueAmount.plus(installment.getDepositAmountOutstanding(getCurrency()));
} }
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.portfolio.savings.domain; package org.apache.fineract.portfolio.savings.domain;
import static org.apache.fineract.infrastructure.core.service.DateUtils.getSystemZoneId;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
@@ -25,13 +27,17 @@ import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table; 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 java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
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;
@Entity @Entity
@Table(name = "m_mandatory_savings_schedule") @Table(name = "m_mandatory_savings_schedule")
public class RecurringDepositScheduleInstallment extends AbstractAuditableCustom { public class RecurringDepositScheduleInstallment extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne(optional = false) @ManyToOne(optional = false)
@JoinColumn(name = "savings_account_id") @JoinColumn(name = "savings_account_id")
@@ -64,15 +70,18 @@ public class RecurringDepositScheduleInstallment extends AbstractAuditableCustom
@Column(name = "obligations_met_on_date") @Column(name = "obligations_met_on_date")
private LocalDate obligationsMetOnDate; private LocalDate obligationsMetOnDate;
@Deprecated
@Column(name = "created_date")
private LocalDateTime createdDateToRemove;
@Deprecated
@Column(name = "lastmodified_date")
private LocalDateTime lastModifiedDateToRemove;
/** /**
* *
*/ */
protected RecurringDepositScheduleInstallment() { protected RecurringDepositScheduleInstallment() {}
this.installmentNumber = null;
this.fromDate = null;
this.dueDate = null;
this.obligationsMet = false;
}
/** /**
* @param account * @param account
@@ -165,6 +174,24 @@ public class RecurringDepositScheduleInstallment extends AbstractAuditableCustom
return this.dueDate; return this.dueDate;
} }
public Integer installmentNumber() {
return this.installmentNumber;
}
@Override
public Optional<OffsetDateTime> getCreatedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getCreatedDate()
.orElse(createdDateToRemove == null ? null : createdDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
}
@Override
public Optional<OffsetDateTime> getLastModifiedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getLastModifiedDate()
.orElse(lastModifiedDateToRemove == null ? null : lastModifiedDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
}
public Money payInstallment(final LocalDate transactionDate, final Money transactionAmountRemaining) { public Money payInstallment(final LocalDate transactionDate, final Money transactionAmountRemaining) {
final MonetaryCurrency currency = transactionAmountRemaining.getCurrency(); final MonetaryCurrency currency = transactionAmountRemaining.getCurrency();
@@ -209,11 +236,11 @@ public class RecurringDepositScheduleInstallment extends AbstractAuditableCustom
} }
private boolean isInAdvance(final LocalDate transactionDate) { private boolean isInAdvance(final LocalDate transactionDate) {
return transactionDate.isBefore(dueDate()); return DateUtils.isBefore(transactionDate, dueDate());
} }
private boolean isLatePayment(final LocalDate transactionDate) { private boolean isLatePayment(final LocalDate transactionDate) {
return transactionDate.isAfter(dueDate()); return DateUtils.isAfter(transactionDate, dueDate());
} }
private Money asMoney(final BigDecimal decimal, final MonetaryCurrency currency) { private Money asMoney(final BigDecimal decimal, final MonetaryCurrency currency) {
@@ -232,8 +259,4 @@ public class RecurringDepositScheduleInstallment extends AbstractAuditableCustom
this.depositAmount = newDepositAmount; this.depositAmount = newDepositAmount;
this.resetDerivedFields(); this.resetDerivedFields();
} }
public Integer installmentNumber() {
return this.installmentNumber;
}
} }
@@ -56,7 +56,6 @@ import org.apache.fineract.infrastructure.configuration.domain.ConfigurationDoma
import org.apache.fineract.infrastructure.core.api.JsonCommand; import org.apache.fineract.infrastructure.core.api.JsonCommand;
import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException; import org.apache.fineract.infrastructure.core.exception.UnsupportedParameterException;
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.ExternalIdFactory; import org.apache.fineract.infrastructure.core.service.ExternalIdFactory;
import org.apache.fineract.organisation.staff.domain.Staff; import org.apache.fineract.organisation.staff.domain.Staff;
import org.apache.fineract.organisation.staff.domain.StaffRepositoryWrapper; import org.apache.fineract.organisation.staff.domain.StaffRepositoryWrapper;
@@ -333,7 +332,7 @@ public class SavingsAccountAssembler {
nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax); nominalAnnualInterestRateOverdraft, minOverdraftForInterestCalculation, withHoldTax);
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.validateNewApplicationState(DateUtils.getBusinessLocalDate(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateNewApplicationState(SAVINGS_ACCOUNT_RESOURCE_NAME);
account.validateAccountValuesWithProduct(); account.validateAccountValuesWithProduct();
@@ -349,7 +348,7 @@ public class SavingsAccountAssembler {
List<SavingsAccountTransaction> savingsAccountTransactions = null; List<SavingsAccountTransaction> savingsAccountTransactions = null;
if (backdatedTxnsAllowedTill) { if (backdatedTxnsAllowedTill) {
LocalDate pivotDate = account.getSummary().getInterestPostedTillDate(); LocalDate pivotDate = account.getSummary().getInterestPostedTillDate();
boolean isNotPresent = pivotDate == null ? true : false; boolean isNotPresent = pivotDate == null;
if (!isNotPresent) { if (!isNotPresent) {
// Get savings account transactions // Get savings account transactions
if (isRelaxingDaysConfigForPivotDateEnabled()) { if (isRelaxingDaysConfigForPivotDateEnabled()) {
@@ -468,7 +467,7 @@ public class SavingsAccountAssembler {
product.withHoldTax()); product.withHoldTax());
account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper); account.setHelpers(this.savingsAccountTransactionSummaryWrapper, this.savingsHelper);
account.validateNewApplicationState(DateUtils.getBusinessLocalDate(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateNewApplicationState(SAVINGS_ACCOUNT_RESOURCE_NAME);
account.validateAccountValuesWithProduct(); account.validateAccountValuesWithProduct();
@@ -41,7 +41,9 @@ import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
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.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.service.DateUtils;
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;
import org.apache.fineract.organisation.monetary.domain.MoneyHelper; import org.apache.fineract.organisation.monetary.domain.MoneyHelper;
@@ -58,7 +60,7 @@ import org.slf4j.LoggerFactory;
*/ */
@Entity @Entity
@Table(name = "m_savings_account_charge") @Table(name = "m_savings_account_charge")
public class SavingsAccountCharge extends AbstractPersistableCustom { public class SavingsAccountCharge extends AbstractAuditableWithUTCDateTimeCustom {
private static final Logger LOG = LoggerFactory.getLogger(SavingsAccountCharge.class); private static final Logger LOG = LoggerFactory.getLogger(SavingsAccountCharge.class);
@@ -448,7 +450,7 @@ public class SavingsAccountCharge extends AbstractPersistableCustom {
final String dateFormatAsInput = command.dateFormat(); final String dateFormatAsInput = command.dateFormat();
final String localeAsInput = command.locale(); final String localeAsInput = command.locale();
if (command.isChangeInLocalDateParameterNamed(dueAsOfDateParamName, getDueLocalDate())) { if (command.isChangeInLocalDateParameterNamed(dueAsOfDateParamName, getDueDate())) {
final String valueAsInput = command.stringValueOfParameterNamed(dueAsOfDateParamName); final String valueAsInput = command.stringValueOfParameterNamed(dueAsOfDateParamName);
actualChanges.put(dueAsOfDateParamName, valueAsInput); actualChanges.put(dueAsOfDateParamName, valueAsInput);
actualChanges.put(dateFormatParamName, dateFormatAsInput); actualChanges.put(dateFormatParamName, dateFormatAsInput);
@@ -521,7 +523,7 @@ public class SavingsAccountCharge extends AbstractPersistableCustom {
return value.compareTo(BigDecimal.ZERO) > 0; return value.compareTo(BigDecimal.ZERO) > 0;
} }
public LocalDate getDueLocalDate() { public LocalDate getDueDate() {
return this.dueDate; return this.dueDate;
} }
@@ -754,17 +756,13 @@ public class SavingsAccountCharge extends AbstractPersistableCustom {
SavingsAccountCharge that = (SavingsAccountCharge) o; SavingsAccountCharge that = (SavingsAccountCharge) o;
return (penaltyCharge == that.penaltyCharge) && (paid == that.paid) && (waived == that.waived) && (status == that.status) return (penaltyCharge == that.penaltyCharge) && (paid == that.paid) && (waived == that.waived) && (status == that.status)
&& Objects.equals(savingsAccount, that.savingsAccount) && Objects.equals(charge, that.charge) && Objects.equals(savingsAccount, that.savingsAccount) && Objects.equals(charge, that.charge)
&& Objects.equals(chargeTime, that.chargeTime) && dueDate.compareTo(that.dueDate) == 0 && Objects.equals(chargeTime, that.chargeTime) && DateUtils.isEqual(dueDate, that.dueDate)
? Boolean.TRUE && Objects.equals(feeOnMonth, that.feeOnMonth) && Objects.equals(feeOnDay, that.feeOnDay)
: Boolean.FALSE && Objects.equals(feeOnMonth, that.feeOnMonth) && Objects.equals(feeOnDay, that.feeOnDay) && Objects.equals(feeInterval, that.feeInterval) && Objects.equals(chargeCalculation, that.chargeCalculation)
&& Objects.equals(feeInterval, that.feeInterval) && Objects.equals(percentage, that.percentage) && Objects.equals(amountPercentageAppliedTo, that.amountPercentageAppliedTo)
&& Objects.equals(chargeCalculation, that.chargeCalculation) && Objects.equals(percentage, that.percentage) && Objects.equals(amount, that.amount) && Objects.equals(amountPaid, that.amountPaid)
&& Objects.equals(amountPercentageAppliedTo, that.amountPercentageAppliedTo) && Objects.equals(amountWaived, that.amountWaived) && Objects.equals(amountWrittenOff, that.amountWrittenOff)
&& Objects.equals(amount, that.amount) && Objects.equals(amountPaid, that.amountPaid) && Objects.equals(amountOutstanding, that.amountOutstanding) && DateUtils.isEqual(inactivationDate, that.inactivationDate);
&& Objects.equals(amountWaived, that.amountWaived)
&& Objects.equals(amountWrittenOff, that.amountWrittenOff)
&& Objects.equals(amountOutstanding, that.amountOutstanding)
&& inactivationDate.compareTo(that.inactivationDate) == 0 ? Boolean.TRUE : Boolean.FALSE;
} }
@Override @Override
@@ -803,12 +801,12 @@ public class SavingsAccountCharge extends AbstractPersistableCustom {
if (isAnnualFee() || isMonthlyFee()) { if (isAnnualFee() || isMonthlyFee()) {
nextDueLocalDate = startingDate.withMonth(this.feeOnMonth); nextDueLocalDate = startingDate.withMonth(this.feeOnMonth);
nextDueLocalDate = setDayOfMonth(nextDueLocalDate); nextDueLocalDate = setDayOfMonth(nextDueLocalDate);
while (startingDate.isAfter(nextDueLocalDate)) { while (DateUtils.isBefore(nextDueLocalDate, startingDate)) {
nextDueLocalDate = calculateNextDueDate(nextDueLocalDate); nextDueLocalDate = calculateNextDueDate(nextDueLocalDate);
} }
} else if (isWeeklyFee()) { } else if (isWeeklyFee()) {
nextDueLocalDate = getDueLocalDate(); nextDueLocalDate = getDueDate();
while (startingDate.isAfter(nextDueLocalDate)) { while (DateUtils.isBefore(nextDueLocalDate, startingDate)) {
nextDueLocalDate = calculateNextDueDate(nextDueLocalDate); nextDueLocalDate = calculateNextDueDate(nextDueLocalDate);
} }
} else { } else {
@@ -883,12 +881,11 @@ public class SavingsAccountCharge extends AbstractPersistableCustom {
} }
public boolean isChargeIsDue(final LocalDate nextDueDate) { public boolean isChargeIsDue(final LocalDate nextDueDate) {
return this.getDueLocalDate().isBefore(nextDueDate); return DateUtils.isBefore(getDueDate(), nextDueDate);
} }
public boolean isChargeIsOverPaid(final LocalDate nextDueDate) { public boolean isChargeIsOverPaid(final LocalDate nextDueDate) {
final BigDecimal amountPaid = this.amountPaid == null ? BigDecimal.ZERO : amountPaid(); return DateUtils.isAfter(getDueDate(), nextDueDate) && MathUtil.isGreaterThanZero(amountPaid());
return this.getDueLocalDate().isAfter(nextDueDate) && amountPaid.compareTo(BigDecimal.ZERO) > 0;
} }
private BigDecimal amountPaid() { private BigDecimal amountPaid() {
@@ -25,7 +25,6 @@ import java.util.List;
import java.util.Set; import java.util.Set;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail; import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues; import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
import org.apache.fineract.useradministration.domain.AppUser;
public interface SavingsAccountDomainService { public interface SavingsAccountDomainService {
@@ -46,6 +45,5 @@ public interface SavingsAccountDomainService {
SavingsAccountTransaction handleReversal(SavingsAccount account, List<SavingsAccountTransaction> savingsAccountTransactions, SavingsAccountTransaction handleReversal(SavingsAccount account, List<SavingsAccountTransaction> savingsAccountTransactions,
boolean backdatedTxnsAllowedTill); boolean backdatedTxnsAllowedTill);
SavingsAccountTransaction handleHold(SavingsAccount account, AppUser createdUser, BigDecimal amount, LocalDate transactionDate, SavingsAccountTransaction handleHold(SavingsAccount account, BigDecimal amount, LocalDate transactionDate, Boolean lienAllowed);
Boolean lienAllowed);
} }
@@ -43,7 +43,6 @@ import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues; import org.apache.fineract.portfolio.savings.SavingsTransactionBooleanValues;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionDTO;
import org.apache.fineract.portfolio.savings.exception.DepositAccountTransactionNotAllowedException; import org.apache.fineract.portfolio.savings.exception.DepositAccountTransactionNotAllowedException;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
@@ -83,8 +82,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt, public SavingsAccountTransaction handleWithdrawal(final SavingsAccount account, final DateTimeFormatter fmt,
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final SavingsTransactionBooleanValues transactionBooleanValues, final boolean backdatedTxnsAllowedTill) { final SavingsTransactionBooleanValues transactionBooleanValues, final boolean backdatedTxnsAllowedTill) {
context.authenticatedUser();
AppUser user = getAppUserIfPresent();
account.validateForAccountBlock(); account.validateForAccountBlock();
account.validateForDebitBlock(); account.validateForDebitBlock();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
@@ -107,7 +105,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
Integer accountType = null; Integer accountType = null;
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, DateUtils.getLocalDateTimeOfSystem(), user, accountType); paymentDetail, null, accountType);
UUID refNo = UUID.randomUUID(); UUID refNo = UUID.randomUUID();
final SavingsAccountTransaction withdrawal = account.withdraw(transactionDTO, transactionBooleanValues.isApplyWithdrawFee(), final SavingsAccountTransaction withdrawal = account.withdraw(transactionDTO, transactionBooleanValues.isApplyWithdrawFee(),
backdatedTxnsAllowedTill, relaxingDaysConfigForPivotDate, refNo.toString()); backdatedTxnsAllowedTill, relaxingDaysConfigForPivotDate, refNo.toString());
@@ -147,14 +145,6 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
return withdrawal; return withdrawal;
} }
private AppUser getAppUserIfPresent() {
AppUser user = null;
if (this.context != null) {
user = this.context.getAuthenticatedUserIfPresent();
}
return user;
}
@Transactional @Transactional
@Override @Override
public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt, public SavingsAccountTransaction handleDeposit(final SavingsAccount account, final DateTimeFormatter fmt,
@@ -169,7 +159,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail, final LocalDate transactionDate, final BigDecimal transactionAmount, final PaymentDetail paymentDetail,
final boolean isAccountTransfer, final boolean isRegularTransaction, final boolean isAccountTransfer, final boolean isRegularTransaction,
final SavingsAccountTransactionType savingsAccountTransactionType, final boolean backdatedTxnsAllowedTill) { final SavingsAccountTransactionType savingsAccountTransactionType, final boolean backdatedTxnsAllowedTill) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
account.validateForAccountBlock(); account.validateForAccountBlock();
account.validateForCreditBlock(); account.validateForCreditBlock();
@@ -193,7 +183,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
Integer accountType = null; Integer accountType = null;
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, DateUtils.getLocalDateTimeOfSystem(), user, accountType); paymentDetail, null, accountType);
UUID refNo = UUID.randomUUID(); UUID refNo = UUID.randomUUID();
final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType, backdatedTxnsAllowedTill, final SavingsAccountTransaction deposit = account.deposit(transactionDTO, savingsAccountTransactionType, backdatedTxnsAllowedTill,
relaxingDaysConfigForPivotDate, refNo.toString()); relaxingDaysConfigForPivotDate, refNo.toString());
@@ -226,13 +216,10 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
@Transactional @Transactional
@Override @Override
public SavingsAccountTransaction handleHold(final SavingsAccount account, final AppUser createdUser, BigDecimal amount, public SavingsAccountTransaction handleHold(final SavingsAccount account, BigDecimal amount, LocalDate transactionDate,
LocalDate transactionDate, Boolean lienAllowed) { Boolean lienAllowed) {
final PaymentDetail paymentDetails = null; return SavingsAccountTransaction.holdAmount(account, account.office(), null, transactionDate,
Money.of(account.getCurrency(), amount), lienAllowed);
SavingsAccountTransaction transaction = SavingsAccountTransaction.holdAmount(account, account.office(), paymentDetails,
transactionDate, Money.of(account.getCurrency(), amount), DateUtils.getLocalDateTimeOfSystem(), createdUser, lienAllowed);
return transaction;
} }
@Override @Override
@@ -322,7 +309,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode()); final MathContext mc = new MathContext(15, MoneyHelper.getRoundingMode());
for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) { for (SavingsAccountTransaction savingsAccountTransaction : savingsAccountTransactions) {
if (savingsAccountTransaction.isPostInterestCalculationRequired() if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), backdatedTxnsAllowedTill)) { && account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), backdatedTxnsAllowedTill)) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, backdatedTxnsAllowedTill, postReversals); postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
@@ -330,7 +317,7 @@ public class SavingsAccountDomainServiceJpa implements SavingsAccountDomainServi
account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.calculateInterestUsing(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals); financialYearBeginningMonth, postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
} }
account.validatePivotDateTransaction(savingsAccountTransaction.getLastTransactionDate(), backdatedTxnsAllowedTill, account.validatePivotDateTransaction(savingsAccountTransaction.getTransactionDate(), backdatedTxnsAllowedTill,
relaxingDaysConfigForPivotDate, "savingsaccount"); relaxingDaysConfigForPivotDate, "savingsaccount");
account.validateAccountBalanceDoesNotBecomeNegativeMinimal(savingsAccountTransaction.getAmount(), false); account.validateAccountBalanceDoesNotBecomeNegativeMinimal(savingsAccountTransaction.getAmount(), false);
account.activateAccountBasedOnBalance(); account.activateAccountBasedOnBalance();
@@ -216,7 +216,7 @@ public final class SavingsAccountSummary {
final SavingsAccountTransaction savingsAccountTransaction = savingsAccountTransactions.get(i); final SavingsAccountTransaction savingsAccountTransaction = savingsAccountTransactions.get(i);
if (savingsAccountTransaction.isInterestPostingAndNotReversed() && !savingsAccountTransaction.isReversalTransaction() if (savingsAccountTransaction.isInterestPostingAndNotReversed() && !savingsAccountTransaction.isReversalTransaction()
&& !isUpdated) { && !isUpdated) {
setInterestPostedTillDate(savingsAccountTransaction.getLastTransactionDate()); setInterestPostedTillDate(savingsAccountTransaction.getTransactionDate());
isUpdated = true; isUpdated = true;
if (!backdatedTxnsAllowedTill) { if (!backdatedTxnsAllowedTill) {
break; break;
@@ -224,7 +224,7 @@ public final class SavingsAccountSummary {
} }
if (savingsAccountTransaction.isOverdraftInterestAndNotReversed() && !savingsAccountTransaction.isReversalTransaction() if (savingsAccountTransaction.isOverdraftInterestAndNotReversed() && !savingsAccountTransaction.isReversalTransaction()
&& !isUpdated) { && !isUpdated) {
setInterestPostedTillDate(savingsAccountTransaction.getLastTransactionDate()); setInterestPostedTillDate(savingsAccountTransaction.getTransactionDate());
isUpdated = true; isUpdated = true;
if (!backdatedTxnsAllowedTill) { if (!backdatedTxnsAllowedTill) {
break; break;
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.portfolio.savings.domain; package org.apache.fineract.portfolio.savings.domain;
import static org.apache.fineract.infrastructure.core.service.DateUtils.getSystemZoneId;
import jakarta.persistence.CascadeType; import jakarta.persistence.CascadeType;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
@@ -29,14 +31,16 @@ import jakarta.persistence.Table;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import org.apache.fineract.infrastructure.core.domain.AbstractPersistableCustom; import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval; import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency; import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
@@ -50,7 +54,6 @@ import org.apache.fineract.portfolio.savings.domain.interest.EndOfDayBalance;
import org.apache.fineract.portfolio.savings.domain.interest.SavingsAccountTransactionDetailsForPostingPeriod; import org.apache.fineract.portfolio.savings.domain.interest.SavingsAccountTransactionDetailsForPostingPeriod;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations; import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.tax.domain.TaxComponent; import org.apache.fineract.portfolio.tax.domain.TaxComponent;
import org.apache.fineract.useradministration.domain.AppUser;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
/** /**
@@ -58,7 +61,7 @@ import org.springframework.util.CollectionUtils;
*/ */
@Entity @Entity
@Table(name = "m_savings_account_transaction") @Table(name = "m_savings_account_transaction")
public final class SavingsAccountTransaction extends AbstractPersistableCustom { public final class SavingsAccountTransaction extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne(optional = false) @ManyToOne(optional = false)
@JoinColumn(name = "savings_account_id", referencedColumnName = "id", nullable = false) @JoinColumn(name = "savings_account_id", referencedColumnName = "id", nullable = false)
@@ -102,16 +105,13 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
@Column(name = "overdraft_amount_derived", scale = 6, precision = 19, nullable = true) @Column(name = "overdraft_amount_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal overdraftAmount; private BigDecimal overdraftAmount;
@Column(name = "created_date", nullable = false) @Deprecated
private LocalDateTime createdDate; @Column(name = "created_date", nullable = true)
private LocalDateTime createdDateToRemove;
@Column(name = "submitted_on_date", nullable = false) @Column(name = "submitted_on_date", nullable = false)
private LocalDate submittedOnDate; private LocalDate submittedOnDate;
@ManyToOne
@JoinColumn(name = "appuser_id", nullable = true)
private AppUser appUser;
@Column(name = "is_manual", length = 1, nullable = true) @Column(name = "is_manual", length = 1, nullable = true)
private boolean isManualTransaction; private boolean isManualTransaction;
@@ -143,16 +143,11 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
@Column(name = "ref_no", nullable = true) @Column(name = "ref_no", nullable = true)
private String refNo; private String refNo;
SavingsAccountTransaction() { SavingsAccountTransaction() {}
this.dateOf = null;
this.typeOf = null;
this.createdDate = null;
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail, private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final LocalDateTime createdDate, final BigDecimal amount, final Integer typeOf, final LocalDate transactionLocalDate, final BigDecimal amount, final boolean isReversed,
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction, final Boolean lienTransaction, final boolean isManualTransaction, final Boolean lienTransaction, final String refNo) {
final String refNo) {
this.savingsAccount = savingsAccount; this.savingsAccount = savingsAccount;
this.office = office; this.office = office;
this.typeOf = typeOf; this.typeOf = typeOf;
@@ -160,42 +155,52 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
this.amount = amount; this.amount = amount;
this.reversed = isReversed; this.reversed = isReversed;
this.paymentDetail = paymentDetail; this.paymentDetail = paymentDetail;
this.createdDate = createdDate; this.createdDateToRemove = null; // #audit backward compatibility deprecated
this.submittedOnDate = DateUtils.getBusinessLocalDate(); this.submittedOnDate = DateUtils.getBusinessLocalDate();
this.appUser = appUser;
this.isManualTransaction = isManualTransaction; this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction; this.lienTransaction = lienTransaction;
this.refNo = refNo; this.refNo = refNo;
} }
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf,
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final boolean isManualTransaction,
final Boolean lienTransaction, final String refNo) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final Money amount, final boolean isReversed,
final boolean isManualTransaction, final Boolean lienTransaction, final String refNo) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, amount.getAmount(), isReversed, isManualTransaction,
lienTransaction, refNo);
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office, public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, LocalDateTime createdDate, final AppUser appUser, final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final String refNo) {
final String refNo) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date,
createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction, refNo); amount, isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office, public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, LocalDateTime createdDate, final AppUser appUser, final PaymentDetail paymentDetail, final LocalDate date, final Money amount,
final SavingsAccountTransactionType savingsAccountTransactionType, final String refNo) { final SavingsAccountTransactionType savingsAccountTransactionType, final String refNo) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date, amount,
createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office, public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, LocalDateTime createdDate, final AppUser appUser, final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final String refNo) {
final String refNo) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(), return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(),
date, createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction, refNo); date, amount, isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -204,7 +209,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date, return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date,
amount, isReversed, null, isManualTransaction, lienTransaction, refNo); amount, isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office, public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office,
@@ -213,82 +218,82 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date, return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date,
amount, isReversed, null, isManualTransaction, lienTransaction, refNo); amount, isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser, final String refNo) { final Money amount, final String refNo) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount, return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount,
isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount, return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount, return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount,
isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final AppUser appUser) { final Money amount) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount, return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount,
isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office,
final AppUser appUser) { final LocalDate date) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, DateUtils.getLocalDateTimeOfSystem(), SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office,
final AppUser appUser) { final LocalDate date) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, DateUtils.getLocalDateTimeOfSystem(), SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office,
final AppUser appUser) { final LocalDate date) {
final boolean isReversed = false; final boolean isReversed = false;
final boolean isManualTransaction = false; final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, DateUtils.getLocalDateTimeOfSystem(), SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, isManualTransaction, lienTransaction, refNo); isReversed, isManualTransaction, lienTransaction, refNo);
} }
public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date, public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
@@ -298,21 +303,51 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, null, isManualTransaction, lienTransaction, SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, isManualTransaction, lienTransaction,
refNo); refNo);
updateTaxDetails(taxDetails, accountTransaction); updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction; return accountTransaction;
} }
public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date, final AppUser appUser, public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date,
final boolean accountTransaction) { final boolean accountTransaction) {
final boolean isReversed = false; final boolean isReversed = false;
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false; final Boolean lienTransaction = false;
final String refNo = null; final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail, return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, DateUtils.getLocalDateTimeOfSystem(), SavingsAccountTransactionType.ESCHEAT.getValue(), date, savingsAccount.getSummary().getAccountBalance(), isReversed,
savingsAccount.getSummary().getAccountBalance(), isReversed, appUser, accountTransaction, lienTransaction, refNo); accountTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
accountTransaction.typeOf, accountTransaction.getTransactionDate(), accountTransaction.amount, accountTransaction.reversed,
accountTransaction.isManualTransaction, accountTransaction.lienTransaction, accountTransaction.refNo);
}
public static SavingsAccountTransaction holdAmount(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final Boolean lienTransaction) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.AMOUNT_HOLD.getValue(),
date, amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction releaseAmount(SavingsAccountTransaction accountTransaction, LocalDate transactionDate) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
SavingsAccountTransactionType.AMOUNT_RELEASE.getValue(), transactionDate, accountTransaction.amount,
accountTransaction.reversed, accountTransaction.isManualTransaction, accountTransaction.lienTransaction,
accountTransaction.refNo);
}
public static SavingsAccountTransaction reversal(SavingsAccountTransaction accountTransaction) {
SavingsAccountTransaction sat = copyTransaction(accountTransaction);
sat.reversed = false;
sat.setReversalTransaction(true);
sat.originalTxnId = accountTransaction.getId();
return sat;
} }
public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails, public static void updateTaxDetails(final Map<TaxComponent, BigDecimal> taxDetails,
@@ -325,71 +360,142 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
} }
} }
public static SavingsAccountTransaction copyTransaction(SavingsAccountTransaction accountTransaction) { public SavingsAccount getSavingsAccount() {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail, return this.savingsAccount;
accountTransaction.typeOf, accountTransaction.transactionLocalDate(), DateUtils.getLocalDateTimeOfSystem(),
accountTransaction.amount, accountTransaction.reversed, accountTransaction.appUser, accountTransaction.isManualTransaction,
accountTransaction.lienTransaction, accountTransaction.refNo);
} }
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final Integer typeOf, public LocalDate getTransactionDate() {
final LocalDate transactionLocalDate, final Money amount, final boolean isReversed, final AppUser appUser,
final boolean isManualTransaction, final Boolean lienTransaction, final String refNo) {
this(savingsAccount, office, null, typeOf, transactionLocalDate, DateUtils.getLocalDateTimeOfSystem(), amount, isReversed, appUser,
isManualTransaction, lienTransaction, refNo);
}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final LocalDateTime createdDate, final Money amount,
final boolean isReversed, final AppUser appUser, final boolean isManualTransaction, final Boolean lienTransaction,
final String refNo) {
this(savingsAccount, office, paymentDetail, typeOf, transactionLocalDate, createdDate, amount.getAmount(), isReversed, appUser,
isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction holdAmount(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, LocalDateTime createdDate, final AppUser appUser,
final Boolean lienTransaction) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.AMOUNT_HOLD.getValue(),
date, createdDate, amount, isReversed, appUser, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction releaseAmount(SavingsAccountTransaction accountTransaction, LocalDate transactionDate,
LocalDateTime createdDate, final AppUser appUser) {
return new SavingsAccountTransaction(accountTransaction.savingsAccount, accountTransaction.office, accountTransaction.paymentDetail,
SavingsAccountTransactionType.AMOUNT_RELEASE.getValue(), transactionDate, createdDate, accountTransaction.amount,
accountTransaction.reversed, appUser, accountTransaction.isManualTransaction, accountTransaction.lienTransaction,
accountTransaction.refNo);
}
public static SavingsAccountTransaction reversal(SavingsAccountTransaction accountTransaction) {
SavingsAccountTransaction sat = copyTransaction(accountTransaction);
sat.reversed = false;
sat.setReversalTransaction(true);
sat.originalTxnId = accountTransaction.getId();
return sat;
}
public LocalDate transactionLocalDate() {
return this.dateOf; return this.dateOf;
} }
public LocalDate getEndOfBalanceDate() {
return balanceEndDate;
}
public boolean isReversed() {
return this.reversed;
}
public void reverse() { public void reverse() {
this.reversed = true; this.reversed = true;
} }
public BigDecimal getAmount() {
return this.amount;
}
public Money getAmount(final MonetaryCurrency currency) { public Money getAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.amount); return Money.of(currency, this.amount);
} }
public void setAmount(final Money amount) {
this.amount = amount == null ? null : amount.getAmount();
}
public BigDecimal getRunningBalance() {
return this.runningBalance;
}
public Money getRunningBalance(final MonetaryCurrency currency) { public Money getRunningBalance(final MonetaryCurrency currency) {
return Money.of(currency, this.runningBalance); return Money.of(currency, this.runningBalance);
} }
public void setRunningBalance(Money balance) {
this.runningBalance = balance == null ? null : balance.getAmount();
}
public boolean isManualTransaction() {
return this.isManualTransaction;
}
public Set<SavingsAccountChargePaidBy> getSavingsAccountChargesPaid() {
return this.savingsAccountChargesPaid;
}
public BigDecimal getOverdraftAmount() {
return this.overdraftAmount;
}
public Money getOverdraftAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.overdraftAmount);
}
void setOverdraftAmount(Money overdraftAmount) {
this.overdraftAmount = overdraftAmount == null ? null : overdraftAmount.getAmount();
}
public List<SavingsAccountTransactionTaxDetails> getTaxDetails() {
return this.taxDetails;
}
public List<Note> getNotes() {
return notes;
}
public Integer getTypeOf() {
return this.typeOf;
}
public SavingsAccountTransactionType getTransactionType() {
return SavingsAccountTransactionType.fromInt(this.typeOf);
}
public LocalDate getDateOf() {
return this.dateOf;
}
public String getRefNo() {
return this.refNo;
}
public PaymentDetail getPaymentDetail() {
return this.paymentDetail;
}
public void updateReleaseId(Long releaseId) {
this.releaseIdOfHoldAmountTransaction = releaseId;
}
public void updateReason(String reasonForBlock) {
this.reasonForBlock = reasonForBlock;
}
public Long getReleaseIdOfHoldAmountTransaction() {
return this.releaseIdOfHoldAmountTransaction;
}
public MonetaryCurrency getCurrency() {
return savingsAccount == null ? null : savingsAccount.getCurrency();
}
public Long getOfficeId() {
return this.office.getId();
}
public LocalDate getBalanceEndDate() {
return this.balanceEndDate;
}
public BigDecimal getCumulativeBalance() {
return this.cumulativeBalance;
}
public Integer getBalanceNumberOfDays() {
return this.balanceNumberOfDays;
}
public LocalDate getSubmittedOnDate() {
return submittedOnDate;
}
public boolean isReversalTransaction() {
return reversalTransaction;
}
void setReversalTransaction(boolean reversalTransaction) {
this.reversalTransaction = reversalTransaction;
}
public boolean isDeposit() { public boolean isDeposit() {
return getTransactionType().isDeposit(); return getTransactionType().isDeposit();
} }
@@ -442,10 +548,6 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
return !isReversed(); return !isReversed();
} }
public boolean isReversed() {
return this.reversed;
}
public boolean isTransferInitiation() { public boolean isTransferInitiation() {
return getTransactionType().isTransferInitiation(); return getTransactionType().isTransferInitiation();
} }
@@ -466,14 +568,6 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
return isTransferInitiation() || isTransferApproval() || isTransferRejection() || isTransferWithdrawal(); return isTransferInitiation() || isTransferApproval() || isTransferRejection() || isTransferWithdrawal();
} }
public boolean occursOn(final LocalDate occursOnDate) {
return getTransactionLocalDate().isEqual(occursOnDate);
}
public void setLoanDisbursement(boolean isLoanDisbursement) {
this.isLoanDisbursement = isLoanDisbursement;
}
public void zeroBalanceFields() { public void zeroBalanceFields() {
this.runningBalance = null; this.runningBalance = null;
this.cumulativeBalance = null; this.cumulativeBalance = null;
@@ -481,35 +575,19 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
this.balanceNumberOfDays = null; this.balanceNumberOfDays = null;
} }
public void updateRunningBalance(final Money balance) {
this.runningBalance = balance.getAmount();
}
public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) { public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
// balance end date should not be before transaction date // balance end date should not be before transaction date
if (endOfBalanceDate != null && endOfBalanceDate.isBefore(this.transactionLocalDate())) { if (endOfBalanceDate != null && DateUtils.isBefore(endOfBalanceDate, this.getTransactionDate())) {
this.balanceEndDate = this.transactionLocalDate(); this.balanceEndDate = this.getTransactionDate();
} else { } else {
this.balanceEndDate = endOfBalanceDate; this.balanceEndDate = endOfBalanceDate;
} }
this.balanceNumberOfDays = LocalDateInterval.create(getTransactionLocalDate(), endOfBalanceDate).daysInPeriodInclusiveOfEndDate(); this.balanceNumberOfDays = LocalDateInterval.create(getTransactionDate(), endOfBalanceDate).daysInPeriodInclusiveOfEndDate();
this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount(); this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
} }
public LocalDate getTransactionLocalDate() {
return this.dateOf;
}
public LocalDate getLastTransactionDate() {
return this.dateOf;
}
public LocalDate getEndOfBalanceLocalDate() {
return balanceEndDate;
}
public boolean isAcceptableForDailyBalance(final LocalDateInterval interestPeriodInterval) { public boolean isAcceptableForDailyBalance(final LocalDateInterval interestPeriodInterval) {
return isNotReversed() && interestPeriodInterval.contains(getTransactionLocalDate()) && isABalanceForAtLeastOneDay(); return isNotReversed() && interestPeriodInterval.contains(getTransactionDate()) && isABalanceForAtLeastOneDay();
} }
private boolean isABalanceForAtLeastOneDay() { private boolean isABalanceForAtLeastOneDay() {
@@ -530,7 +608,7 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
thisTransactionData.put("officeId", this.office.getId()); thisTransactionData.put("officeId", this.office.getId());
thisTransactionData.put("type", transactionType); thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", isReversed()); thisTransactionData.put("reversed", isReversed());
thisTransactionData.put("date", getTransactionLocalDate()); thisTransactionData.put("date", getTransactionDate());
thisTransactionData.put("currencyCode", currencyCode); thisTransactionData.put("currencyCode", currencyCode);
thisTransactionData.put("amount", this.amount); thisTransactionData.put("amount", this.amount);
thisTransactionData.put("overdraftAmount", this.overdraftAmount); thisTransactionData.put("overdraftAmount", this.overdraftAmount);
@@ -573,29 +651,28 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
return thisTransactionData; return thisTransactionData;
} }
public boolean isAfter(final LocalDate transactionDate) { public boolean isBefore(final LocalDate date) {
return getTransactionLocalDate().isAfter(transactionDate); return DateUtils.isBefore(getTransactionDate(), date);
} }
public boolean isManualTransaction() { public boolean isAfter(final LocalDate date) {
return this.isManualTransaction; return DateUtils.isAfter(getTransactionDate(), date);
} }
public void setPostInterestAsOn(boolean isManualTransaction) { public boolean occursOn(final LocalDate date) {
this.isManualTransaction = isManualTransaction; return DateUtils.isEqual(getTransactionDate(), date);
} }
public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) { public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) {
final Money endOfDayBalance = Money.of(currency, this.runningBalance); final Money endOfDayBalance = Money.of(currency, this.runningBalance);
final Money openingBalance = endOfDayBalance; final Money openingBalance = endOfDayBalance;
LocalDate balanceDate = periodInterval.startDate(); LocalDate balanceDate = periodInterval.startDate();
int numberOfDays = periodInterval.daysInPeriodInclusiveOfEndDate(); int numberOfDays = periodInterval.daysInPeriodInclusiveOfEndDate();
if (periodInterval.contains(getTransactionLocalDate())) { if (periodInterval.contains(getTransactionDate())) {
balanceDate = getTransactionLocalDate(); balanceDate = getTransactionDate();
final LocalDateInterval newInterval = LocalDateInterval.create(getTransactionLocalDate(), periodInterval.endDate()); final LocalDateInterval newInterval = LocalDateInterval.create(getTransactionDate(), periodInterval.endDate());
numberOfDays = newInterval.daysInPeriodInclusiveOfEndDate(); numberOfDays = newInterval.daysInPeriodInclusiveOfEndDate();
} }
@@ -603,7 +680,6 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
} }
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance, final LocalDate nextTransactionDate) { public EndOfDayBalance toEndOfDayBalance(final Money openingBalance, final LocalDate nextTransactionDate) {
final MonetaryCurrency currency = openingBalance.getCurrency(); final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy(); Money endOfDayBalance = openingBalance.copy();
if (isDeposit() || isDividendPayoutAndNotReversed()) { if (isDeposit() || isDividendPayoutAndNotReversed()) {
@@ -612,11 +688,11 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
endOfDayBalance = openingBalance.minus(getAmount(currency)); endOfDayBalance = openingBalance.minus(getAmount(currency));
} }
int numberOfDays = LocalDateInterval.create(getTransactionLocalDate(), nextTransactionDate).daysInPeriodInclusiveOfEndDate(); int numberOfDays = LocalDateInterval.create(getTransactionDate(), nextTransactionDate).daysInPeriodInclusiveOfEndDate();
if (!openingBalance.isEqualTo(endOfDayBalance) && numberOfDays > 1) { if (!openingBalance.isEqualTo(endOfDayBalance) && numberOfDays > 1) {
numberOfDays = numberOfDays - 1; numberOfDays = numberOfDays - 1;
} }
return EndOfDayBalance.from(getTransactionLocalDate(), openingBalance, endOfDayBalance, numberOfDays); return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance, numberOfDays);
} }
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) { public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
@@ -629,24 +705,23 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
if (openingBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) { if (openingBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) {
endOfDayBalance = openingBalance.minus(getAmount(currency)); endOfDayBalance = openingBalance.minus(getAmount(currency));
} else { } else {
endOfDayBalance = Money.of(currency, this.runningBalance); endOfDayBalance = getRunningBalance(currency);
} }
} }
return EndOfDayBalance.from(getTransactionLocalDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays); return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays);
} }
public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) { public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) {
final MonetaryCurrency currency = openingBalance.getCurrency(); final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy(); Money endOfDayBalance = openingBalance.copy();
int numberOfDaysOfBalance = this.balanceNumberOfDays; int numberOfDaysOfBalance = this.balanceNumberOfDays;
LocalDate balanceStartDate = getTransactionLocalDate(); LocalDate balanceStartDate = getTransactionDate();
LocalDate balanceEndDate = getEndOfBalanceLocalDate(); LocalDate balanceEndDate = getEndOfBalanceDate();
if (boundedBy.startDate().isAfter(balanceStartDate)) { if (DateUtils.isBefore(balanceStartDate, boundedBy.startDate())) {
balanceStartDate = boundedBy.startDate(); balanceStartDate = boundedBy.startDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -661,12 +736,12 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
if (endOfDayBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) { if (endOfDayBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) {
endOfDayBalance = endOfDayBalance.minus(getAmount(currency)); endOfDayBalance = endOfDayBalance.minus(getAmount(currency));
} else { } else {
endOfDayBalance = Money.of(currency, this.runningBalance); endOfDayBalance = getRunningBalance(currency);
} }
} }
} }
if (balanceEndDate.isAfter(boundedBy.endDate())) { if (DateUtils.isAfter(balanceEndDate, boundedBy.endDate())) {
balanceEndDate = boundedBy.endDate(); balanceEndDate = boundedBy.endDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate); final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate(); numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
@@ -676,16 +751,16 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
} }
public boolean isBalanceInExistencesForOneDayOrMore() { public boolean isBalanceInExistencesForOneDayOrMore() {
return this.balanceNumberOfDays != null && this.balanceNumberOfDays.intValue() >= 1; return this.balanceNumberOfDays != null && this.balanceNumberOfDays >= 1;
} }
public boolean fallsWithin(final LocalDateInterval periodInterval) { public boolean fallsWithin(final LocalDateInterval periodInterval) {
final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate()); final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionDate(), getEndOfBalanceDate());
return periodInterval.contains(balanceInterval); return periodInterval.contains(balanceInterval);
} }
public boolean spansAnyPortionOf(final LocalDateInterval periodInterval) { public boolean spansAnyPortionOf(final LocalDateInterval periodInterval) {
final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionLocalDate(), getEndOfBalanceLocalDate()); final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionDate(), getEndOfBalanceDate());
return balanceInterval.containsPortionOf(periodInterval); return balanceInterval.containsPortionOf(periodInterval);
} }
@@ -793,123 +868,22 @@ public final class SavingsAccountTransaction extends AbstractPersistableCustom {
return null; return null;
} }
public Set<SavingsAccountChargePaidBy> getSavingsAccountChargesPaid() {
return this.savingsAccountChargesPaid;
}
public void updateOverdraftAmount(BigDecimal overdraftAmount) {
this.overdraftAmount = overdraftAmount;
}
public Money getOverdraftAmount(final MonetaryCurrency currency) {
return Money.of(currency, this.overdraftAmount);
}
public boolean isPaymentForCurrentCharge(final SavingsAccountCharge savingsAccountCharge) { public boolean isPaymentForCurrentCharge(final SavingsAccountCharge savingsAccountCharge) {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy(); final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return chargePaidBy != null && chargePaidBy.getSavingsAccountCharge().equals(savingsAccountCharge); return chargePaidBy != null && chargePaidBy.getSavingsAccountCharge().equals(savingsAccountCharge);
} }
public BigDecimal getAmount() { @Override
return this.amount; public Optional<OffsetDateTime> getCreatedDate() {
} // #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getCreatedDate()
public List<SavingsAccountTransactionTaxDetails> getTaxDetails() { .orElse(createdDateToRemove == null ? null : createdDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
return this.taxDetails;
}
public List<Note> getNotes() {
return notes;
}
public void updateAmount(final Money amount) {
this.amount = amount.getAmount();
}
public Integer getTypeOf() {
return this.typeOf;
}
public SavingsAccountTransactionType getTransactionType() {
return SavingsAccountTransactionType.fromInt(this.typeOf);
}
public SavingsAccount getSavingsAccount() {
return this.savingsAccount;
}
public void setSavingsAccount(SavingsAccount savingsAccount) {
this.savingsAccount = savingsAccount;
}
public LocalDate getDateOf() {
return this.dateOf;
}
public String getRefNo() {
return this.refNo;
}
public PaymentDetail getPaymentDetail() {
return this.paymentDetail;
}
public void updateReleaseId(Long releaseId) {
this.releaseIdOfHoldAmountTransaction = releaseId;
}
public void updateReason(String reasonForBlock) {
this.reasonForBlock = reasonForBlock;
}
public Long getReleaseIdOfHoldAmountTransaction() {
return this.releaseIdOfHoldAmountTransaction;
} }
public boolean isAmountOnHoldNotReleased() { public boolean isAmountOnHoldNotReleased() {
return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null); return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null);
} }
public Long getOfficeId() {
return this.office.getId();
}
public LocalDate getBalanceEndDate() {
return this.balanceEndDate;
}
public BigDecimal getCumulativeBalance() {
return this.cumulativeBalance;
}
public Integer getBalanceNumberOfDays() {
return this.balanceNumberOfDays;
}
public Long getAppUserId() {
return this.appUser.getId();
}
public LocalDateTime getCreatedDate() {
return this.createdDate;
}
public LocalDate getSubmittedOnDate() {
return submittedOnDate;
}
public boolean getIsManualTransaction() {
return this.isManualTransaction;
}
public void setReversalTransaction(boolean reversalTransaction) {
this.reversalTransaction = reversalTransaction;
}
public boolean isReversalTransaction() {
return reversalTransaction;
}
public SavingsAccountTransactionDetailsForPostingPeriod toSavingsAccountTransactionDetailsForPostingPeriod(MonetaryCurrency currency, public SavingsAccountTransactionDetailsForPostingPeriod toSavingsAccountTransactionDetailsForPostingPeriod(MonetaryCurrency currency,
boolean isAllowOverDraft) { boolean isAllowOverDraft) {
return new SavingsAccountTransactionDetailsForPostingPeriod(getId(), this.dateOf, this.balanceEndDate, this.runningBalance, return new SavingsAccountTransactionDetailsForPostingPeriod(getId(), this.dateOf, this.balanceEndDate, this.runningBalance,
@@ -19,6 +19,7 @@
package org.apache.fineract.portfolio.savings.domain; package org.apache.fineract.portfolio.savings.domain;
import java.util.Comparator; import java.util.Comparator;
import org.apache.fineract.infrastructure.core.service.DateUtils;
/** /**
* Sort savings account transactions by transaction date and transaction type placing * Sort savings account transactions by transaction date and transaction type placing
@@ -27,18 +28,17 @@ public class SavingsAccountTransactionComparator implements Comparator<SavingsAc
@Override @Override
public int compare(final SavingsAccountTransaction o1, final SavingsAccountTransaction o2) { public int compare(final SavingsAccountTransaction o1, final SavingsAccountTransaction o2) {
int compareResult = 0; int result = DateUtils.compare(o1.getTransactionDate(), o2.getTransactionDate());
final int comparsion = o1.transactionLocalDate().compareTo(o2.transactionLocalDate()); if (result != 0) {
if (comparsion == 0) { return result;
compareResult = o1.getCreatedDate().compareTo(o2.getCreatedDate());
if (compareResult == 0 && o1.getId() != null && o2.getId() != null) {
compareResult = o1.getId().compareTo(o2.getId());
} else {
compareResult = comparsion;
}
} else {
compareResult = comparsion;
} }
return compareResult; result = DateUtils.compare(o1.getCreatedDateTime(), o2.getCreatedDateTime());
if (result != 0) {
return result;
}
if (o1.getId() != null && o2.getId() != null) {
return o1.getId().compareTo(o2.getId());
}
return 0;
} }
} }
@@ -19,26 +19,25 @@
package org.apache.fineract.portfolio.savings.domain; package org.apache.fineract.portfolio.savings.domain;
import java.util.Comparator; import java.util.Comparator;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData; import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
public class SavingsAccountTransactionDataComparator implements Comparator<SavingsAccountTransactionData> { public class SavingsAccountTransactionDataComparator implements Comparator<SavingsAccountTransactionData> {
@Override @Override
public int compare(final SavingsAccountTransactionData o1, final SavingsAccountTransactionData o2) { public int compare(final SavingsAccountTransactionData o1, final SavingsAccountTransactionData o2) {
int compareResult = 0; int result = DateUtils.compare(o1.getTransactionDate(), o2.getTransactionDate());
if (result != 0) {
final int comparsion = o1.getTransactionDate().compareTo(o2.getTransactionDate()); return result;
if (comparsion == 0) {
compareResult = o1.getSubmittedOnDate().compareTo(o2.getSubmittedOnDate());
if (compareResult == 0 && o1.getId() != null && o2.getId() != null) {
compareResult = o1.getId().compareTo(o2.getId());
} else {
compareResult = comparsion;
}
} else {
compareResult = comparsion;
} }
return compareResult; result = DateUtils.compare(o1.getSubmittedOnDate(), o2.getSubmittedOnDate());
if (result != 0) {
return result;
}
if (o1.getId() != null && o2.getId() != null) {
return o1.getId().compareTo(o2.getId());
}
return 0;
} }
} }
@@ -18,18 +18,24 @@
*/ */
package org.apache.fineract.portfolio.savings.domain; package org.apache.fineract.portfolio.savings.domain;
import static org.apache.fineract.infrastructure.core.service.DateUtils.getSystemZoneId;
import jakarta.persistence.Column; import jakarta.persistence.Column;
import jakarta.persistence.Entity; import jakarta.persistence.Entity;
import jakarta.persistence.JoinColumn; import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne; 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 java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
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
@Table(name = "m_savings_officer_assignment_history") @Table(name = "m_savings_officer_assignment_history")
public class SavingsOfficerAssignmentHistory extends AbstractAuditableCustom { public class SavingsOfficerAssignmentHistory extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne @ManyToOne
@JoinColumn(name = "account_id", nullable = false) @JoinColumn(name = "account_id", nullable = false)
@@ -45,10 +51,13 @@ public class SavingsOfficerAssignmentHistory extends AbstractAuditableCustom {
@Column(name = "end_date") @Column(name = "end_date")
private LocalDate endDate; private LocalDate endDate;
public static SavingsOfficerAssignmentHistory createNew(final SavingsAccount account, final Staff savingsOfficer, @Deprecated
final LocalDate assignmentDate) { @Column(name = "created_date")
return new SavingsOfficerAssignmentHistory(account, savingsOfficer, assignmentDate, null); private LocalDateTime createdDateToRemove;
}
@Deprecated
@Column(name = "lastmodified_date")
private LocalDateTime lastModifiedDateToRemove;
protected SavingsOfficerAssignmentHistory() { protected SavingsOfficerAssignmentHistory() {
// //
@@ -62,7 +71,12 @@ public class SavingsOfficerAssignmentHistory extends AbstractAuditableCustom {
this.endDate = endDate; this.endDate = endDate;
} }
public void updateSavingsOfficer(final Staff savingsOfficer) { public static SavingsOfficerAssignmentHistory createNew(final SavingsAccount account, final Staff savingsOfficer,
final LocalDate startDate) {
return new SavingsOfficerAssignmentHistory(account, savingsOfficer, startDate, null);
}
public void setSavingsOfficer(final Staff savingsOfficer) {
this.savingsOfficer = savingsOfficer; this.savingsOfficer = savingsOfficer;
} }
@@ -70,28 +84,28 @@ public class SavingsOfficerAssignmentHistory extends AbstractAuditableCustom {
return this.savingsOfficer.identifiedBy(staff); return this.savingsOfficer.identifiedBy(staff);
} }
public void updateStartDate(final LocalDate startDate) {
this.startDate = startDate;
}
public void updateEndDate(final LocalDate endDate) {
this.endDate = endDate;
}
public boolean matchesStartDateOf(final LocalDate matchingDate) {
return getStartDate().isEqual(matchingDate);
}
public LocalDate getStartDate() { public LocalDate getStartDate() {
return this.startDate; return this.startDate;
} }
public boolean hasStartDateBefore(final LocalDate matchingDate) { public void setStartDate(final LocalDate startDate) {
return matchingDate.isBefore(getStartDate()); this.startDate = startDate;
} }
public boolean isCurrentRecord() { public LocalDate getEndDate() {
return this.endDate == null; return this.endDate;
}
public void setEndDate(final LocalDate endDate) {
this.endDate = endDate;
}
public boolean matchesStartDateOf(final LocalDate matchingDate) {
return DateUtils.isEqual(matchingDate, getStartDate());
}
public boolean isBeforeStartDate(final LocalDate matchingDate) {
return DateUtils.isBefore(matchingDate, getStartDate());
} }
/** /**
@@ -100,12 +114,25 @@ public class SavingsOfficerAssignmentHistory extends AbstractAuditableCustom {
* @param compareDate * @param compareDate
* @return * @return
*/ */
public boolean isEndDateAfter(final LocalDate compareDate) { public boolean isBeforeEndDate(final LocalDate compareDate) {
return this.endDate != null && this.endDate.isAfter(compareDate); return DateUtils.isBefore(compareDate, this.endDate);
} }
public LocalDate getEndDate() { public boolean isCurrentRecord() {
return this.endDate; return this.endDate == null;
} }
@Override
public Optional<OffsetDateTime> getCreatedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getCreatedDate()
.orElse(createdDateToRemove == null ? null : createdDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
}
@Override
public Optional<OffsetDateTime> getLastModifiedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getLastModifiedDate()
.orElse(lastModifiedDateToRemove == null ? null : lastModifiedDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
}
} }
@@ -20,6 +20,7 @@ package org.apache.fineract.portfolio.savings.jobs.generaterdschedule;
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory; import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
import org.apache.fineract.infrastructure.jobs.service.JobName; import org.apache.fineract.infrastructure.jobs.service.JobName;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService; import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService;
import org.springframework.batch.core.Job; import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step; import org.springframework.batch.core.Step;
@@ -43,6 +44,8 @@ public class GenerateRdScheduleConfig {
private RoutingDataSourceServiceFactory dataSourceServiceFactory; private RoutingDataSourceServiceFactory dataSourceServiceFactory;
@Autowired @Autowired
private DepositAccountReadPlatformService depositAccountReadPlatformService; private DepositAccountReadPlatformService depositAccountReadPlatformService;
@Autowired
private PlatformSecurityContext securityContext;
@Bean @Bean
protected Step generateRdScheduleStep() { protected Step generateRdScheduleStep() {
@@ -58,6 +61,6 @@ public class GenerateRdScheduleConfig {
@Bean @Bean
public GenerateRdScheduleTasklet generateRdScheduleTasklet() { public GenerateRdScheduleTasklet generateRdScheduleTasklet() {
return new GenerateRdScheduleTasklet(dataSourceServiceFactory, depositAccountReadPlatformService); return new GenerateRdScheduleTasklet(dataSourceServiceFactory, depositAccountReadPlatformService, securityContext);
} }
} }
@@ -18,15 +18,23 @@
*/ */
package org.apache.fineract.portfolio.savings.jobs.generaterdschedule; package org.apache.fineract.portfolio.savings.jobs.generaterdschedule;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_BY_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_DATE_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_BY_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE_DB_FIELD;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.format.DateTimeFormatter; import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import java.util.Map; import java.util.Map;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.fineract.infrastructure.core.service.DateUtils; import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory; import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
import org.apache.fineract.infrastructure.security.service.PlatformSecurityContext;
import org.apache.fineract.portfolio.savings.DepositAccountUtils; import org.apache.fineract.portfolio.savings.DepositAccountUtils;
import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService; import org.apache.fineract.portfolio.savings.service.DepositAccountReadPlatformService;
import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.StepContribution;
@@ -41,19 +49,20 @@ public class GenerateRdScheduleTasklet implements Tasklet {
private final RoutingDataSourceServiceFactory dataSourceServiceFactory; private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
private final DepositAccountReadPlatformService depositAccountReadPlatformService; private final DepositAccountReadPlatformService depositAccountReadPlatformService;
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private final PlatformSecurityContext securityContext;
private final DateTimeFormatter formatterWithTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
@Override @Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceServiceFactory.determineDataSourceService().retrieveDataSource()); final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceServiceFactory.determineDataSourceService().retrieveDataSource());
final Collection<Map<String, Object>> scheduleDetails = depositAccountReadPlatformService.retriveDataForRDScheduleCreation(); final Collection<Map<String, Object>> scheduleDetails = depositAccountReadPlatformService.retriveDataForRDScheduleCreation();
String insertSql = "INSERT INTO m_mandatory_savings_schedule (savings_account_id, duedate, installment, deposit_amount, completed_derived, created_date, lastmodified_date) VALUES "; String insertSql = "INSERT INTO m_mandatory_savings_schedule (savings_account_id, duedate, installment, deposit_amount, completed_derived, "
StringBuilder sb = new StringBuilder(); + CREATED_DATE_DB_FIELD + ", " + CREATED_BY_DB_FIELD + ", " + LAST_MODIFIED_DATE_DB_FIELD + ", " + LAST_MODIFIED_BY_DB_FIELD
String currentDate = DateUtils.getLocalDateTimeOfTenant().format(DateUtils.DEFAULT_DATETIME_FORMATTER); + ") " + "VALUES (?, ?, ?, ?, ?, ?, ?)";
List<Object[]> params = new ArrayList<>();
Long userId = securityContext.authenticatedUser().getId();
int iterations = 0; int iterations = 0;
for (Map<String, Object> details : scheduleDetails) { for (Map<String, Object> details : scheduleDetails) {
Long count = (Long) details.get("futureInstallemts"); Long count = (Long) details.get("futureInstallments");
if (count == null) { if (count == null) {
count = 0L; count = 0L;
} }
@@ -66,34 +75,20 @@ public class GenerateRdScheduleTasklet implements Tasklet {
count++; count++;
installmentNumber++; installmentNumber++;
lastDepositDate = DepositAccountUtils.calculateNextDepositDate(lastDepositDate, recurrence); lastDepositDate = DepositAccountUtils.calculateNextDepositDate(lastDepositDate, recurrence);
OffsetDateTime auditTime = DateUtils.getAuditOffsetDateTime();
if (sb.length() > 0) { params.add(new Object[] { savingsId, lastDepositDate, installmentNumber, amount, false, auditTime, userId, auditTime,
sb.append(", "); userId });
}
sb.append("(");
sb.append(savingsId);
sb.append(",'");
sb.append(formatter.format(lastDepositDate));
sb.append("',");
sb.append(installmentNumber);
sb.append(",");
sb.append(amount);
sb.append(", b'0','");
sb.append(currentDate);
sb.append("','");
sb.append(currentDate);
sb.append("')");
iterations++; iterations++;
if (iterations > 200) { }
jdbcTemplate.update(insertSql + sb); // NOSONAR if (iterations > 200) {
sb = new StringBuilder(); jdbcTemplate.batchUpdate(insertSql, params);
} params.clear();
iterations = 0;
} }
} }
if (sb.length() > 0) { if (!params.isEmpty()) {
jdbcTemplate.update(insertSql + sb); // NOSONAR jdbcTemplate.batchUpdate(insertSql, params);
} }
return RepeatStatus.FINISHED; return RepeatStatus.FINISHED;
} }
@@ -18,6 +18,8 @@
*/ */
package org.apache.fineract.portfolio.savings.service; package org.apache.fineract.portfolio.savings.service;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_BY_DB_FIELD;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@@ -524,7 +526,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
sb.append(" select rd.savings_account_id savingsId, rd.mandatory_recommended_deposit_amount as amount,"); sb.append(" select rd.savings_account_id savingsId, rd.mandatory_recommended_deposit_amount as amount,");
sb.append(" mc.recurrence as recurrence ,"); sb.append(" mc.recurrence as recurrence ,");
sb.append(" max(ms.duedate) as dueDate , max(ms.installment) as installment,"); sb.append(" max(ms.duedate) as dueDate , max(ms.installment) as installment,");
sb.append(" count(ms.installment) as futureInstallemts"); sb.append(" count(ms.installment) as futureInstallments");
sb.append(" from m_deposit_account_term_and_preclosure dat "); sb.append(" from m_deposit_account_term_and_preclosure dat ");
sb.append(" inner join m_savings_account sa on sa.id = dat.savings_account_id and sa.status_enum = ?"); sb.append(" inner join m_savings_account sa on sa.id = dat.savings_account_id and sa.status_enum = ?");
sb.append(" inner join m_deposit_account_recurring_detail rd on rd.savings_account_id = dat.savings_account_id "); sb.append(" inner join m_deposit_account_recurring_detail rd on rd.savings_account_id = dat.savings_account_id ");
@@ -1163,7 +1165,7 @@ public class DepositAccountReadPlatformServiceImpl implements DepositAccountRead
sqlBuilder.append("left join m_account_transfer_transaction totran on totran.to_savings_transaction_id = tr.id "); sqlBuilder.append("left join m_account_transfer_transaction totran on totran.to_savings_transaction_id = tr.id ");
sqlBuilder.append("left join m_payment_detail pd on tr.payment_detail_id = pd.id "); sqlBuilder.append("left join m_payment_detail pd on tr.payment_detail_id = pd.id ");
sqlBuilder.append("left join m_payment_type pt on pd.payment_type_id = pt.id "); sqlBuilder.append("left join m_payment_type pt on pd.payment_type_id = pt.id ");
sqlBuilder.append("left join m_appuser au on au.id=tr.appuser_id "); sqlBuilder.append("left join m_appuser au on au.id = tr." + CREATED_BY_DB_FIELD);
this.schemaSql = sqlBuilder.toString(); this.schemaSql = sqlBuilder.toString();
} }
@@ -144,7 +144,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional @Transactional
@Override @Override
public CommandProcessingResult activateFDAccount(final Long savingsId, final JsonCommand command) { public CommandProcessingResult activateFDAccount(final Long savingsId, final JsonCommand command) {
final AppUser user = this.context.authenticatedUser(); final AppUser user = context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -160,7 +160,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final Map<String, Object> changes = account.activate(user, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> changes = account.activate(user, command);
Money activationChargeAmount = getActivationCharge(account); Money activationChargeAmount = getActivationCharge(account);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
@@ -172,13 +172,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
if (portfolioAccountData == null) { if (portfolioAccountData == null) {
final PaymentDetail paymentDetail = null; final PaymentDetail paymentDetail = null;
this.depositAccountDomainService.handleFDDeposit(account, fmt, account.getActivationLocalDate(), this.depositAccountDomainService.handleFDDeposit(account, fmt, account.getActivationDate(),
amountForDeposit.getAmount(), paymentDetail); amountForDeposit.getAmount(), paymentDetail);
} else { } else {
final SavingsAccount fromSavingsAccount = null; final SavingsAccount fromSavingsAccount = null;
boolean isRegularTransaction = false; boolean isRegularTransaction = false;
final boolean isExceptionForBalanceCheck = false; final boolean isExceptionForBalanceCheck = false;
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(account.getActivationLocalDate(), final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(account.getActivationDate(),
amountForDeposit.getAmount(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, amountForDeposit.getAmount(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS,
portfolioAccountData.getId(), account.getId(), "Account Transfer", locale, fmt, null, null, null, null, null, portfolioAccountData.getId(), account.getId(), "Account Transfer", locale, fmt, null, null, null, null, null,
AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, ExternalId.empty(), null, account, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, ExternalId.empty(), null, account,
@@ -188,9 +188,9 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final boolean isInterestTransfer = false; final boolean isInterestTransfer = false;
final LocalDate postInterestOnDate = null; final LocalDate postInterestOnDate = null;
if (activationChargeAmount.isGreaterThanZero()) { if (activationChargeAmount.isGreaterThanZero()) {
payActivationCharge(account, user); payActivationCharge(account);
} }
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate(), false)) { if (account.isBeforeLastPostingPeriod(account.getActivationDate(), false)) {
final LocalDate today = DateUtils.getBusinessLocalDate(); final LocalDate today = DateUtils.getBusinessLocalDate();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd,
financialYearBeginningMonth, postInterestOnDate, false); financialYearBeginningMonth, postInterestOnDate, false);
@@ -237,11 +237,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
return activationChargeAmount; return activationChargeAmount;
} }
private void payActivationCharge(final FixedDepositAccount account, AppUser user) { private void payActivationCharge(final FixedDepositAccount account) {
for (SavingsAccountCharge savingsAccountCharge : account.charges()) { for (SavingsAccountCharge savingsAccountCharge : account.charges()) {
if (savingsAccountCharge.isSavingsActivation()) { if (savingsAccountCharge.isSavingsActivation()) {
account.payCharge(savingsAccountCharge, savingsAccountCharge.getAmount(account.getCurrency()), account.payCharge(savingsAccountCharge, savingsAccountCharge.getAmount(account.getCurrency()), account.getActivationDate(),
account.getActivationLocalDate(), user, false, null); false, null);
} }
} }
} }
@@ -267,7 +267,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final Map<String, Object> changes = account.activate(user, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> changes = account.activate(user, command);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
@@ -277,12 +277,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService final PortfolioAccountData portfolioAccountData = this.accountAssociationsReadPlatformService
.retriveSavingsLinkedAssociation(savingsId); .retriveSavingsLinkedAssociation(savingsId);
if (portfolioAccountData == null) { if (portfolioAccountData == null) {
this.depositAccountDomainService.handleRDDeposit(account, fmt, account.getActivationLocalDate(), this.depositAccountDomainService.handleRDDeposit(account, fmt, account.getActivationDate(),
amountForDeposit.getAmount(), null, isRegularTransaction); amountForDeposit.getAmount(), null, isRegularTransaction);
} else { } else {
final boolean isExceptionForBalanceCheck = false; final boolean isExceptionForBalanceCheck = false;
final SavingsAccount fromSavingsAccount = null; final SavingsAccount fromSavingsAccount = null;
final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(account.getActivationLocalDate(), final AccountTransferDTO accountTransferDTO = new AccountTransferDTO(account.getActivationDate(),
amountForDeposit.getAmount(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS, amountForDeposit.getAmount(), PortfolioAccountType.SAVINGS, PortfolioAccountType.SAVINGS,
portfolioAccountData.getId(), account.getId(), "Account Transfer", locale, fmt, null, null, null, null, null, portfolioAccountData.getId(), account.getId(), "Account Transfer", locale, fmt, null, null, null, null, null,
AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, ExternalId.empty(), null, account, AccountTransferType.ACCOUNT_TRANSFER.getValue(), null, null, ExternalId.empty(), null, account,
@@ -314,7 +314,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
account.updateOverduePayments(overdueUptoDate); account.updateOverduePayments(overdueUptoDate);
final boolean isInterestTransfer = false; final boolean isInterestTransfer = false;
final LocalDate postInterestOnDate = null; final LocalDate postInterestOnDate = null;
if (account.isBeforeLastPostingPeriod(account.getActivationLocalDate(), false)) { if (account.isBeforeLastPostingPeriod(account.getActivationDate(), false)) {
final LocalDate today = DateUtils.getBusinessLocalDate(); final LocalDate today = DateUtils.getBusinessLocalDate();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, false, postReversals); postInterestOnDate, false, postReversals);
@@ -571,7 +571,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
final boolean postReversals = false; final boolean postReversals = false;
if (savingsAccountTransaction.isPostInterestCalculationRequired() if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { && account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), false)) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, false, postReversals); postInterestOnDate, false, postReversals);
} else { } else {
@@ -620,7 +620,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override @Override
public CommandProcessingResult adjustRDTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) { public CommandProcessingResult adjustRDTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -671,11 +671,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
UUID refNo = UUID.randomUUID(); UUID refNo = UUID.randomUUID();
if (savingsAccountTransaction.isDeposit()) { if (savingsAccountTransaction.isDeposit()) {
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, savingsAccountTransaction.getCreatedDate(), user, accountType); paymentDetail, null, accountType);
transaction = account.deposit(transactionDTO, false, relaxingDaysConfigForPivotDate, refNo.toString()); transaction = account.deposit(transactionDTO, false, relaxingDaysConfigForPivotDate, refNo.toString());
} else { } else {
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, savingsAccountTransaction.getCreatedDate(), user, accountType); paymentDetail, null, accountType);
transaction = account.withdraw(transactionDTO, true, false, relaxingDaysConfigForPivotDate, refNo.toString()); transaction = account.withdraw(transactionDTO, true, false, relaxingDaysConfigForPivotDate, refNo.toString());
} }
final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction); final Long newtransactionId = saveTransactionToGenerateTransactionId(transaction);
@@ -683,7 +683,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final LocalDate postInterestOnDate = null; final LocalDate postInterestOnDate = null;
final boolean postReversals = false; final boolean postReversals = false;
if (account.isBeforeLastPostingPeriod(transactionDate, false) if (account.isBeforeLastPostingPeriod(transactionDate, false)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { || account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), false)) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, false, postReversals); postInterestOnDate, false, postReversals);
} else { } else {
@@ -757,8 +757,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
DepositAccountType.FIXED_DEPOSIT); DepositAccountType.FIXED_DEPOSIT);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
this.depositAccountDomainService.handleFDAccountClosure(account, paymentDetail, user, command, DateUtils.getBusinessLocalDate(), this.depositAccountDomainService.handleFDAccountClosure(account, paymentDetail, user, command, changes);
changes);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
@@ -791,8 +790,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
DepositAccountType.RECURRING_DEPOSIT); DepositAccountType.RECURRING_DEPOSIT);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
this.depositAccountDomainService.handleRDAccountClosure(account, paymentDetail, user, command, DateUtils.getBusinessLocalDate(), this.depositAccountDomainService.handleRDAccountClosure(account, paymentDetail, user, command, changes);
changes);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
@@ -825,8 +823,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
DepositAccountType.FIXED_DEPOSIT); DepositAccountType.FIXED_DEPOSIT);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
this.depositAccountDomainService.handleFDAccountPreMatureClosure(account, paymentDetail, user, command, this.depositAccountDomainService.handleFDAccountPreMatureClosure(account, paymentDetail, user, command, changes);
DateUtils.getBusinessLocalDate(), changes);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
@@ -868,8 +865,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
} }
} }
this.depositAccountDomainService.handleRDAccountPreMatureClosure(account, paymentDetail, user, command, this.depositAccountDomainService.handleRDAccountPreMatureClosure(account, paymentDetail, user, command, changes);
DateUtils.getBusinessLocalDate(), changes);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
if (StringUtils.isNotBlank(noteText)) { if (StringUtils.isNotBlank(noteText)) {
@@ -892,7 +888,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override @Override
public SavingsAccountTransaction initiateSavingsTransfer(final Long accountId, final LocalDate transferDate, public SavingsAccountTransaction initiateSavingsTransfer(final Long accountId, final LocalDate transferDate,
final DepositAccountType depositAccountType) { final DepositAccountType depositAccountType) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
@@ -904,7 +900,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransaction newTransferTransaction = SavingsAccountTransaction.initiateTransfer(savingsAccount, final SavingsAccountTransaction newTransferTransaction = SavingsAccountTransaction.initiateTransfer(savingsAccount,
savingsAccount.office(), transferDate, user); savingsAccount.office(), transferDate);
savingsAccount.addTransaction(newTransferTransaction); savingsAccount.addTransaction(newTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
@@ -924,7 +920,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override @Override
public SavingsAccountTransaction withdrawSavingsTransfer(final Long accountId, final LocalDate transferDate, public SavingsAccountTransaction withdrawSavingsTransfer(final Long accountId, final LocalDate transferDate,
final DepositAccountType depositAccountType) { final DepositAccountType depositAccountType) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -937,7 +933,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransaction withdrawtransferTransaction = SavingsAccountTransaction.withdrawTransfer(savingsAccount, final SavingsAccountTransaction withdrawtransferTransaction = SavingsAccountTransaction.withdrawTransfer(savingsAccount,
savingsAccount.office(), transferDate, user); savingsAccount.office(), transferDate);
savingsAccount.addTransaction(withdrawtransferTransaction); savingsAccount.addTransaction(withdrawtransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final boolean postReversals = false; final boolean postReversals = false;
@@ -965,7 +961,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override @Override
public SavingsAccountTransaction acceptSavingsTransfer(final Long accountId, final LocalDate transferDate, public SavingsAccountTransaction acceptSavingsTransfer(final Long accountId, final LocalDate transferDate,
final Office acceptedInOffice, final Staff fieldOfficer, final DepositAccountType depositAccountType) { final Office acceptedInOffice, final Staff fieldOfficer, final DepositAccountType depositAccountType) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -978,7 +974,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransaction acceptTransferTransaction = SavingsAccountTransaction.approveTransfer(savingsAccount, final SavingsAccountTransaction acceptTransferTransaction = SavingsAccountTransaction.approveTransfer(savingsAccount,
acceptedInOffice, transferDate, user); acceptedInOffice, transferDate);
savingsAccount.addTransaction(acceptTransferTransaction); savingsAccount.addTransaction(acceptTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
if (fieldOfficer != null) { if (fieldOfficer != null) {
@@ -1024,11 +1020,11 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewFromJson(savingsAccount, chargeDefinition, command); final SavingsAccountCharge savingsAccountCharge = SavingsAccountCharge.createNewFromJson(savingsAccount, chargeDefinition, command);
if (savingsAccountCharge.getDueLocalDate() != null) { if (savingsAccountCharge.getDueDate() != null) {
// transaction date should not be on a holiday or non working day // transaction date should not be on a holiday or non working day
if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled()
&& this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { && this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -1036,8 +1032,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
} }
if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled()
&& !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { && !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -1080,14 +1076,14 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Map<String, Object> changes = savingsAccountCharge.update(command); final Map<String, Object> changes = savingsAccountCharge.update(command);
if (savingsAccountCharge.getDueLocalDate() != null) { if (savingsAccountCharge.getDueDate() != null) {
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);
// transaction date should not be on a holiday or non working day // transaction date should not be on a holiday or non working day
if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled()
&& this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { && this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -1095,8 +1091,8 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
} }
if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled()
&& !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { && !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -1120,7 +1116,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override @Override
public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId, public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId,
@SuppressWarnings("unused") final DepositAccountType depositAccountType) { @SuppressWarnings("unused") final DepositAccountType depositAccountType) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -1137,12 +1133,12 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.waiveCharge(savingsAccountChargeId, user, false); account.waiveCharge(savingsAccountChargeId, false);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null; LocalDate postInterestOnDate = null;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
final boolean postReversals = false; final boolean postReversals = false;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate(), false)) { if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueDate(), false)) {
final LocalDate today = DateUtils.getBusinessLocalDate(); final LocalDate today = DateUtils.getBusinessLocalDate();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, false, postReversals); postInterestOnDate, false, postReversals);
@@ -1249,13 +1245,13 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Override @Override
public void applyChargeDue(final Long savingsAccountChargeId, final Long accountId, public void applyChargeDue(final Long savingsAccountChargeId, final Long accountId,
@SuppressWarnings("unused") final DepositAccountType depositAccountType) { @SuppressWarnings("unused") final DepositAccountType depositAccountType) {
// always use current date as transaction date for batch job
final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId); .findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
// always use current date as transaction date for batch job
final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MM yyyy"); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MM yyyy");
while (transactionDate.isAfter(savingsAccountCharge.getDueLocalDate())) {
while (DateUtils.isBefore(savingsAccountCharge.getDueDate(), transactionDate)) {
payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt); payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt);
} }
} }
@@ -1263,7 +1259,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
@Transactional @Transactional
private void payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final BigDecimal amountPaid, private void payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, final BigDecimal amountPaid,
final DateTimeFormatter formatter) { final DateTimeFormatter formatter) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -1275,7 +1271,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
final Set<Long> existingTransactionIds = new HashSet<>(); final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user, false, null); account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, false, null);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null; LocalDate postInterestOnDate = null;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
@@ -1323,10 +1319,10 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
if (fdAccount.isMatured() && (fdAccount.isReinvestOnClosure() || fdAccount.isTransferToSavingsOnClosure())) { if (fdAccount.isMatured() && (fdAccount.isReinvestOnClosure() || fdAccount.isTransferToSavingsOnClosure())) {
DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd"); DateTimeFormatter fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd");
Map<String, Object> changes = new HashMap<>(); Map<String, Object> changes = new HashMap<>();
AppUser user = context.authenticatedUser(); final AppUser user = context.authenticatedUser();
Long toSavingsId = fdAccount.getTransferToSavingsAccountId(); Long toSavingsId = fdAccount.getTransferToSavingsAccountId();
this.depositAccountDomainService.handleFDAccountMaturityClosure(fdAccount, null, user, fdAccount.maturityDate(), fmt, this.depositAccountDomainService.handleFDAccountMaturityClosure(fdAccount, null, user, fmt, fdAccount.maturityDate(),
fdAccount.maturityDate(), fdAccount.getOnAccountClosureId(), toSavingsId, "Apply maturity instructions", changes); fdAccount.getOnAccountClosureId(), toSavingsId, "Apply maturity instructions", changes);
if (changes.get("reinvestedDepositId") != null) { if (changes.get("reinvestedDepositId") != null) {
Long reinvestedDepositId = (Long) changes.get("reinvestedDepositId"); Long reinvestedDepositId = (Long) changes.get("reinvestedDepositId");
@@ -1335,7 +1331,7 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
.assembleFrom(reinvestedDepositId, DepositAccountType.FIXED_DEPOSIT); .assembleFrom(reinvestedDepositId, DepositAccountType.FIXED_DEPOSIT);
Money activationChargeAmount = getActivationCharge(reinvestAccount); Money activationChargeAmount = getActivationCharge(reinvestAccount);
if (activationChargeAmount.isGreaterThanZero()) { if (activationChargeAmount.isGreaterThanZero()) {
payActivationCharge(reinvestAccount, user); payActivationCharge(reinvestAccount);
amountForDeposit = amountForDeposit.plus(activationChargeAmount); amountForDeposit = amountForDeposit.plus(activationChargeAmount);
} }
this.depositAccountDomainService.handleFDDeposit(reinvestAccount, fmt, fdAccount.maturityDate(), this.depositAccountDomainService.handleFDDeposit(reinvestAccount, fmt, fdAccount.maturityDate(),
@@ -1393,13 +1389,4 @@ public class DepositAccountWritePlatformServiceJpaRepositoryImpl implements Depo
isRegularTransaction); isRegularTransaction);
} }
private AppUser getAppUserIfPresent() {
AppUser user = null;
if (this.context != null) {
user = this.context.getAuthenticatedUserIfPresent();
}
return user;
}
} }
@@ -47,7 +47,6 @@ import org.apache.fineract.infrastructure.core.exception.GeneralPlatformDomainRu
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
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.event.business.domain.deposit.FixedDepositAccountCreateBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.deposit.FixedDepositAccountCreateBusinessEvent;
import org.apache.fineract.infrastructure.event.business.domain.deposit.RecurringDepositAccountCreateBusinessEvent; import org.apache.fineract.infrastructure.event.business.domain.deposit.RecurringDepositAccountCreateBusinessEvent;
import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService; import org.apache.fineract.infrastructure.event.business.service.BusinessEventNotifierService;
@@ -384,7 +383,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
DepositAccountType.FIXED_DEPOSIT); DepositAccountType.FIXED_DEPOSIT);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
account.modifyApplication(command, changes); account.modifyApplication(command, changes);
account.validateNewApplicationState(DateUtils.getBusinessLocalDate(), DepositAccountType.FIXED_DEPOSIT.resourceName()); account.validateNewApplicationState(DepositAccountType.FIXED_DEPOSIT.resourceName());
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
updateFDAndRDCommonChanges(changes, command, account); updateFDAndRDCommonChanges(changes, command, account);
@@ -474,7 +473,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
DepositAccountType.RECURRING_DEPOSIT); DepositAccountType.RECURRING_DEPOSIT);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
account.modifyApplication(command, changes); account.modifyApplication(command, changes);
account.validateNewApplicationState(DateUtils.getBusinessLocalDate(), DepositAccountType.RECURRING_DEPOSIT.resourceName()); account.validateNewApplicationState(DepositAccountType.RECURRING_DEPOSIT.resourceName());
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
updateFDAndRDCommonChanges(changes, command, account); updateFDAndRDCommonChanges(changes, command, account);
@@ -638,7 +637,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType);
checkClientOrGroupActive(savingsAccount); checkClientOrGroupActive(savingsAccount);
final Map<String, Object> changes = savingsAccount.approveApplication(currentUser, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> changes = savingsAccount.approveApplication(currentUser, command);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount); this.savingAccountRepository.save(savingsAccount);
@@ -708,7 +707,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType);
checkClientOrGroupActive(savingsAccount); checkClientOrGroupActive(savingsAccount);
final Map<String, Object> changes = savingsAccount.rejectApplication(currentUser, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> changes = savingsAccount.rejectApplication(currentUser, command);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount); this.savingAccountRepository.save(savingsAccount);
@@ -742,8 +741,7 @@ public class DepositApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType); final SavingsAccount savingsAccount = this.depositAccountAssembler.assembleFrom(savingsId, depositAccountType);
checkClientOrGroupActive(savingsAccount); checkClientOrGroupActive(savingsAccount);
final Map<String, Object> changes = savingsAccount.applicantWithdrawsFromApplication(currentUser, command, final Map<String, Object> changes = savingsAccount.applicantWithdrawsFromApplication(currentUser, command);
DateUtils.getBusinessLocalDate());
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount); this.savingAccountRepository.save(savingsAccount);
@@ -94,7 +94,6 @@ public class SavingsAccountChargeReadPlatformServiceImpl implements SavingsAccou
@Override @Override
public SavingsAccountChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException { public SavingsAccountChargeData mapRow(final ResultSet rs, @SuppressWarnings("unused") final int rowNum) throws SQLException {
final Long id = rs.getLong("id"); final Long id = rs.getLong("id");
final Long chargeId = rs.getLong("chargeId"); final Long chargeId = rs.getLong("chargeId");
final Long accountId = rs.getLong("accountId"); final Long accountId = rs.getLong("accountId");
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.portfolio.savings.service; package org.apache.fineract.portfolio.savings.service;
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;
@@ -30,6 +31,7 @@ import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval; import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
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.savings.DepositAccountType; import org.apache.fineract.portfolio.savings.DepositAccountType;
@@ -62,7 +64,6 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
public SavingsAccountData postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer, public SavingsAccountData postInterest(final MathContext mc, final LocalDate interestPostingUpToDate, final boolean isInterestTransfer,
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
final LocalDate postInterestOnDate, final boolean backdatedTxnsAllowedTill, final SavingsAccountData savingsAccountData) { final LocalDate postInterestOnDate, final boolean backdatedTxnsAllowedTill, final SavingsAccountData savingsAccountData) {
Money interestPostedToDate = Money.zero(savingsAccountData.getCurrency()); Money interestPostedToDate = Money.zero(savingsAccountData.getCurrency());
LocalDate startInterestDate = getStartInterestCalculationDate(savingsAccountData); LocalDate startInterestDate = getStartInterestCalculationDate(savingsAccountData);
@@ -84,13 +85,11 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
withholdTransactions.addAll(findWithHoldSavingsTransactionsWithPivotConfig(savingsAccountData)); withholdTransactions.addAll(findWithHoldSavingsTransactionsWithPivotConfig(savingsAccountData));
for (final PostingPeriod interestPostingPeriod : postingPeriods) { for (final PostingPeriod interestPostingPeriod : postingPeriods) {
final LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction(); final LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction();
final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned(); final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();
if (!interestPostingTransactionDate.isAfter(interestPostingUpToDate)) { if (!DateUtils.isAfter(interestPostingTransactionDate, interestPostingUpToDate)) {
interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod); interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod);
final SavingsAccountTransactionData postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate, final SavingsAccountTransactionData postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate,
savingsAccountData); savingsAccountData);
@@ -245,7 +244,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
Money periodStartingBalance; Money periodStartingBalance;
if (savingsAccountData.getStartInterestCalculationDate() != null if (savingsAccountData.getStartInterestCalculationDate() != null
&& !savingsAccountData.getStartInterestCalculationDate().equals(savingsAccountData.getActivationLocalDate())) { && !savingsAccountData.getStartInterestCalculationDate().equals(savingsAccountData.getActivationLocalDate())) {
final SavingsAccountTransactionData transaction = retrieveLastTransactions(savingsAccountData); final SavingsAccountTransactionData transaction = retrieveLastTransaction(savingsAccountData);
if (transaction == null) { if (transaction == null) {
periodStartingBalance = Money.zero(savingsAccountData.getCurrency()); periodStartingBalance = Money.zero(savingsAccountData.getCurrency());
@@ -329,13 +328,8 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
} }
protected LocalDate getLockedInUntilLocalDate(final SavingsAccountData savingsAccount) { protected LocalDate getLockedInUntilLocalDate(final SavingsAccountData savingsAccount) {
LocalDate lockedInUntilLocalDate = null; LocalDate lockedInUntilLocalDate = savingsAccount.getLockedInUntilDate();
if (savingsAccount.getLockedInUntilDate() != null) { return lockedInUntilLocalDate == null ? savingsAccount.getActivationLocalDate() : lockedInUntilLocalDate;
lockedInUntilLocalDate = savingsAccount.getActivationLocalDate();
// lockedInUntilLocalDate = LocalDate.ofInstant(this.lockedInUntilDate.toInstant(),
// DateUtils.getDateTimeZoneOfTenant());
}
return lockedInUntilLocalDate;
} }
private BigDecimal minBalanceForInterestCalculation(final SavingsAccountData savingsAccountData) { private BigDecimal minBalanceForInterestCalculation(final SavingsAccountData savingsAccountData) {
@@ -358,20 +352,17 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
return savingsAccountData.getSavingsAccountTransactionData(); return savingsAccountData.getSavingsAccountTransactionData();
} }
private SavingsAccountTransactionData retrieveLastTransactions(final SavingsAccountData savingsAccountData) { private SavingsAccountTransactionData retrieveLastTransaction(@NotNull SavingsAccountData savingsAccountData) {
if (savingsAccountData.getSavingsAccountTransactionData() != null List<SavingsAccountTransactionData> transactions = savingsAccountData.getSavingsAccountTransactionData();
&& savingsAccountData.getSavingsAccountTransactionData().size() == 1) { if (transactions == null || transactions.isEmpty()) {
return savingsAccountData.getSavingsAccountTransactionData().get(0); return savingsAccountData.getLastSavingsAccountTransaction(); // what is this?
} }
final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>(); if (transactions.size() == 1) {
listOfTransactionsSorted.addAll(savingsAccountData.getSavingsAccountTransactionData()); return transactions.get(0);
if (!listOfTransactionsSorted.isEmpty()) {
final SavingsAccountTransactionDataComparator transactionComparator = new SavingsAccountTransactionDataComparator();
Collections.sort(listOfTransactionsSorted, transactionComparator);
} else {
listOfTransactionsSorted.add(savingsAccountData.getLastSavingsAccountTransaction());
} }
return listOfTransactionsSorted.get(0); final List<SavingsAccountTransactionData> listOfTransactionsSorted = new ArrayList<>(transactions);
listOfTransactionsSorted.sort(new SavingsAccountTransactionDataComparator());
return listOfTransactionsSorted.get(0); // this is the first transaction, not the last
} }
public LocalDate getStartInterestCalculationDate(final SavingsAccountData savingsAccountData) { public LocalDate getStartInterestCalculationDate(final SavingsAccountData savingsAccountData) {
@@ -18,6 +18,9 @@
*/ */
package org.apache.fineract.portfolio.savings.service; package org.apache.fineract.portfolio.savings.service;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_BY_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_DATE_DB_FIELD;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
@@ -278,7 +281,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
} }
sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date <= ? ) "; sql = sql + " and (sa.interest_posted_till_date is null or sa.interest_posted_till_date <= ? ) ";
sql = sql + " order by sa.id, tr.transaction_date, tr.created_date, tr.id"; // #audit backward compatibility
sql = sql + " order by sa.id, tr.transaction_date, tr." + CREATED_DATE_DB_FIELD + ", tr.created_date, tr.id";
List<SavingsAccountData> savingsAccountDataList = this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting, // NOSONAR List<SavingsAccountData> savingsAccountDataList = this.jdbcTemplate.query(sql, this.savingAccountMapperForInterestPosting, // NOSONAR
new Object[] { maxSavingsId, status, pageSize, yesterday }); new Object[] { maxSavingsId, status, pageSize, yesterday });
@@ -459,19 +463,19 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
Integer daysToDormancy = null; Integer daysToDormancy = null;
Integer daysToEscheat = null; Integer daysToEscheat = null;
LocalDate localTenantDate = DateUtils.getBusinessLocalDate(); LocalDate currentDate = DateUtils.getBusinessLocalDate();
if (isDormancyTrackingActive && statusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())) { if (isDormancyTrackingActive && statusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())) {
if (subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()) { if (subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()) {
daysToEscheat = Math.toIntExact( daysToEscheat = Math
ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToEscheat))); .toIntExact(ChronoUnit.DAYS.between(currentDate, lastActiveTransactionDate.plusDays(numDaysToEscheat)));
} }
if (subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()) { if (subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()) {
daysToDormancy = Math.toIntExact( daysToDormancy = Math.toIntExact(
ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToDormancy))); ChronoUnit.DAYS.between(currentDate, lastActiveTransactionDate.plusDays(numDaysToDormancy)));
} }
if (subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()) { if (subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()) {
daysToInactive = Math.toIntExact( daysToInactive = Math.toIntExact(
ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToInactive))); ChronoUnit.DAYS.between(currentDate, lastActiveTransactionDate.plusDays(numDaysToInactive)));
} }
} }
final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate"); final LocalDate approvedOnDate = JdbcSupport.getLocalDate(rs, "approvedOnDate");
@@ -896,19 +900,19 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
Integer daysToDormancy = null; Integer daysToDormancy = null;
Integer daysToEscheat = null; Integer daysToEscheat = null;
LocalDate localTenantDate = DateUtils.getBusinessLocalDate(); LocalDate currentDate = DateUtils.getBusinessLocalDate();
if (isDormancyTrackingActive && statusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())) { if (isDormancyTrackingActive && statusEnum.equals(SavingsAccountStatusType.ACTIVE.getValue())) {
if (subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()) { if (subStatusEnum < SavingsAccountSubStatusEnum.ESCHEAT.getValue()) {
daysToEscheat = Math daysToEscheat = Math
.toIntExact(ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToEscheat))); .toIntExact(ChronoUnit.DAYS.between(currentDate, lastActiveTransactionDate.plusDays(numDaysToEscheat)));
} }
if (subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()) { if (subStatusEnum < SavingsAccountSubStatusEnum.DORMANT.getValue()) {
daysToDormancy = Math daysToDormancy = Math
.toIntExact(ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToDormancy))); .toIntExact(ChronoUnit.DAYS.between(currentDate, lastActiveTransactionDate.plusDays(numDaysToDormancy)));
} }
if (subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()) { if (subStatusEnum < SavingsAccountSubStatusEnum.INACTIVE.getValue()) {
daysToInactive = Math daysToInactive = Math
.toIntExact(ChronoUnit.DAYS.between(localTenantDate, lastActiveTransactionDate.plusDays(numDaysToInactive))); .toIntExact(ChronoUnit.DAYS.between(currentDate, lastActiveTransactionDate.plusDays(numDaysToInactive)));
} }
} }
@@ -1266,10 +1270,10 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
@Override @Override
public Collection<SavingsAccountTransactionData> retrieveAllTransactions(final Long savingsId, DepositAccountType depositAccountType) { public Collection<SavingsAccountTransactionData> retrieveAllTransactions(final Long savingsId, DepositAccountType depositAccountType) {
// #audit backward compatibility
final String sql = "select " + this.transactionsMapper.schema() final String sql = "select " + this.transactionsMapper.schema()
+ " where sa.id = ? and sa.deposit_type_enum = ? order by tr.transaction_date DESC, tr.created_date DESC, tr.id DESC"; + " where sa.id = ? and sa.deposit_type_enum = ? order by tr.transaction_date DESC," + " tr." + CREATED_DATE_DB_FIELD
+ " DESC, tr.created_date DESC, tr.id DESC";
return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId, depositAccountType.getValue() }); // NOSONAR return this.jdbcTemplate.query(sql, this.transactionsMapper, new Object[] { savingsId, depositAccountType.getValue() }); // NOSONAR
} }
@@ -1352,8 +1356,8 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
+ "left join m_account_transfer_transaction fromtran on fromtran.from_savings_transaction_id = tr.id " + "left join m_account_transfer_transaction fromtran on fromtran.from_savings_transaction_id = tr.id "
+ "left join m_account_transfer_transaction totran on totran.to_savings_transaction_id = tr.id " + "left join m_account_transfer_transaction totran on totran.to_savings_transaction_id = tr.id "
+ "left join m_payment_detail pd on tr.payment_detail_id = pd.id " + "left join m_payment_detail pd on tr.payment_detail_id = pd.id "
+ "left join m_payment_type pt on pd.payment_type_id = pt.id left join m_appuser au on au.id=tr.appuser_id " + "left join m_payment_type pt on pd.payment_type_id = pt.id left join m_appuser au on au.id= tr." + CREATED_BY_DB_FIELD
+ "left join m_note nt ON nt.savings_account_transaction_id=tr.id "; + " left join m_note nt ON nt.savings_account_transaction_id=tr.id ";
} }
public String schema() { public String schema() {
@@ -207,7 +207,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
final Map<String, Object> changes = account.activate(user, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> changes = account.activate(user, command);
entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(), entityDatatableChecksWritePlatformService.runTheCheckForProduct(savingsId, EntityTables.SAVINGS.getName(),
StatusEnum.ACTIVATE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(), account.productId()); StatusEnum.ACTIVATE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(), account.productId());
@@ -235,7 +235,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public void processPostActiveActions(final SavingsAccount account, final DateTimeFormatter fmt, final Set<Long> existingTransactionIds, public void processPostActiveActions(final SavingsAccount account, final DateTimeFormatter fmt, final Set<Long> existingTransactionIds,
final Set<Long> existingReversedTransactionIds) { final Set<Long> existingReversedTransactionIds) {
AppUser user = getAppUserIfPresent(); getAppUserIfPresent();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -245,13 +245,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
boolean isRegularTransaction = false; boolean isRegularTransaction = false;
if (amountForDeposit.isGreaterThanZero()) { if (amountForDeposit.isGreaterThanZero()) {
boolean isAccountTransfer = false; boolean isAccountTransfer = false;
this.savingsAccountDomainService.handleDeposit(account, fmt, account.getActivationLocalDate(), amountForDeposit.getAmount(), this.savingsAccountDomainService.handleDeposit(account, fmt, account.getActivationDate(), amountForDeposit.getAmount(), null,
null, isAccountTransfer, isRegularTransaction, false); isAccountTransfer, isRegularTransaction, false);
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
} }
account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, user); account.processAccountUponActivation(isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null; List<DepositAccountOnHoldTransaction> depositAccountOnHoldTransactions = null;
if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) > 0) { if (account.getOnHoldFunds().compareTo(BigDecimal.ZERO) > 0) {
depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository depositAccountOnHoldTransactions = this.depositAccountOnHoldTransactionRepository
@@ -417,16 +417,16 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional @Transactional
@Override @Override
public CommandProcessingResult applyAnnualFee(final Long savingsAccountChargeId, final Long accountId) { public CommandProcessingResult applyAnnualFee(final Long savingsAccountChargeId, final Long accountId) {
AppUser user = getAppUserIfPresent(); getAppUserIfPresent();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId); .findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
final LocalDate todaysDate = DateUtils.getBusinessLocalDate(); final LocalDate currentDate = DateUtils.getBusinessLocalDate();
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MM yyyy").withZone(DateUtils.getDateTimeZoneOfTenant()); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MM yyyy").withZone(DateUtils.getDateTimeZoneOfTenant());
while (todaysDate.isAfter(savingsAccountCharge.getDueLocalDate())) { while (DateUtils.isBefore(savingsAccountCharge.getDueDate(), currentDate)) {
this.payCharge(savingsAccountCharge, savingsAccountCharge.getDueLocalDate(), savingsAccountCharge.amount(), fmt, user, false); this.payCharge(savingsAccountCharge, savingsAccountCharge.getDueDate(), savingsAccountCharge.amount(), fmt, false);
} }
return new CommandProcessingResultBuilder() // return new CommandProcessingResultBuilder() //
@@ -681,7 +681,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId); throw new SavingsAccountTransactionNotFoundException(savingsId, transactionId);
} }
this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(savingsAccountTransaction.getTransactionLocalDate(), this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(savingsAccountTransaction.getTransactionDate(),
account); account);
if (!allowAccountTransferModification if (!allowAccountTransferModification
@@ -718,7 +718,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
boolean postReversals = false; boolean postReversals = false;
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
if (savingsAccountTransaction.isPostInterestCalculationRequired() if (savingsAccountTransaction.isPostInterestCalculationRequired()
&& account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { && account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), false)) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, false, postReversals); postInterestOnDate, false, postReversals);
} else { } else {
@@ -746,7 +746,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public CommandProcessingResult adjustSavingsTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) { public CommandProcessingResult adjustSavingsTransaction(final Long savingsId, final Long transactionId, final JsonCommand command) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -808,7 +808,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
Integer accountType = null; Integer accountType = null;
final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount, final SavingsAccountTransactionDTO transactionDTO = new SavingsAccountTransactionDTO(fmt, transactionDate, transactionAmount,
paymentDetail, savingsAccountTransaction.getCreatedDate(), user, accountType); paymentDetail, null, accountType);
UUID refNo = UUID.randomUUID(); UUID refNo = UUID.randomUUID();
if (savingsAccountTransaction.isDeposit()) { if (savingsAccountTransaction.isDeposit()) {
transaction = account.deposit(transactionDTO, false, relaxingDaysConfigForPivotDate, refNo.toString()); transaction = account.deposit(transactionDTO, false, relaxingDaysConfigForPivotDate, refNo.toString());
@@ -819,7 +819,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final LocalDate postInterestOnDate = null; final LocalDate postInterestOnDate = null;
boolean postReversals = false; boolean postReversals = false;
if (account.isBeforeLastPostingPeriod(transactionDate, false) if (account.isBeforeLastPostingPeriod(transactionDate, false)
|| account.isBeforeLastPostingPeriod(savingsAccountTransaction.transactionLocalDate(), false)) { || account.isBeforeLastPostingPeriod(savingsAccountTransaction.getTransactionDate(), false)) {
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, false, postReversals); postInterestOnDate, false, postReversals);
} else { } else {
@@ -927,7 +927,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
List<SavingsAccountTransaction> savingTransactions = account.getTransactions(); List<SavingsAccountTransaction> savingTransactions = account.getTransactions();
for (SavingsAccountTransaction savingTransaction : savingTransactions) { for (SavingsAccountTransaction savingTransaction : savingTransactions) {
if (savingTransaction.isInterestPosting() && savingTransaction.isNotReversed() if (savingTransaction.isInterestPosting() && savingTransaction.isNotReversed()
&& DateUtils.isEqual(closedDate, savingTransaction.getTransactionLocalDate())) { && DateUtils.isEqual(closedDate, savingTransaction.getTransactionDate())) {
postInterestOnClosingDate = true; postInterestOnClosingDate = true;
break; break;
} }
@@ -957,7 +957,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
} }
final Map<String, Object> accountChanges = account.close(user, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> accountChanges = account.close(user, command);
changes.putAll(accountChanges); changes.putAll(accountChanges);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.savingAccountRepositoryWrapper.save(account); this.savingAccountRepositoryWrapper.save(account);
@@ -985,7 +985,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public SavingsAccountTransaction initiateSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) { public SavingsAccountTransaction initiateSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) {
AppUser user = getAppUserIfPresent(); getAppUserIfPresent();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -999,7 +999,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransaction newTransferTransaction = SavingsAccountTransaction.initiateTransfer(savingsAccount, final SavingsAccountTransaction newTransferTransaction = SavingsAccountTransaction.initiateTransfer(savingsAccount,
savingsAccount.office(), transferDate, user); savingsAccount.office(), transferDate);
savingsAccount.addTransaction(newTransferTransaction); savingsAccount.addTransaction(newTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.TRANSFER_IN_PROGRESS.getValue());
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
@@ -1019,7 +1019,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public SavingsAccountTransaction withdrawSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) { public SavingsAccountTransaction withdrawSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) {
AppUser user = getAppUserIfPresent(); getAppUserIfPresent();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -1030,7 +1030,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransaction withdrawtransferTransaction = SavingsAccountTransaction.withdrawTransfer(savingsAccount, final SavingsAccountTransaction withdrawtransferTransaction = SavingsAccountTransaction.withdrawTransfer(savingsAccount,
savingsAccount.office(), transferDate, user); savingsAccount.office(), transferDate);
savingsAccount.addTransaction(withdrawtransferTransaction); savingsAccount.addTransaction(withdrawtransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
@@ -1058,7 +1058,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public SavingsAccountTransaction acceptSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate, public SavingsAccountTransaction acceptSavingsTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate,
final Office acceptedInOffice, final Staff fieldOfficer) { final Office acceptedInOffice, final Staff fieldOfficer) {
AppUser user = getAppUserIfPresent(); getAppUserIfPresent();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -1069,7 +1069,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(savingsAccount, existingTransactionIds, existingReversedTransactionIds);
final SavingsAccountTransaction acceptTransferTransaction = SavingsAccountTransaction.approveTransfer(savingsAccount, final SavingsAccountTransaction acceptTransferTransaction = SavingsAccountTransaction.approveTransfer(savingsAccount,
acceptedInOffice, transferDate, user); acceptedInOffice, transferDate);
savingsAccount.addTransaction(acceptTransferTransaction); savingsAccount.addTransaction(acceptTransferTransaction);
savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue()); savingsAccount.setStatus(SavingsAccountStatusType.ACTIVE.getValue());
if (fieldOfficer != null) { if (fieldOfficer != null) {
@@ -1126,17 +1126,17 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
savingsAccountCharge.setFreeWithdrawalCount(0); savingsAccountCharge.setFreeWithdrawalCount(0);
} }
if (savingsAccountCharge.getDueLocalDate() != null) { if (savingsAccountCharge.getDueDate() != null) {
// transaction date should not be on a holiday or non working day // transaction date should not be on a holiday or non working day
if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled()
&& this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { && this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday");
} }
if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled()
&& !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { && !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day");
} }
} }
@@ -1178,14 +1178,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final Map<String, Object> changes = savingsAccountCharge.update(command); final Map<String, Object> changes = savingsAccountCharge.update(command);
if (savingsAccountCharge.getDueLocalDate() != null) { if (savingsAccountCharge.getDueDate() != null) {
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);
// transaction date should not be on a holiday or non working day // transaction date should not be on a holiday or non working day
if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled() if (!this.configurationDomainService.allowTransactionsOnHolidayEnabled()
&& this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueLocalDate())) { && this.holidayRepository.isHoliday(savingsAccount.officeId(), savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.on.holiday");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -1193,8 +1193,8 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
} }
if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled() if (!this.configurationDomainService.allowTransactionsOnNonWorkingDayEnabled()
&& !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueLocalDate())) { && !this.workingDaysRepository.isWorkingDay(savingsAccountCharge.getDueDate())) {
baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueLocalDate().format(fmt)) baseDataValidator.reset().parameter(dueAsOfDateParamName).value(savingsAccountCharge.getDueDate().format(fmt))
.failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day"); .failWithCodeNoParameterAddedToErrorCode("charge.due.date.is.a.nonworking.day");
if (!dataValidationErrors.isEmpty()) { if (!dataValidationErrors.isEmpty()) {
throw new PlatformApiDataValidationException(dataValidationErrors); throw new PlatformApiDataValidationException(dataValidationErrors);
@@ -1217,7 +1217,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional @Transactional
@Override @Override
public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId) { public CommandProcessingResult waiveCharge(final Long savingsAccountId, final Long savingsAccountChargeId) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
@@ -1242,13 +1242,13 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
} }
account.waiveCharge(savingsAccountChargeId, user, backdatedTxnsAllowedTill); account.waiveCharge(savingsAccountChargeId, backdatedTxnsAllowedTill);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null; LocalDate postInterestOnDate = null;
final MathContext mc = MathContext.DECIMAL64; final MathContext mc = MathContext.DECIMAL64;
boolean postReversals = false; boolean postReversals = false;
if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueLocalDate(), backdatedTxnsAllowedTill)) { if (account.isBeforeLastPostingPeriod(savingsAccountCharge.getDueDate(), backdatedTxnsAllowedTill)) {
final LocalDate today = DateUtils.getBusinessLocalDate(); final LocalDate today = DateUtils.getBusinessLocalDate();
account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth, account.postInterest(mc, today, isInterestTransfer, isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth,
postInterestOnDate, backdatedTxnsAllowedTill, postReversals); postInterestOnDate, backdatedTxnsAllowedTill, postReversals);
@@ -1308,7 +1308,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public CommandProcessingResult payCharge(final Long savingsAccountId, final Long savingsAccountChargeId, final JsonCommand command) { public CommandProcessingResult payCharge(final Long savingsAccountId, final Long savingsAccountChargeId, final JsonCommand command) {
AppUser user = getAppUserIfPresent(); context.authenticatedUser();
this.savingsAccountChargeDataValidator.validatePayCharge(command.json()); this.savingsAccountChargeDataValidator.validatePayCharge(command.json());
final Locale locale = command.extractLocale(); final Locale locale = command.extractLocale();
@@ -1344,7 +1344,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final boolean backdatedTxnsAllowedTill = false; final boolean backdatedTxnsAllowedTill = false;
SavingsAccountTransaction chargeTransaction = this.payCharge(savingsAccountCharge, transactionDate, amountPaid, fmt, user, SavingsAccountTransaction chargeTransaction = this.payCharge(savingsAccountCharge, transactionDate, amountPaid, fmt,
backdatedTxnsAllowedTill); backdatedTxnsAllowedTill);
final String noteText = command.stringValueOfParameterNamed("note"); final String noteText = command.stringValueOfParameterNamed("note");
@@ -1367,16 +1367,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Override @Override
public void applyChargeDue(final Long savingsAccountChargeId, final Long accountId) { public void applyChargeDue(final Long savingsAccountChargeId, final Long accountId) {
// always use current date as transaction date for batch job // always use current date as transaction date for batch job
AppUser user = null;
final LocalDate transactionDate = DateUtils.getBusinessLocalDate(); final LocalDate transactionDate = DateUtils.getBusinessLocalDate();
final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository final SavingsAccountCharge savingsAccountCharge = this.savingsAccountChargeRepository
.findOneWithNotFoundDetection(savingsAccountChargeId, accountId); .findOneWithNotFoundDetection(savingsAccountChargeId, accountId);
final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MM yyyy").withZone(DateUtils.getDateTimeZoneOfTenant()); final DateTimeFormatter fmt = DateTimeFormatter.ofPattern("dd MM yyyy").withZone(DateUtils.getDateTimeZoneOfTenant());
while (savingsAccountCharge.isNotFullyPaid() && DateUtils.isBeforeBusinessDate(savingsAccountCharge.getDueLocalDate())) { while (savingsAccountCharge.isNotFullyPaid() && DateUtils.isBefore(savingsAccountCharge.getDueDate(), transactionDate)) {
payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt, user, false); payCharge(savingsAccountCharge, transactionDate, savingsAccountCharge.amoutOutstanding(), fmt, false);
} }
} }
@@ -1390,8 +1388,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional @Transactional
private SavingsAccountTransaction payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate, private SavingsAccountTransaction payCharge(final SavingsAccountCharge savingsAccountCharge, final LocalDate transactionDate,
final BigDecimal amountPaid, final DateTimeFormatter formatter, final AppUser user, final boolean backdatedTxnsAllowedTill) { final BigDecimal amountPaid, final DateTimeFormatter formatter, final boolean backdatedTxnsAllowedTill) {
final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService final boolean isSavingsInterestPostingAtCurrentPeriodEnd = this.configurationDomainService
.isSavingsInterestPostingAtCurrentPeriodEnd(); .isSavingsInterestPostingAtCurrentPeriodEnd();
final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth(); final Integer financialYearBeginningMonth = this.configurationDomainService.retrieveFinancialYearBeginningMonth();
@@ -1409,7 +1406,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
account.validateAccountBalanceDoesNotViolateOverdraft(savingsAccountTransaction, amountPaid); account.validateAccountBalanceDoesNotViolateOverdraft(savingsAccountTransaction, amountPaid);
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
SavingsAccountTransaction chargeTransaction = account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter, user, SavingsAccountTransaction chargeTransaction = account.payCharge(savingsAccountCharge, amountPaid, transactionDate, formatter,
backdatedTxnsAllowedTill, null); backdatedTxnsAllowedTill, null);
boolean isInterestTransfer = false; boolean isInterestTransfer = false;
LocalDate postInterestOnDate = null; LocalDate postInterestOnDate = null;
@@ -1649,7 +1646,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
final Set<Long> existingTransactionIds = new HashSet<>(); final Set<Long> existingTransactionIds = new HashSet<>();
final Set<Long> existingReversedTransactionIds = new HashSet<>(); final Set<Long> existingReversedTransactionIds = new HashSet<>();
updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds); updateExistingTransactionsDetails(account, existingTransactionIds, existingReversedTransactionIds);
account.setSubStatusInactive(appuserRepository.fetchSystemUser(), false); account.setSubStatusInactive(false);
this.savingAccountRepositoryWrapper.saveAndFlush(account); this.savingAccountRepositoryWrapper.saveAndFlush(account);
postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, false); postJournalEntries(account, existingTransactionIds, existingReversedTransactionIds, false);
} }
@@ -1767,16 +1764,15 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
this.savingsAccountTransactionDataValidator.validateHoldAndAssembleForm(command.json(), account, submittedBy, this.savingsAccountTransactionDataValidator.validateHoldAndAssembleForm(command.json(), account, submittedBy,
backdatedTxnsAllowedTill); backdatedTxnsAllowedTill);
SavingsAccountTransaction transaction = this.savingsAccountDomainService.handleHold(account, getAppUserIfPresent(), amount, SavingsAccountTransaction transaction = this.savingsAccountDomainService.handleHold(account, amount, transactionDate, lienAllowed);
transactionDate, lienAllowed);
account.holdAmount(amount); account.holdAmount(amount);
transaction.updateRunningBalance(runningBalance); transaction.setRunningBalance(runningBalance);
final String reasonForBlock = command.stringValueOfParameterNamed(SavingsApiConstants.reasonForBlockParamName); final String reasonForBlock = command.stringValueOfParameterNamed(SavingsApiConstants.reasonForBlockParamName);
transaction.updateReason(reasonForBlock); transaction.updateReason(reasonForBlock);
account.getAccountBalance(); account.getAccountBalance();
this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(transaction.getTransactionLocalDate(), account); this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(transaction.getTransactionDate(), account);
this.savingsAccountTransactionRepository.saveAndFlush(transaction); this.savingsAccountTransactionRepository.saveAndFlush(transaction);
@@ -1794,14 +1790,14 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
@Transactional @Transactional
@Override @Override
public CommandProcessingResult releaseAmount(final Long savingsId, final Long savingsTransactionId) { public CommandProcessingResult releaseAmount(final Long savingsId, final Long savingsTransactionId) {
final AppUser submittedBy = this.context.authenticatedUser(); context.authenticatedUser();
SavingsAccountTransaction holdTransaction = this.savingsAccountTransactionRepository SavingsAccountTransaction holdTransaction = this.savingsAccountTransactionRepository
.findOneByIdAndSavingsAccountId(savingsTransactionId, savingsId); .findOneByIdAndSavingsAccountId(savingsTransactionId, savingsId);
holdTransaction.updateReason(null); holdTransaction.updateReason(null);
final SavingsAccountTransaction transaction = this.savingsAccountTransactionDataValidator final SavingsAccountTransaction transaction = this.savingsAccountTransactionDataValidator
.validateReleaseAmountAndAssembleForm(holdTransaction, submittedBy); .validateReleaseAmountAndAssembleForm(holdTransaction);
final boolean backdatedTxnsAllowedTill = this.savingAccountAssembler.getPivotConfigStatus(); final boolean backdatedTxnsAllowedTill = this.savingAccountAssembler.getPivotConfigStatus();
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, backdatedTxnsAllowedTill); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, backdatedTxnsAllowedTill);
@@ -1814,9 +1810,9 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
runningBalance = runningBalance.minus(savingsOnHold); runningBalance = runningBalance.minus(savingsOnHold);
runningBalance = runningBalance.plus(transaction.getAmount()); runningBalance = runningBalance.plus(transaction.getAmount());
transaction.updateRunningBalance(runningBalance); transaction.setRunningBalance(runningBalance);
this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(transaction.getTransactionLocalDate(), account); this.savingsAccountTransactionDataValidator.validateTransactionWithPivotDate(transaction.getTransactionDate(), account);
account.releaseOnHoldAmount(transaction.getAmount()); account.releaseOnHoldAmount(transaction.getAmount());
this.savingsAccountTransactionRepository.saveAndFlush(transaction); this.savingsAccountTransactionRepository.saveAndFlush(transaction);
@@ -1914,11 +1910,11 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
private void validateTransactionsForTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) { private void validateTransactionsForTransfer(final SavingsAccount savingsAccount, final LocalDate transferDate) {
for (SavingsAccountTransaction transaction : savingsAccount.getTransactions()) { for (SavingsAccountTransaction transaction : savingsAccount.getTransactions()) {
if ((DateUtils.isEqual(transferDate, transaction.getTransactionLocalDate()) if ((DateUtils.isEqual(transferDate, transaction.getTransactionDate())
&& DateUtils.isEqual(transferDate, transaction.getSubmittedOnDate())) && DateUtils.isEqual(transferDate, transaction.getSubmittedOnDate()))
|| DateUtils.isBefore(transferDate, transaction.getTransactionLocalDate())) { || DateUtils.isBefore(transferDate, transaction.getTransactionDate())) {
throw new GeneralPlatformDomainRuleException(TransferApiConstants.transferClientSavingsException, throw new GeneralPlatformDomainRuleException(TransferApiConstants.transferClientSavingsException,
TransferApiConstants.transferClientSavingsException, transaction.getCreatedDate(), transferDate); TransferApiConstants.transferClientSavingsException, transaction.getCreatedDateTime(), transferDate);
} }
} }
} }
@@ -47,7 +47,6 @@ import org.apache.fineract.infrastructure.core.data.CommandProcessingResultBuild
import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder; import org.apache.fineract.infrastructure.core.data.DataValidatorBuilder;
import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException; import org.apache.fineract.infrastructure.core.exception.PlatformApiDataValidationException;
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.dataqueries.data.EntityTables; import org.apache.fineract.infrastructure.dataqueries.data.EntityTables;
import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum; import org.apache.fineract.infrastructure.dataqueries.data.StatusEnum;
import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService; import org.apache.fineract.infrastructure.dataqueries.service.EntityDatatableChecksWritePlatformService;
@@ -320,7 +319,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, false); final SavingsAccount account = this.savingAccountAssembler.assembleFrom(savingsId, false);
checkClientOrGroupActive(account); checkClientOrGroupActive(account);
account.modifyApplication(command, changes); account.modifyApplication(command, changes);
account.validateNewApplicationState(DateUtils.getBusinessLocalDate(), SAVINGS_ACCOUNT_RESOURCE_NAME); account.validateNewApplicationState(SAVINGS_ACCOUNT_RESOURCE_NAME);
account.validateAccountValuesWithProduct(); account.validateAccountValuesWithProduct();
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
@@ -480,7 +479,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
StatusEnum.APPROVE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(), StatusEnum.APPROVE.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(),
savingsAccount.productId()); savingsAccount.productId());
final Map<String, Object> changes = savingsAccount.approveApplication(currentUser, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> changes = savingsAccount.approveApplication(currentUser, command);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount); this.savingAccountRepository.save(savingsAccount);
@@ -603,7 +602,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
StatusEnum.REJECTED.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(), StatusEnum.REJECTED.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(),
savingsAccount.productId()); savingsAccount.productId());
final Map<String, Object> changes = savingsAccount.rejectApplication(currentUser, command, DateUtils.getBusinessLocalDate()); final Map<String, Object> changes = savingsAccount.rejectApplication(currentUser, command);
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount); this.savingAccountRepository.save(savingsAccount);
@@ -640,8 +639,7 @@ public class SavingsApplicationProcessWritePlatformServiceJpaRepositoryImpl impl
StatusEnum.WITHDRAWN.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(), StatusEnum.WITHDRAWN.getCode().longValue(), EntityTables.SAVINGS.getForeignKeyColumnNameOnDatatable(),
savingsAccount.productId()); savingsAccount.productId());
final Map<String, Object> changes = savingsAccount.applicantWithdrawsFromApplication(currentUser, command, final Map<String, Object> changes = savingsAccount.applicantWithdrawsFromApplication(currentUser, command);
DateUtils.getBusinessLocalDate());
if (!changes.isEmpty()) { if (!changes.isEmpty()) {
this.savingAccountRepository.save(savingsAccount); this.savingAccountRepository.save(savingsAccount);
@@ -18,12 +18,16 @@
*/ */
package org.apache.fineract.portfolio.savings.service; package org.apache.fineract.portfolio.savings.service;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_BY_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_DATE_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_BY_DB_FIELD;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.LAST_MODIFIED_DATE_DB_FIELD;
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.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
@@ -64,7 +68,6 @@ public class SavingsSchedularInterestPoster {
@Transactional(isolation = Isolation.READ_UNCOMMITTED, rollbackFor = Exception.class) @Transactional(isolation = Isolation.READ_UNCOMMITTED, rollbackFor = Exception.class)
public void postInterest() throws JobExecutionException { public void postInterest() throws JobExecutionException {
if (!savingAccounts.isEmpty()) { if (!savingAccounts.isEmpty()) {
List<Throwable> errors = new ArrayList<>(); List<Throwable> errors = new ArrayList<>();
for (SavingsAccountData savingsAccountData : savingAccounts) { for (SavingsAccountData savingsAccountData : savingAccounts) {
@@ -114,14 +117,14 @@ public class SavingsSchedularInterestPoster {
savingsAccountTransactionData.setId(dataFromFetch.getId()); savingsAccountTransactionData.setId(dataFromFetch.getId());
if (savingsAccountData.getGlAccountIdForSavingsControl() != 0 if (savingsAccountData.getGlAccountIdForSavingsControl() != 0
&& savingsAccountData.getGlAccountIdForInterestOnSavings() != 0) { && savingsAccountData.getGlAccountIdForInterestOnSavings() != 0) {
OffsetDateTime auditDatetime = DateUtils.getAuditOffsetDateTime();
paramsForGLInsertion.add(new Object[] { savingsAccountData.getGlAccountIdForSavingsControl(), paramsForGLInsertion.add(new Object[] { savingsAccountData.getGlAccountIdForSavingsControl(),
savingsAccountData.getOfficeId(), null, currencyCode, savingsAccountData.getOfficeId(), null, currencyCode,
SAVINGS_TRANSACTION_IDENTIFIER + savingsAccountTransactionData.getId().toString(), SAVINGS_TRANSACTION_IDENTIFIER + savingsAccountTransactionData.getId().toString(),
savingsAccountTransactionData.getId(), null, false, null, false, savingsAccountTransactionData.getId(), null, false, null, false,
savingsAccountTransactionData.getTransactionDate(), JournalEntryType.CREDIT.getValue().longValue(), savingsAccountTransactionData.getTransactionDate(), JournalEntryType.CREDIT.getValue().longValue(),
savingsAccountTransactionData.getAmount(), null, JournalEntryType.CREDIT.getValue().longValue(), savingsAccountTransactionData.getAmount(), null, JournalEntryType.CREDIT.getValue().longValue(),
savingsAccountData.getId(), DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(), savingsAccountData.getId(), auditDatetime, auditDatetime, false, BigDecimal.ZERO, BigDecimal.ZERO, null,
DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(), false, BigDecimal.ZERO, BigDecimal.ZERO, null,
savingsAccountTransactionData.getTransactionDate(), null, userId, userId, savingsAccountTransactionData.getTransactionDate(), null, userId, userId,
DateUtils.getBusinessLocalDate() }); DateUtils.getBusinessLocalDate() });
@@ -131,8 +134,7 @@ public class SavingsSchedularInterestPoster {
savingsAccountTransactionData.getId(), null, false, null, false, savingsAccountTransactionData.getId(), null, false, null, false,
savingsAccountTransactionData.getTransactionDate(), JournalEntryType.DEBIT.getValue().longValue(), savingsAccountTransactionData.getTransactionDate(), JournalEntryType.DEBIT.getValue().longValue(),
savingsAccountTransactionData.getAmount(), null, JournalEntryType.DEBIT.getValue().longValue(), savingsAccountTransactionData.getAmount(), null, JournalEntryType.DEBIT.getValue().longValue(),
savingsAccountData.getId(), DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(), savingsAccountData.getId(), auditDatetime, auditDatetime, false, BigDecimal.ZERO, BigDecimal.ZERO, null,
DateUtils.getOffsetDateTimeOfTenantWithMostPrecision(), false, BigDecimal.ZERO, BigDecimal.ZERO, null,
savingsAccountTransactionData.getTransactionDate(), null, userId, userId, savingsAccountTransactionData.getTransactionDate(), null, userId, userId,
DateUtils.getBusinessLocalDate() }); DateUtils.getBusinessLocalDate() });
} }
@@ -147,16 +149,12 @@ public class SavingsSchedularInterestPoster {
} }
private String batchQueryForJournalEntries() { private String batchQueryForJournalEntries() {
StringBuilder query = new StringBuilder(100); return "INSERT INTO acc_gl_journal_entry(account_id,office_id,reversal_id,currency_code,transaction_id,"
+ "savings_transaction_id,client_transaction_id,reversed,ref_num,manual_entry,entry_date,type_enum,"
query.append("INSERT INTO acc_gl_journal_entry(account_id,office_id,reversal_id,currency_code,transaction_id,"); + "amount,description,entity_type_enum,entity_id,created_on_utc,"
query.append("savings_transaction_id,client_transaction_id,reversed,ref_num,manual_entry,entry_date,type_enum,"); + "last_modified_on_utc,is_running_balance_calculated,office_running_balance,organization_running_balance,"
query.append("amount,description,entity_type_enum,entity_id,created_on_utc,"); + "payment_details_id,transaction_date,share_transaction_id, created_by, last_modified_by, submitted_on_date) "
query.append("last_modified_on_utc,is_running_balance_calculated,office_running_balance,organization_running_balance,"); + "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
query.append("payment_details_id,transaction_date,share_transaction_id, created_by, last_modified_by, submitted_on_date) ");
query.append("VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
return query.toString();
} }
private List<SavingsAccountTransactionData> fetchTransactionsFromIds(final List<String> refNo) throws DataAccessException { private List<SavingsAccountTransactionData> fetchTransactionsFromIds(final List<String> refNo) throws DataAccessException {
@@ -172,7 +170,10 @@ public class SavingsSchedularInterestPoster {
List<Object[]> paramsForSavingsSummary = new ArrayList<>(); List<Object[]> paramsForSavingsSummary = new ArrayList<>();
List<Object[]> paramsForTransactionUpdate = new ArrayList<>(); List<Object[]> paramsForTransactionUpdate = new ArrayList<>();
List<String> transRefNo = new ArrayList<>(); List<String> transRefNo = new ArrayList<>();
LocalDate currentDate = DateUtils.getBusinessLocalDate();
Long userId = platformSecurityContext.authenticatedUser().getId();
for (SavingsAccountData savingsAccountData : savingsAccountDataList) { for (SavingsAccountData savingsAccountData : savingsAccountDataList) {
OffsetDateTime auditTime = DateUtils.getAuditOffsetDateTime();
SavingsAccountSummaryData savingsAccountSummaryData = savingsAccountData.getSummary(); SavingsAccountSummaryData savingsAccountSummaryData = savingsAccountData.getSummary();
paramsForSavingsSummary.add(new Object[] { savingsAccountSummaryData.getTotalDeposits(), paramsForSavingsSummary.add(new Object[] { savingsAccountSummaryData.getTotalDeposits(),
savingsAccountSummaryData.getTotalWithdrawals(), savingsAccountSummaryData.getTotalInterestEarned(), savingsAccountSummaryData.getTotalWithdrawals(), savingsAccountSummaryData.getTotalInterestEarned(),
@@ -180,39 +181,30 @@ public class SavingsSchedularInterestPoster {
savingsAccountSummaryData.getTotalFeeCharge(), savingsAccountSummaryData.getTotalPenaltyCharge(), savingsAccountSummaryData.getTotalFeeCharge(), savingsAccountSummaryData.getTotalPenaltyCharge(),
savingsAccountSummaryData.getTotalAnnualFees(), savingsAccountSummaryData.getAccountBalance(), savingsAccountSummaryData.getTotalAnnualFees(), savingsAccountSummaryData.getAccountBalance(),
savingsAccountSummaryData.getTotalOverdraftInterestDerived(), savingsAccountSummaryData.getTotalWithholdTax(), savingsAccountSummaryData.getTotalOverdraftInterestDerived(), savingsAccountSummaryData.getTotalWithholdTax(),
Date.from(savingsAccountSummaryData.getLastInterestCalculationDate().atStartOfDay(DateUtils.getDateTimeZoneOfTenant()) savingsAccountSummaryData.getLastInterestCalculationDate(),
.toInstant()), savingsAccountSummaryData.getInterestPostedTillDate() != null ? savingsAccountSummaryData.getInterestPostedTillDate()
savingsAccountSummaryData.getInterestPostedTillDate() != null : savingsAccountSummaryData.getLastInterestCalculationDate(),
? Date.from(savingsAccountSummaryData.getInterestPostedTillDate() auditTime, userId, savingsAccountData.getId() });
.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant())
: Date.from(savingsAccountSummaryData.getLastInterestCalculationDate()
.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant()),
savingsAccountData.getId() });
List<SavingsAccountTransactionData> savingsAccountTransactionDataList = savingsAccountData.getSavingsAccountTransactionData(); List<SavingsAccountTransactionData> savingsAccountTransactionDataList = savingsAccountData.getSavingsAccountTransactionData();
LocalDateTime currentDate = DateUtils.getLocalDateTimeOfTenant();
for (SavingsAccountTransactionData savingsAccountTransactionData : savingsAccountTransactionDataList) { for (SavingsAccountTransactionData savingsAccountTransactionData : savingsAccountTransactionDataList) {
Date balanceEndDate = null;
if (savingsAccountTransactionData.getBalanceEndDate() != null) {
balanceEndDate = Date.from(savingsAccountTransactionData.getBalanceEndDate()
.atStartOfDay(DateUtils.getDateTimeZoneOfTenant()).toInstant());
}
if (savingsAccountTransactionData.getId() == null) { if (savingsAccountTransactionData.getId() == null) {
UUID uuid = UUID.randomUUID(); UUID uuid = UUID.randomUUID();
savingsAccountTransactionData.setRefNo(uuid.toString()); savingsAccountTransactionData.setRefNo(uuid.toString());
transRefNo.add(uuid.toString()); transRefNo.add(uuid.toString());
paramsForTransactionInsertion.add(new Object[] { savingsAccountData.getId(), savingsAccountData.getOfficeId(), paramsForTransactionInsertion.add(new Object[] { savingsAccountData.getId(), savingsAccountData.getOfficeId(),
savingsAccountTransactionData.isReversed(), savingsAccountTransactionData.getTransactionType().getId(), savingsAccountTransactionData.isReversed(), savingsAccountTransactionData.getTransactionType().getId(),
savingsAccountTransactionData.getTransactionDate(), savingsAccountTransactionData.getAmount(), balanceEndDate, savingsAccountTransactionData.getTransactionDate(), savingsAccountTransactionData.getAmount(),
savingsAccountTransactionData.getBalanceNumberOfDays(), savingsAccountTransactionData.getRunningBalance(), savingsAccountTransactionData.getBalanceEndDate(), savingsAccountTransactionData.getBalanceNumberOfDays(),
savingsAccountTransactionData.getCumulativeBalance(), currentDate, 1, savingsAccountTransactionData.getRunningBalance(), savingsAccountTransactionData.getCumulativeBalance(),
savingsAccountTransactionData.isManualTransaction(), savingsAccountTransactionData.getRefNo(), auditTime, userId, auditTime, userId, savingsAccountTransactionData.isManualTransaction(),
savingsAccountTransactionData.isReversalTransaction(), savingsAccountTransactionData.getOverdraftAmount(), savingsAccountTransactionData.getRefNo(), savingsAccountTransactionData.isReversalTransaction(),
DateUtils.getBusinessLocalDate() }); savingsAccountTransactionData.getOverdraftAmount(), currentDate });
} else { } else {
paramsForTransactionUpdate.add(new Object[] { savingsAccountTransactionData.isReversed(), paramsForTransactionUpdate.add(new Object[] { savingsAccountTransactionData.isReversed(),
savingsAccountTransactionData.getAmount(), savingsAccountTransactionData.getOverdraftAmount(), balanceEndDate, savingsAccountTransactionData.getAmount(), savingsAccountTransactionData.getOverdraftAmount(),
savingsAccountTransactionData.getBalanceNumberOfDays(), savingsAccountTransactionData.getRunningBalance(), savingsAccountTransactionData.getBalanceEndDate(), savingsAccountTransactionData.getBalanceNumberOfDays(),
savingsAccountTransactionData.getCumulativeBalance(), savingsAccountTransactionData.isReversalTransaction(), savingsAccountTransactionData.getRunningBalance(), savingsAccountTransactionData.getCumulativeBalance(),
savingsAccountTransactionData.isReversalTransaction(), auditTime, userId,
savingsAccountTransactionData.getId() }); savingsAccountTransactionData.getId() });
} }
} }
@@ -240,35 +232,23 @@ public class SavingsSchedularInterestPoster {
} }
private String batchQueryForTransactionInsertion() { private String batchQueryForTransactionInsertion() {
StringBuilder query = new StringBuilder(100); return "INSERT INTO m_savings_account_transaction (savings_account_id, office_id, is_reversed, transaction_type_enum, transaction_date, amount, balance_end_date_derived, "
query.append("INSERT INTO m_savings_account_transaction (savings_account_id, office_id, is_reversed, "); + "balance_number_of_days_derived, running_balance_derived, cumulative_balance_derived, " + CREATED_DATE_DB_FIELD + ", "
query.append("transaction_type_enum, transaction_date, amount, balance_end_date_derived, "); + CREATED_BY_DB_FIELD + ", " + LAST_MODIFIED_DATE_DB_FIELD + ", " + LAST_MODIFIED_BY_DB_FIELD
query.append("balance_number_of_days_derived, running_balance_derived, cumulative_balance_derived, "); + ", is_manual, ref_no, is_reversal, "
query.append("created_date, appuser_id, is_manual, ref_no, is_reversal, "); + "overdraft_amount_derived, submitted_on_date) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
query.append("overdraft_amount_derived, submitted_on_date) VALUES ");
query.append("(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
return query.toString();
} }
private String batchQueryForSavingsSummaryUpdate() { private String batchQueryForSavingsSummaryUpdate() {
StringBuilder query = new StringBuilder(100); return "update m_savings_account set total_deposits_derived=?, total_withdrawals_derived=?, total_interest_earned_derived=?, total_interest_posted_derived=?, total_withdrawal_fees_derived=?, "
query.append("update m_savings_account set total_deposits_derived=?, total_withdrawals_derived=?, "); + "total_fees_charge_derived=?, total_penalty_charge_derived=?, total_annual_fees_derived=?, account_balance_derived=?, total_overdraft_interest_derived=?, total_withhold_tax_derived=?, "
query.append("total_interest_earned_derived=?, total_interest_posted_derived=?, total_withdrawal_fees_derived=?, "); + "last_interest_calculation_date=?, interest_posted_till_date=?, " + LAST_MODIFIED_DATE_DB_FIELD + " = ?, "
query.append("total_fees_charge_derived=?, total_penalty_charge_derived=?, total_annual_fees_derived=?, "); + LAST_MODIFIED_BY_DB_FIELD + " = ? WHERE id=? ";
query.append("account_balance_derived=?, total_overdraft_interest_derived=?, total_withhold_tax_derived=?, ");
query.append("last_interest_calculation_date=?, interest_posted_till_date=? where id=? ");
return query.toString();
} }
private String batchQueryForTransactionsUpdate() { private String batchQueryForTransactionsUpdate() {
StringBuilder query = new StringBuilder(100); return "UPDATE m_savings_account_transaction "
query.append("UPDATE m_savings_account_transaction "); + "SET is_reversed=?, amount=?, overdraft_amount_derived=?, balance_end_date_derived=?, balance_number_of_days_derived=?, running_balance_derived=?, cumulative_balance_derived=?, is_reversal=?, "
query.append("SET is_reversed=?, "); + LAST_MODIFIED_DATE_DB_FIELD + " = ?, " + LAST_MODIFIED_BY_DB_FIELD + " = ? " + "WHERE id=?";
query.append("amount=?, overdraft_amount_derived=?, balance_end_date_derived=?, ");
query.append("balance_number_of_days_derived=?, running_balance_derived=?, cumulative_balance_derived=?, ");
query.append("is_reversal=? ");
query.append("WHERE id=?");
return query.toString();
} }
} }
@@ -18,6 +18,7 @@
*/ */
package org.apache.fineract.portfolio.savings.service.search; package org.apache.fineract.portfolio.savings.service.search;
import static org.apache.fineract.infrastructure.core.domain.AuditableFieldsConstants.CREATED_DATE_DB_FIELD;
import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME; import static org.apache.fineract.portfolio.savings.SavingsApiConstants.SAVINGS_ACCOUNT_RESOURCE_NAME;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
@@ -87,7 +88,7 @@ public class SavingsAccountTransactionsSearchServiceImpl implements SavingsAccou
sortPageable = pageable.withSort(Sort.by(orders.stream() sortPageable = pageable.withSort(Sort.by(orders.stream()
.map(e -> e.withProperty(SearchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList())); .map(e -> e.withProperty(SearchUtil.validateToJdbcColumnName(e.getProperty(), headersByName, false))).toList()));
} else { } else {
pageable = pageable.withSort(Sort.Direction.DESC, "transaction_date", "created_date", "id"); pageable = pageable.withSort(Sort.Direction.DESC, "transaction_date", CREATED_DATE_DB_FIELD, "id");
sortPageable = pageable; sortPageable = pageable;
} }
@@ -147,4 +147,5 @@
<include file="parts/0125_transaction_summary_with_asset_owner_report_chargeoff_reason.xml" relativeToChangelogFile="true" /> <include file="parts/0125_transaction_summary_with_asset_owner_report_chargeoff_reason.xml" relativeToChangelogFile="true" />
<include file="parts/0126_add_loan_product_installment_level_delinquency.xml" relativeToChangelogFile="true" /> <include file="parts/0126_add_loan_product_installment_level_delinquency.xml" relativeToChangelogFile="true" />
<include file="parts/0127_client_name_length.xml" relativeToChangelogFile="true" /> <include file="parts/0127_client_name_length.xml" relativeToChangelogFile="true" />
<include file="parts/0128_savings_audit.xml" relativeToChangelogFile="true" />
</databaseChangeLog> </databaseChangeLog>
@@ -0,0 +1,266 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-4.1.xsd">
<!-- add audit fields -->
<changeSet id="1" author="fineract">
<addColumn tableName="m_savings_account">
<column name="created_by" type="BIGINT" valueComputed="submittedon_userid"/>
<column name="last_modified_by" type="BIGINT"/>
</addColumn>
<renameColumn tableName="m_savings_account_transaction" oldColumnName="appuser_id" newColumnName="created_by" columnDataType="BIGINT"/>
<addColumn tableName="m_savings_account_transaction">
<column name="last_modified_by" type="BIGINT"/>
</addColumn>
<addColumn tableName="m_deposit_account_on_hold_transaction">
<column name="created_by" type="BIGINT"/>
<column name="last_modified_by" type="BIGINT"/>
</addColumn>
<addColumn tableName="m_savings_account_charge">
<column name="created_by" type="BIGINT"/>
<column name="last_modified_by" type="BIGINT"/>
</addColumn>
<renameColumn tableName="m_savings_officer_assignment_history" oldColumnName="createdby_id" newColumnName="created_by" columnDataType="BIGINT"/>
<renameColumn tableName="m_savings_officer_assignment_history" oldColumnName="lastmodifiedby_id" newColumnName="last_modified_by" columnDataType="BIGINT"/>
<renameColumn tableName="m_mandatory_savings_schedule" oldColumnName="createdby_id" newColumnName="created_by" columnDataType="BIGINT"/>
<renameColumn tableName="m_mandatory_savings_schedule" oldColumnName="lastmodifiedby_id" newColumnName="last_modified_by" columnDataType="BIGINT"/>
</changeSet>
<changeSet id="2" author="fineract" context="mysql">
<addColumn tableName="m_savings_account">
<column name="created_on_utc" type="DATETIME(6)"/>
<column name="last_modified_on_utc" type="DATETIME(6)"/>
</addColumn>
<addColumn tableName="m_savings_account_transaction">
<column name="created_on_utc" type="DATETIME(6)"/>
<column name="last_modified_on_utc" type="DATETIME(6)"/>
</addColumn>
<addColumn tableName="m_deposit_account_on_hold_transaction">
<column name="created_on_utc" type="DATETIME(6)"/>
<column name="last_modified_on_utc" type="DATETIME(6)"/>
</addColumn>
<addColumn tableName="m_savings_account_charge">
<column name="created_on_utc" type="DATETIME(6)"/>
<column name="last_modified_on_utc" type="DATETIME(6)"/>
</addColumn>
<addColumn tableName="m_savings_officer_assignment_history">
<column name="created_on_utc" type="DATETIME(6)"/>
<column name="last_modified_on_utc" type="DATETIME(6)"/>
</addColumn>
<addColumn tableName="m_mandatory_savings_schedule">
<column name="created_on_utc" type="DATETIME(6)"/>
<column name="last_modified_on_utc" type="DATETIME(6)"/>
</addColumn>
</changeSet>
<changeSet id="2" author="fineract" context="postgresql">
<addColumn tableName="m_savings_account">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
<addColumn tableName="m_savings_account_transaction">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
<addColumn tableName="m_deposit_account_on_hold_transaction">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
<addColumn tableName="m_savings_account_charge">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
<addColumn tableName="m_savings_officer_assignment_history">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
<addColumn tableName="m_mandatory_savings_schedule">
<column name="created_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
<column name="last_modified_on_utc" type="TIMESTAMP WITH TIME ZONE"/>
</addColumn>
</changeSet>
<!-- add audit constraints -->
<changeSet id="3" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_account</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_account" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_account" columnName="last_modified_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_account" columnName="created_on_utc" columnDataType="DATETIME(6)"/>
<addNotNullConstraint tableName="m_savings_account" columnName="last_modified_on_utc" columnDataType="DATETIME(6)"/>
</changeSet>
<changeSet id="3" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_account</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_account" columnName="created_by"/>
<addNotNullConstraint tableName="m_savings_account" columnName="last_modified_by"/>
<addNotNullConstraint tableName="m_savings_account" columnName="created_on_utc"/>
<addNotNullConstraint tableName="m_savings_account" columnName="last_modified_on_utc"/>
</changeSet>
<changeSet id="4" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_account_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="last_modified_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="created_on_utc" columnDataType="DATETIME(6)"/>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="last_modified_on_utc" columnDataType="DATETIME(6)"/>
</changeSet>
<changeSet id="4" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_account_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="created_by"/>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="last_modified_by"/>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="created_on_utc"/>
<addNotNullConstraint tableName="m_savings_account_transaction" columnName="last_modified_on_utc"/>
</changeSet>
<changeSet id="5" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_deposit_account_on_hold_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="last_modified_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="created_on_utc" columnDataType="DATETIME(6)"/>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="last_modified_on_utc" columnDataType="DATETIME(6)"/>
</changeSet>
<changeSet id="5" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_deposit_account_on_hold_transaction</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="created_by"/>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="last_modified_by"/>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="created_on_utc"/>
<addNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="last_modified_on_utc"/>
</changeSet>
<changeSet id="6" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_account_charge</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="last_modified_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="created_on_utc" columnDataType="DATETIME(6)"/>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="last_modified_on_utc" columnDataType="DATETIME(6)"/>
</changeSet>
<changeSet id="6" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_account_charge</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="created_by"/>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="last_modified_by"/>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="created_on_utc"/>
<addNotNullConstraint tableName="m_savings_account_charge" columnName="last_modified_on_utc"/>
</changeSet>
<changeSet id="7" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_officer_assignment_history</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="last_modified_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="created_on_utc" columnDataType="DATETIME(6)"/>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="last_modified_on_utc" columnDataType="DATETIME(6)"/>
</changeSet>
<changeSet id="7" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_savings_officer_assignment_history</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="created_by"/>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="last_modified_by"/>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="created_on_utc"/>
<addNotNullConstraint tableName="m_savings_officer_assignment_history" columnName="last_modified_on_utc"/>
</changeSet>
<changeSet id="8" author="fineract" context="mysql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_mandatory_savings_schedule</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="created_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="last_modified_by" columnDataType="BIGINT"/>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="created_on_utc" columnDataType="DATETIME(6)"/>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="last_modified_on_utc" columnDataType="DATETIME(6)"/>
</changeSet>
<changeSet id="8" author="fineract" context="postgresql">
<preConditions onFail="MARK_RAN">
<sqlCheck expectedResult="0">select count(*) from m_mandatory_savings_schedule</sqlCheck>
</preConditions>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="created_by"/>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="last_modified_by"/>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="created_on_utc"/>
<addNotNullConstraint tableName="m_mandatory_savings_schedule" columnName="last_modified_on_utc"/>
</changeSet>
<changeSet id="9" author="fineract">
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_savings_account"
constraintName="FK_savings_account_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_savings_account"
constraintName="FK_savings_account_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_savings_account_transaction"
constraintName="FK_savings_account_transaction_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_savings_account_transaction"
constraintName="FK_savings_account_transaction_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_deposit_account_on_hold_transaction"
constraintName="FK_deposit_account_on_hold_transaction_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_deposit_account_on_hold_transaction"
constraintName="FK_deposit_account_on_hold_transaction_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_savings_account_charge"
constraintName="FK_savings_account_charge_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_savings_account_charge"
constraintName="FK_savings_account_charge_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_savings_officer_assignment_history"
constraintName="FK_savings_officer_assignment_history_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_savings_officer_assignment_history"
constraintName="FK_savings_officer_assignment_history_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="created_by" baseTableName="m_mandatory_savings_schedule"
constraintName="FK_mandatory_savings_schedule_created_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
<addForeignKeyConstraint baseColumnNames="last_modified_by" baseTableName="m_mandatory_savings_schedule"
constraintName="FK_mandatory_savings_schedule_last_modified_by" deferrable="false" initiallyDeferred="false"
onDelete="RESTRICT" onUpdate="RESTRICT" referencedColumnNames="id"
referencedTableName="m_appuser" validate="true"/>
</changeSet>
<!-- old fields -->
<changeSet id="10" author="fineract">
<dropNotNullConstraint tableName="m_savings_account_transaction" columnName="created_date" columnDataType="DATETIME"/>
<dropNotNullConstraint tableName="m_deposit_account_on_hold_transaction" columnName="created_date" columnDataType="DATETIME"/>
</changeSet>
</databaseChangeLog>
@@ -54,7 +54,7 @@ public class CustomAuditingHandlerTest {
CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext)); CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext));
AbstractAuditableWithUTCDateTimeCustom targetObject = Mockito.spy(AbstractAuditableWithUTCDateTimeCustom.class); AbstractAuditableWithUTCDateTimeCustom targetObject = Mockito.spy(AbstractAuditableWithUTCDateTimeCustom.class);
targetObject = testInstance.markCreated(targetObject); targetObject = testInstance.markCreated(targetObject);
OffsetDateTime now = OffsetDateTime.now(DateUtils.getDateTimeZoneOfTenant()); OffsetDateTime now = DateUtils.getAuditOffsetDateTime();
assertTrue(targetObject.getCreatedDate().isPresent()); assertTrue(targetObject.getCreatedDate().isPresent());
assertEquals(now.getYear(), targetObject.getCreatedDate().get().getYear()); assertEquals(now.getYear(), targetObject.getCreatedDate().get().getYear());
@@ -70,7 +70,7 @@ public class CustomAuditingHandlerTest {
CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext)); CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext));
AbstractAuditableWithUTCDateTimeCustom targetObject = Mockito.spy(AbstractAuditableWithUTCDateTimeCustom.class); AbstractAuditableWithUTCDateTimeCustom targetObject = Mockito.spy(AbstractAuditableWithUTCDateTimeCustom.class);
targetObject = testInstance.markModified(targetObject); targetObject = testInstance.markModified(targetObject);
OffsetDateTime now = OffsetDateTime.now(DateUtils.getDateTimeZoneOfTenant()); OffsetDateTime now = DateUtils.getAuditOffsetDateTime();
assertTrue(targetObject.getLastModifiedDate().isPresent()); assertTrue(targetObject.getLastModifiedDate().isPresent());
assertEquals(now.getYear(), targetObject.getLastModifiedDate().get().getYear()); assertEquals(now.getYear(), targetObject.getLastModifiedDate().get().getYear());
@@ -86,7 +86,7 @@ public class CustomAuditingHandlerTest {
CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext)); CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext));
AbstractAuditableCustom targetObject = Mockito.spy(AbstractAuditableCustom.class); AbstractAuditableCustom targetObject = Mockito.spy(AbstractAuditableCustom.class);
targetObject = testInstance.markModified(targetObject); targetObject = testInstance.markModified(targetObject);
LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault()); LocalDateTime now = DateUtils.getLocalDateTimeOfSystem();
assertTrue(targetObject.getLastModifiedDate().isPresent()); assertTrue(targetObject.getLastModifiedDate().isPresent());
assertEquals(now.getYear(), targetObject.getLastModifiedDate().get().getYear()); assertEquals(now.getYear(), targetObject.getLastModifiedDate().get().getYear());
@@ -102,7 +102,7 @@ public class CustomAuditingHandlerTest {
CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext)); CustomAuditingHandler testInstance = new CustomAuditingHandler(PersistentEntities.of(mappingContext));
AbstractAuditableCustom targetObject = Mockito.spy(AbstractAuditableCustom.class); AbstractAuditableCustom targetObject = Mockito.spy(AbstractAuditableCustom.class);
targetObject = testInstance.markCreated(targetObject); targetObject = testInstance.markCreated(targetObject);
LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault()); LocalDateTime now = DateUtils.getLocalDateTimeOfSystem();
assertTrue(targetObject.getCreatedDate().isPresent()); assertTrue(targetObject.getCreatedDate().isPresent());
assertEquals(now.getYear(), targetObject.getCreatedDate().get().getYear()); assertEquals(now.getYear(), targetObject.getCreatedDate().get().getYear());
@@ -23,7 +23,6 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime; import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalAccessor;
import java.util.Optional; import java.util.Optional;
import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant; import org.apache.fineract.infrastructure.core.domain.FineractPlatformTenant;
@@ -36,14 +35,13 @@ public class CustomDateTimeProviderTest {
@BeforeEach @BeforeEach
public void init() { public void init() {
ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null)); ThreadLocalContextUtil.setTenant(new FineractPlatformTenant(1L, "default", "Default", "Asia/Kolkata", null));
} }
@Test @Test
public void instanceDateProvider() { public void instanceDateProvider() {
Optional<TemporalAccessor> dateTimeProvider = CustomDateTimeProvider.INSTANCE.getNow(); Optional<TemporalAccessor> dateTimeProvider = CustomDateTimeProvider.INSTANCE.getNow();
LocalDateTime now = LocalDateTime.now(ZoneId.systemDefault()); LocalDateTime now = DateUtils.getLocalDateTimeOfSystem();
assertTrue(dateTimeProvider.isPresent()); assertTrue(dateTimeProvider.isPresent());
assertTrue(dateTimeProvider.get() instanceof LocalDateTime); assertTrue(dateTimeProvider.get() instanceof LocalDateTime);
@@ -56,8 +54,8 @@ public class CustomDateTimeProviderTest {
@Test @Test
public void tenantDateProvider() { public void tenantDateProvider() {
Optional<TemporalAccessor> dateTimeProvider = CustomDateTimeProvider.TENANT.getNow(); Optional<TemporalAccessor> dateTimeProvider = CustomDateTimeProvider.UTC.getNow();
OffsetDateTime now = OffsetDateTime.now(DateUtils.getDateTimeZoneOfTenant()); OffsetDateTime now = DateUtils.getAuditOffsetDateTime();
assertTrue(dateTimeProvider.isPresent()); assertTrue(dateTimeProvider.isPresent());
assertTrue(dateTimeProvider.get() instanceof OffsetDateTime); assertTrue(dateTimeProvider.get() instanceof OffsetDateTime);
@@ -56,22 +56,22 @@ public class DateUtilsTest {
@Test @Test
public void getLocalDateOfTenant() { public void getLocalDateOfTenant() {
assertEquals(LocalDate.now(ZoneId.of("Asia/Kolkata")), DateUtils.getLocalDateOfTenant()); assertTrue(DateUtils.isEqualTenantDate(LocalDate.now(ZoneId.of("Asia/Kolkata"))));
} }
@Test @Test
public void getLocalDateTimeOfTenant() { public void getLocalDateTimeOfTenant() {
assertEquals(LocalDateTime.now(ZoneId.of("Asia/Kolkata")).truncatedTo(ChronoUnit.SECONDS), DateUtils.getLocalDateTimeOfTenant()); assertTrue(DateUtils.isEqualTenantDateTime(LocalDateTime.now(ZoneId.of("Asia/Kolkata")), ChronoUnit.SECONDS));
} }
@Test @Test
public void getOffsetDateTimeOfTenant() { public void getOffsetDateTimeOfTenant() {
assertEquals(OffsetDateTime.now(ZoneId.of("Asia/Kolkata")).truncatedTo(ChronoUnit.SECONDS), DateUtils.getOffsetDateTimeOfTenant()); assertTrue(DateUtils.isEqualTenantDateTime(OffsetDateTime.now(ZoneId.of("Asia/Kolkata")), ChronoUnit.SECONDS));
} }
@Test @Test
public void getLocalDateTimeOfSystem() { public void getLocalDateTimeOfSystem() {
assertEquals(LocalDateTime.now(ZoneId.systemDefault()).truncatedTo(ChronoUnit.SECONDS), DateUtils.getLocalDateTimeOfSystem()); assertTrue(DateUtils.isEqualSystemDateTime(LocalDateTime.now(ZoneId.systemDefault()), ChronoUnit.SECONDS));
} }
@Test @Test
@@ -83,6 +83,6 @@ public class DateUtilsTest {
@Test @Test
public void getBusinesLocalDate() { public void getBusinesLocalDate() {
assertEquals(LocalDate.of(2022, 6, 12), DateUtils.getBusinessLocalDate()); assertTrue(DateUtils.isEqualBusinessDate(LocalDate.of(2022, 6, 12)));
} }
} }
@@ -3008,7 +3008,7 @@ public class ClientSavingsIntegrationTest {
this.savingsProductHelper = new SavingsProductHelper(); this.savingsProductHelper = new SavingsProductHelper();
this.scheduleJobHelper = new SchedulerJobHelper(requestSpec); this.scheduleJobHelper = new SchedulerJobHelper(requestSpec);
configurationForBackdatedTransaction(); configurationForBackdatedTransaction();
LocalDate transactionDate = LocalDate.now(Utils.getZoneIdOfTenant()).minusDays(5); LocalDate transactionDate = Utils.getLocalDateOfTenant().minusDays(5);
final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy"); final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd MMMM yyyy");
String startDate = formatter.format(transactionDate); String startDate = formatter.format(transactionDate);
String secondTrx = formatter.format(transactionDate.plusDays(1)); String secondTrx = formatter.format(transactionDate.plusDays(1));
@@ -30,7 +30,6 @@ import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.math.BigDecimal; import java.math.BigDecimal;
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.Locale; import java.util.Locale;
@@ -162,7 +161,7 @@ public class SavingsAccountTransactionsSearchIntegrationTest {
@Test @Test
public void testSavingsTransactionsSearchSubmittedDateFromTo() throws JsonProcessingException { public void testSavingsTransactionsSearchSubmittedDateFromTo() throws JsonProcessingException {
LocalDate businessDate = LocalDate.now(ZoneId.systemDefault()); LocalDate businessDate = Utils.getLocalDateOfTenant();
final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate); final Integer clientID = ClientHelper.createClient(this.requestSpec, this.responseSpec, startDate);
Assertions.assertNotNull(clientID); Assertions.assertNotNull(clientID);
final Integer savingsId = createSavingsAccountDailyPosting(clientID, startDate); final Integer savingsId = createSavingsAccountDailyPosting(clientID, startDate);
@@ -18,13 +18,13 @@
*/ */
package org.apache.fineract.integrationtests; package org.apache.fineract.integrationtests;
import java.text.SimpleDateFormat; import java.time.format.DateTimeFormatter;
import java.util.Date;
import org.apache.fineract.client.models.PostSavingsAccountsAccountIdRequest; import org.apache.fineract.client.models.PostSavingsAccountsAccountIdRequest;
import org.apache.fineract.client.models.PostSavingsAccountsAccountIdResponse; import org.apache.fineract.client.models.PostSavingsAccountsAccountIdResponse;
import org.apache.fineract.client.models.PostSavingsAccountsRequest; import org.apache.fineract.client.models.PostSavingsAccountsRequest;
import org.apache.fineract.client.models.PostSavingsAccountsResponse; import org.apache.fineract.client.models.PostSavingsAccountsResponse;
import org.apache.fineract.integrationtests.client.IntegrationTest; import org.apache.fineract.integrationtests.client.IntegrationTest;
import org.apache.fineract.integrationtests.common.Utils;
import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -42,8 +42,8 @@ public class SavingsAccountsTest extends IntegrationTest {
private static final Logger LOG = LoggerFactory.getLogger(SavingsAccountsTest.class); private static final Logger LOG = LoggerFactory.getLogger(SavingsAccountsTest.class);
private final String dateFormat = "dd MMMM yyyy"; private final String dateFormat = "dd MMMM yyyy";
private final String locale = "en"; private final String locale = "en";
private final SimpleDateFormat simpleDateFormat = new SimpleDateFormat(dateFormat); private final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern(dateFormat);
private final String formattedDate = simpleDateFormat.format(new Date()); private final String formattedDate = dateFormatter.format(Utils.getLocalDateOfTenant());
private int savingId = 1; private int savingId = 1;
@Test @Test
@@ -27,13 +27,13 @@ import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.integrationtests.common.BusinessDateHelper; import org.apache.fineract.integrationtests.common.BusinessDateHelper;
import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.CommonConstants; import org.apache.fineract.integrationtests.common.CommonConstants;
@@ -72,7 +72,7 @@ public class SavingsInterestPostingIntegrationTest {
@Test @Test
public void testSavingsDailyInterestPosting() { public void testSavingsDailyInterestPosting() {
LocalDate today = LocalDate.now(ZoneId.systemDefault()); LocalDate today = Utils.getLocalDateOfTenant();
try { try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, true); GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, true);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, today); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, today);
@@ -100,7 +100,7 @@ public class SavingsInterestPostingIntegrationTest {
List<Integer> submittedOnDateStringList = (List<Integer>) interestPostingTransaction.get("submittedOnDate"); List<Integer> submittedOnDateStringList = (List<Integer>) interestPostingTransaction.get("submittedOnDate");
LocalDate submittedOnDate = submittedOnDateStringList.stream().collect( LocalDate submittedOnDate = submittedOnDateStringList.stream().collect(
Collectors.collectingAndThen(Collectors.toList(), list -> LocalDate.of(list.get(0), list.get(1), list.get(2)))); Collectors.collectingAndThen(Collectors.toList(), list -> LocalDate.of(list.get(0), list.get(1), list.get(2))));
assertTrue(submittedOnDate.compareTo(today) == 0, "Submitted On Date check for Interest Posting transaction"); assertTrue(DateUtils.isEqual(submittedOnDate, today), "Submitted On Date check for Interest Posting transaction");
} finally { } finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, false); GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, false);
} }
@@ -27,13 +27,13 @@ import io.restassured.http.ContentType;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType; import org.apache.fineract.infrastructure.businessdate.domain.BusinessDateType;
import org.apache.fineract.infrastructure.core.service.DateUtils;
import org.apache.fineract.integrationtests.common.BusinessDateHelper; import org.apache.fineract.integrationtests.common.BusinessDateHelper;
import org.apache.fineract.integrationtests.common.ClientHelper; import org.apache.fineract.integrationtests.common.ClientHelper;
import org.apache.fineract.integrationtests.common.CommonConstants; import org.apache.fineract.integrationtests.common.CommonConstants;
@@ -149,7 +149,7 @@ public class SavingsInterestPostingJobIntegrationTest {
@Test @Test
public void testSavingsDailyInterestPostingJob() { public void testSavingsDailyInterestPostingJob() {
LocalDate today = LocalDate.now(ZoneId.systemDefault()); LocalDate today = Utils.getLocalDateOfTenant();
try { try {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, true); GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, true);
BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, today); BusinessDateHelper.updateBusinessDate(requestSpec, responseSpec, BusinessDateType.BUSINESS_DATE, today);
@@ -179,7 +179,7 @@ public class SavingsInterestPostingJobIntegrationTest {
List<Integer> submittedOnDateStringList = (List<Integer>) interestPostingTransaction.get("submittedOnDate"); List<Integer> submittedOnDateStringList = (List<Integer>) interestPostingTransaction.get("submittedOnDate");
LocalDate submittedOnDate = submittedOnDateStringList.stream().collect( LocalDate submittedOnDate = submittedOnDateStringList.stream().collect(
Collectors.collectingAndThen(Collectors.toList(), list -> LocalDate.of(list.get(0), list.get(1), list.get(2)))); Collectors.collectingAndThen(Collectors.toList(), list -> LocalDate.of(list.get(0), list.get(1), list.get(2))));
assertTrue(submittedOnDate.compareTo(today) == 0, "Submitted On Date check for Interest Posting transaction"); assertTrue(DateUtils.isEqual(submittedOnDate, today), "Submitted On Date check for Interest Posting transaction");
} finally { } finally {
GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, false); GlobalConfigurationHelper.updateIsBusinessDateEnabled(requestSpec, responseSpec, false);
} }
@@ -198,9 +198,7 @@ public class SavingsInterestPostingJobIntegrationTest {
this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "10000", startDate, CommonConstants.RESPONSE_RESOURCE_ID); this.savingsAccountHelper.withdrawalFromSavingsAccount(savingsId, "10000", startDate, CommonConstants.RESPONSE_RESOURCE_ID);
/*** // Runs Post interest posting job and verify the new account created with Overdraft is posting negative interest
* Runs Post interest posting job and verify the new account created with Overdraft is posting negative interest
*/
this.scheduleJobHelper.executeAndAwaitJob(jobName); this.scheduleJobHelper.executeAndAwaitJob(jobName);
Object transactionObj = this.savingsAccountHelper.getSavingsDetails(savingsId, "transactions"); Object transactionObj = this.savingsAccountHelper.getSavingsDetails(savingsId, "transactions");
ArrayList<HashMap<String, Object>> transactions = (ArrayList<HashMap<String, Object>>) transactionObj; ArrayList<HashMap<String, Object>> transactions = (ArrayList<HashMap<String, Object>>) transactionObj;
@@ -37,7 +37,6 @@ import java.text.SimpleDateFormat;
import java.time.Duration; import java.time.Duration;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalTime; import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime; import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
@@ -179,7 +178,7 @@ public class SchedulerJobsTestResults {
ArrayList<Integer> annualFeeDueDateAsArrayList = (ArrayList<Integer>) annualFeeDetails.get("dueDate"); ArrayList<Integer> annualFeeDueDateAsArrayList = (ArrayList<Integer>) annualFeeDetails.get("dueDate");
LocalDate nextDueDateForAnnualFee = LocalDate.of(annualFeeDueDateAsArrayList.get(0), annualFeeDueDateAsArrayList.get(1), LocalDate nextDueDateForAnnualFee = LocalDate.of(annualFeeDueDateAsArrayList.get(0), annualFeeDueDateAsArrayList.get(1),
annualFeeDueDateAsArrayList.get(2)); annualFeeDueDateAsArrayList.get(2));
LocalDate todaysDate = LocalDate.now(ZoneId.of("Asia/Kolkata")); LocalDate todaysDate = Utils.getLocalDateOfTenant();
Truth.assertWithMessage("Verifying that all due Annual Fees have been paid").that(nextDueDateForAnnualFee) Truth.assertWithMessage("Verifying that all due Annual Fees have been paid").that(nextDueDateForAnnualFee)
.isGreaterThan(todaysDate); .isGreaterThan(todaysDate);
@@ -42,7 +42,9 @@ import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeFormatterBuilder;
import java.time.temporal.ChronoField; import java.time.temporal.ChronoField;
@@ -374,6 +376,14 @@ public final class Utils {
return dateFormat.format(dateToBeConvert.getTime()); return dateFormat.format(dateToBeConvert.getTime());
} }
public static OffsetDateTime getAuditOffsetDateTime() {
return OffsetDateTime.now(ZoneOffset.UTC);
}
public static LocalDateTime getLocalDateTimeOfSystem() {
return LocalDateTime.now(ZoneId.systemDefault());
}
public static TimeZone getTimeZoneOfTenant() { public static TimeZone getTimeZoneOfTenant() {
return TimeZone.getTimeZone(TENANT_TIME_ZONE); return TimeZone.getTimeZone(TENANT_TIME_ZONE);
} }
@@ -386,6 +396,10 @@ public final class Utils {
return LocalDate.now(getZoneIdOfTenant()); return LocalDate.now(getZoneIdOfTenant());
} }
public static LocalDateTime getLocalDateTimeOfTenant() {
return LocalDateTime.now(getZoneIdOfTenant());
}
public static Date convertJsonElementAsDate(JsonElement jsonElement) { public static Date convertJsonElementAsDate(JsonElement jsonElement) {
if (jsonElement.isJsonArray()) { if (jsonElement.isJsonArray()) {
JsonArray jsonArray = jsonElement.getAsJsonArray(); JsonArray jsonArray = jsonElement.getAsJsonArray();
@@ -25,7 +25,6 @@ import com.google.gson.Gson;
import io.restassured.path.json.JsonPath; import io.restassured.path.json.JsonPath;
import io.restassured.specification.RequestSpecification; import io.restassured.specification.RequestSpecification;
import io.restassured.specification.ResponseSpecification; import io.restassured.specification.ResponseSpecification;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@@ -108,7 +107,7 @@ public class CampaignsHelper {
map.put("triggerType", triggerType); map.put("triggerType", triggerType);
if (2 == triggerType) { if (2 == triggerType) {
map.put("recurrenceStartDate", map.put("recurrenceStartDate",
LocalDateTime.now(Utils.getZoneIdOfTenant()).plusMinutes(1).format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT))); Utils.getLocalDateTimeOfTenant().plusMinutes(1).format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
map.put("frequency", 1); map.put("frequency", 1);
map.put("interval", "1"); map.put("interval", "1");
} }
@@ -136,7 +135,7 @@ public class CampaignsHelper {
map.put("triggerType", triggerType); map.put("triggerType", triggerType);
if (2 == triggerType) { if (2 == triggerType) {
map.put("recurrenceStartDate", map.put("recurrenceStartDate",
LocalDateTime.now(Utils.getZoneIdOfTenant()).plusMinutes(1).format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT))); Utils.getLocalDateTimeOfTenant().plusMinutes(1).format(DateTimeFormatter.ofPattern(DATE_TIME_FORMAT)));
} }
map.put("campaignName", Utils.randomStringGenerator("Campaign_Name_", 5)); map.put("campaignName", Utils.randomStringGenerator("Campaign_Name_", 5));
map.put("campaignType", 1); map.put("campaignType", 1);