blob: 2cd1c7ddec02af02a36e5a8a342f61eea6d0eeac [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.fineract.portfolio.savings.domain;
import static org.apache.fineract.infrastructure.core.service.DateUtils.getSystemZoneId;
import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.apache.fineract.infrastructure.core.domain.AbstractAuditableWithUTCDateTimeCustom;
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.Money;
import org.apache.fineract.organisation.office.domain.Office;
import org.apache.fineract.portfolio.paymentdetail.domain.PaymentDetail;
import org.apache.fineract.portfolio.savings.SavingsAccountTransactionType;
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionEnumData;
import org.apache.fineract.portfolio.savings.domain.interest.EndOfDayBalance;
import org.apache.fineract.portfolio.savings.domain.interest.SavingsAccountTransactionDetailsForPostingPeriod;
import org.apache.fineract.portfolio.savings.service.SavingsEnumerations;
import org.apache.fineract.portfolio.tax.domain.TaxComponent;
import org.springframework.util.CollectionUtils;
/**
* All monetary transactions against a savings account are modelled through this entity.
*/
@Entity
@Table(name = "m_savings_account_transaction")
public final class SavingsAccountTransaction extends AbstractAuditableWithUTCDateTimeCustom {
@ManyToOne(optional = false)
@JoinColumn(name = "savings_account_id", referencedColumnName = "id", nullable = false)
private SavingsAccount savingsAccount;
@ManyToOne
@JoinColumn(name = "office_id", nullable = false)
private Office office;
@ManyToOne(cascade = CascadeType.ALL, optional = true)
@JoinColumn(name = "payment_detail_id", nullable = true)
private PaymentDetail paymentDetail;
@Column(name = "transaction_type_enum", nullable = false)
private Integer typeOf;
@Column(name = "transaction_date", nullable = false)
private LocalDate dateOf;
@Column(name = "amount", scale = 6, precision = 19, nullable = false)
private BigDecimal amount;
@Column(name = "is_reversed", nullable = false)
private boolean reversed;
@Column(name = "running_balance_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal runningBalance;
@Column(name = "cumulative_balance_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal cumulativeBalance;
@Column(name = "balance_end_date_derived", nullable = true)
private LocalDate balanceEndDate;
@Column(name = "balance_number_of_days_derived", nullable = true)
private Integer balanceNumberOfDays;
@OneToMany(cascade = CascadeType.ALL, mappedBy = "savingsAccountTransaction", orphanRemoval = true, fetch = FetchType.EAGER)
private Set<SavingsAccountChargePaidBy> savingsAccountChargesPaid = new HashSet<>();
@Column(name = "overdraft_amount_derived", scale = 6, precision = 19, nullable = true)
private BigDecimal overdraftAmount;
@Deprecated
@Column(name = "created_date", nullable = true)
private LocalDateTime createdDateToRemove;
@Column(name = "submitted_on_date", nullable = false)
private LocalDate submittedOnDate;
@Column(name = "is_manual", length = 1, nullable = true)
private boolean isManualTransaction;
@Column(name = "is_loan_disbursement", length = 1, nullable = true)
private boolean isLoanDisbursement;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER, mappedBy = "savingsAccountTransaction")
private List<SavingsAccountTransactionTaxDetails> taxDetails = new ArrayList<>();
@Column(name = "release_id_of_hold_amount", length = 20)
private Long releaseIdOfHoldAmountTransaction;
@Column(name = "reason_for_block", nullable = true)
private String reasonForBlock;
@Column(name = "is_reversal", nullable = false)
private boolean reversalTransaction;
@Column(name = "original_transaction_id")
private Long originalTxnId;
@Column(name = "is_lien_transaction")
private Boolean lienTransaction;
@Column(name = "ref_no", nullable = true)
private String refNo;
SavingsAccountTransaction() {}
private SavingsAccountTransaction(final SavingsAccount savingsAccount, final Office office, final PaymentDetail paymentDetail,
final Integer typeOf, final LocalDate transactionLocalDate, final BigDecimal amount, final boolean isReversed,
final boolean isManualTransaction, final Boolean lienTransaction, final String refNo) {
this.savingsAccount = savingsAccount;
this.office = office;
this.typeOf = typeOf;
this.dateOf = transactionLocalDate;
this.amount = amount;
this.reversed = isReversed;
this.paymentDetail = paymentDetail;
this.createdDateToRemove = null; // #audit backward compatibility deprecated
this.submittedOnDate = DateUtils.getBusinessLocalDate();
this.isManualTransaction = isManualTransaction;
this.lienTransaction = lienTransaction;
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,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.DEPOSIT.getValue(), date,
amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction deposit(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount,
final SavingsAccountTransactionType savingsAccountTransactionType, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, savingsAccountTransactionType.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawal(final SavingsAccount savingsAccount, final Office office,
final PaymentDetail paymentDetail, final LocalDate date, final Money amount, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail, SavingsAccountTransactionType.WITHDRAWAL.getValue(),
date, amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction interestPosting(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.INTEREST_POSTING.getValue(), date,
amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction overdraftInterest(final SavingsAccount savingsAccount, final Office office,
final LocalDate date, final Money amount, final boolean isManualTransaction) {
final boolean isReversed = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.OVERDRAFT_INTEREST.getValue(), date,
amount, isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawalFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final String refNo) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WITHDRAWAL_FEE.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction annualFee(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.ANNUAL_FEE.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction charge(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.PAY_CHARGE.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction waiver(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, SavingsAccountTransactionType.WAIVE_CHARGES.getValue(), date, amount,
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction initiateTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.INITIATE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction approveTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.APPROVE_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withdrawTransfer(final SavingsAccount savingsAccount, final Office office,
final LocalDate date) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, office, paymentDetail,
SavingsAccountTransactionType.WITHDRAW_TRANSFER.getValue(), date, savingsAccount.getSummary().getAccountBalance(),
isReversed, isManualTransaction, lienTransaction, refNo);
}
public static SavingsAccountTransaction withHoldTax(final SavingsAccount savingsAccount, final Office office, final LocalDate date,
final Money amount, final Map<TaxComponent, BigDecimal> taxDetails) {
final boolean isReversed = false;
final boolean isManualTransaction = false;
final Boolean lienTransaction = false;
final String refNo = null;
SavingsAccountTransaction accountTransaction = new SavingsAccountTransaction(savingsAccount, office,
SavingsAccountTransactionType.WITHHOLD_TAX.getValue(), date, amount, isReversed, isManualTransaction, lienTransaction,
refNo);
updateTaxDetails(taxDetails, accountTransaction);
return accountTransaction;
}
public static SavingsAccountTransaction escheat(final SavingsAccount savingsAccount, final LocalDate date,
final boolean accountTransaction) {
final boolean isReversed = false;
final PaymentDetail paymentDetail = null;
final Boolean lienTransaction = false;
final String refNo = null;
return new SavingsAccountTransaction(savingsAccount, savingsAccount.office(), paymentDetail,
SavingsAccountTransactionType.ESCHEAT.getValue(), date, savingsAccount.getSummary().getAccountBalance(), isReversed,
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,
final SavingsAccountTransaction accountTransaction) {
if (taxDetails != null) {
for (Map.Entry<TaxComponent, BigDecimal> mapEntry : taxDetails.entrySet()) {
accountTransaction.getTaxDetails()
.add(new SavingsAccountTransactionTaxDetails(accountTransaction, mapEntry.getKey(), mapEntry.getValue()));
}
}
}
public SavingsAccount getSavingsAccount() {
return this.savingsAccount;
}
public LocalDate getTransactionDate() {
return this.dateOf;
}
public LocalDate getEndOfBalanceDate() {
return balanceEndDate;
}
public boolean isReversed() {
return this.reversed;
}
public void reverse() {
this.reversed = true;
}
public BigDecimal getAmount() {
return this.amount;
}
public Money getAmount(final MonetaryCurrency currency) {
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) {
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 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() {
return getTransactionType().isDeposit();
}
public boolean isDepositAndNotReversed() {
return getTransactionType().isDeposit() && isNotReversed();
}
public boolean isDividendPayout() {
return getTransactionType().isDividendPayout();
}
public boolean isDividendPayoutAndNotReversed() {
return getTransactionType().isDividendPayout() && isNotReversed();
}
public boolean isWithdrawal() {
return getTransactionType().isWithdrawal();
}
public boolean isPostInterestCalculationRequired() {
return this.isDeposit() || this.isWithdrawal() || this.isChargeTransaction() || this.isDividendPayout() || this.isInterestPosting();
}
public boolean isInterestPostingAndNotReversed() {
return getTransactionType().isInterestPosting() && isNotReversed();
}
public boolean isInterestPosting() {
return getTransactionType().isInterestPosting() || getTransactionType().isOverDraftInterestPosting();
}
public boolean isWithdrawalFeeAndNotReversed() {
return getTransactionType().isWithdrawalFee() && isNotReversed();
}
public boolean isWithdrawalFee() {
return getTransactionType().isWithdrawalFee();
}
public boolean isAnnualFeeAndNotReversed() {
return isAnnualFee() && isNotReversed();
}
public boolean isAnnualFee() {
return getTransactionType().isAnnualFee();
}
public boolean isNotReversed() {
return !isReversed();
}
public boolean isTransferInitiation() {
return getTransactionType().isTransferInitiation();
}
public boolean isTransferApproval() {
return getTransactionType().isTransferApproval();
}
public boolean isTransferRejection() {
return getTransactionType().isTransferRejection();
}
public boolean isTransferWithdrawal() {
return getTransactionType().isTransferWithdrawal();
}
public boolean isTransferRelatedTransaction() {
return isTransferInitiation() || isTransferApproval() || isTransferRejection() || isTransferWithdrawal();
}
public void zeroBalanceFields() {
this.runningBalance = null;
this.cumulativeBalance = null;
this.balanceEndDate = null;
this.balanceNumberOfDays = null;
}
public void updateCumulativeBalanceAndDates(final MonetaryCurrency currency, final LocalDate endOfBalanceDate) {
// balance end date should not be before transaction date
if (endOfBalanceDate != null && DateUtils.isBefore(endOfBalanceDate, this.getTransactionDate())) {
this.balanceEndDate = this.getTransactionDate();
} else {
this.balanceEndDate = endOfBalanceDate;
}
this.balanceNumberOfDays = LocalDateInterval.create(getTransactionDate(), endOfBalanceDate).daysInPeriodInclusiveOfEndDate();
this.cumulativeBalance = Money.of(currency, this.runningBalance).multipliedBy(this.balanceNumberOfDays).getAmount();
}
public boolean isAcceptableForDailyBalance(final LocalDateInterval interestPeriodInterval) {
return isNotReversed() && interestPeriodInterval.contains(getTransactionDate()) && isABalanceForAtLeastOneDay();
}
private boolean isABalanceForAtLeastOneDay() {
return this.balanceNumberOfDays != null && this.balanceNumberOfDays > 0;
}
public boolean hasNotAmount(final Money amountToCheck) {
final Money transactionAmount = getAmount(amountToCheck.getCurrency());
return transactionAmount.isNotEqualTo(amountToCheck);
}
public Map<String, Object> toMapData(final String currencyCode) {
final Map<String, Object> thisTransactionData = new LinkedHashMap<>();
final SavingsAccountTransactionEnumData transactionType = SavingsEnumerations.transactionType(this.typeOf);
thisTransactionData.put("id", getId());
thisTransactionData.put("officeId", this.office.getId());
thisTransactionData.put("type", transactionType);
thisTransactionData.put("reversed", isReversed());
thisTransactionData.put("date", getTransactionDate());
thisTransactionData.put("currencyCode", currencyCode);
thisTransactionData.put("amount", this.amount);
thisTransactionData.put("overdraftAmount", this.overdraftAmount);
if (this.paymentDetail != null) {
thisTransactionData.put("paymentTypeId", this.paymentDetail.getPaymentType().getId());
}
/***
* 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)
***/
if (!this.savingsAccountChargesPaid.isEmpty()) {
final List<Map<String, Object>> savingsChargesPaidData = new ArrayList<>();
for (final SavingsAccountChargePaidBy chargePaidBy : this.savingsAccountChargesPaid) {
final Map<String, Object> savingChargePaidData = new LinkedHashMap<>();
savingChargePaidData.put("chargeId", chargePaidBy.getSavingsAccountCharge().getCharge().getId());
savingChargePaidData.put("isPenalty", chargePaidBy.getSavingsAccountCharge().getCharge().isPenalty());
savingChargePaidData.put("savingsChargeId", chargePaidBy.getSavingsAccountCharge().getId());
savingChargePaidData.put("amount", chargePaidBy.getAmount());
savingsChargesPaidData.add(savingChargePaidData);
}
thisTransactionData.put("savingsChargesPaid", savingsChargesPaidData);
}
if (!this.taxDetails.isEmpty()) {
final List<Map<String, Object>> taxData = new ArrayList<>();
for (final SavingsAccountTransactionTaxDetails taxDetails : this.taxDetails) {
final Map<String, Object> taxDetailsData = new HashMap<>();
taxDetailsData.put("amount", taxDetails.getAmount());
if (taxDetails.getTaxComponent().getCreditAcount() != null) {
taxDetailsData.put("creditAccountId", taxDetails.getTaxComponent().getCreditAcount().getId());
}
taxData.add(taxDetailsData);
}
thisTransactionData.put("taxDetails", taxData);
}
return thisTransactionData;
}
public boolean isBefore(final LocalDate date) {
return DateUtils.isBefore(getTransactionDate(), date);
}
public boolean isAfter(final LocalDate date) {
return DateUtils.isAfter(getTransactionDate(), date);
}
public boolean occursOn(final LocalDate date) {
return DateUtils.isEqual(getTransactionDate(), date);
}
public EndOfDayBalance toEndOfDayBalance(final LocalDateInterval periodInterval, final MonetaryCurrency currency) {
final Money endOfDayBalance = Money.of(currency, this.runningBalance);
final Money openingBalance = endOfDayBalance;
LocalDate balanceDate = periodInterval.startDate();
int numberOfDays = periodInterval.daysInPeriodInclusiveOfEndDate();
if (periodInterval.contains(getTransactionDate())) {
balanceDate = getTransactionDate();
final LocalDateInterval newInterval = LocalDateInterval.create(getTransactionDate(), periodInterval.endDate());
numberOfDays = newInterval.daysInPeriodInclusiveOfEndDate();
}
return EndOfDayBalance.from(balanceDate, openingBalance, endOfDayBalance, numberOfDays);
}
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance, final LocalDate nextTransactionDate) {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit() || isDividendPayoutAndNotReversed()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
}
int numberOfDays = LocalDateInterval.create(getTransactionDate(), nextTransactionDate).daysInPeriodInclusiveOfEndDate();
if (!openingBalance.isEqualTo(endOfDayBalance) && numberOfDays > 1) {
numberOfDays = numberOfDays - 1;
}
return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance, numberOfDays);
}
public EndOfDayBalance toEndOfDayBalance(final Money openingBalance) {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
if (isDeposit() || isDividendPayoutAndNotReversed()) {
endOfDayBalance = openingBalance.plus(getAmount(currency));
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
if (openingBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) {
endOfDayBalance = openingBalance.minus(getAmount(currency));
} else {
endOfDayBalance = getRunningBalance(currency);
}
}
return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays);
}
public EndOfDayBalance toEndOfDayBalanceBoundedBy(final Money openingBalance, final LocalDateInterval boundedBy) {
final MonetaryCurrency currency = openingBalance.getCurrency();
Money endOfDayBalance = openingBalance.copy();
int numberOfDaysOfBalance = this.balanceNumberOfDays;
LocalDate balanceStartDate = getTransactionDate();
LocalDate balanceEndDate = getEndOfBalanceDate();
if (DateUtils.isBefore(balanceStartDate, boundedBy.startDate())) {
balanceStartDate = boundedBy.startDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
} else {
if (isDeposit() || isDividendPayoutAndNotReversed()) {
// endOfDayBalance = openingBalance.plus(getAmount(currency));
// if (endOfDayBalance.isLessThanZero()) {
endOfDayBalance = endOfDayBalance.plus(getAmount(currency));
// }
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
// endOfDayBalance = openingBalance.minus(getAmount(currency));
if (endOfDayBalance.isGreaterThanZero() || this.savingsAccount.allowOverdraft()) {
endOfDayBalance = endOfDayBalance.minus(getAmount(currency));
} else {
endOfDayBalance = getRunningBalance(currency);
}
}
}
if (DateUtils.isAfter(balanceEndDate, boundedBy.endDate())) {
balanceEndDate = boundedBy.endDate();
final LocalDateInterval spanOfBalance = LocalDateInterval.create(balanceStartDate, balanceEndDate);
numberOfDaysOfBalance = spanOfBalance.daysInPeriodInclusiveOfEndDate();
}
return EndOfDayBalance.from(balanceStartDate, openingBalance, endOfDayBalance, numberOfDaysOfBalance);
}
public boolean isBalanceInExistencesForOneDayOrMore() {
return this.balanceNumberOfDays != null && this.balanceNumberOfDays >= 1;
}
public boolean fallsWithin(final LocalDateInterval periodInterval) {
final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionDate(), getEndOfBalanceDate());
return periodInterval.contains(balanceInterval);
}
public boolean spansAnyPortionOf(final LocalDateInterval periodInterval) {
final LocalDateInterval balanceInterval = LocalDateInterval.create(getTransactionDate(), getEndOfBalanceDate());
return balanceInterval.containsPortionOf(periodInterval);
}
public boolean isIdentifiedBy(final Long transactionId) {
return getId().equals(transactionId);
}
public boolean isCredit() {
return isCreditType() && !isReversed() && !isReversalTransaction();
}
public boolean isCreditType() {
return getTransactionType().isCredit();
}
public boolean isDebit() {
return isDebitType() && !isReversed() && !isReversalTransaction();
}
public boolean isDebitType() {
return getTransactionType().isDebit();
}
public boolean isWithHoldTaxAndNotReversed() {
return getTransactionType().isWithHoldTax() && isNotReversed();
}
public boolean isOverdraftInterestAndNotReversed() {
return getTransactionType().isIncomeFromInterest() && isNotReversed();
}
public boolean isPayCharge() {
return getTransactionType().isPayCharge();
}
public boolean isChargeTransaction() {
return getTransactionType().isChargeTransaction();
}
public boolean isChargeTransactionAndNotReversed() {
return getTransactionType().isChargeTransaction() && isNotReversed();
}
public boolean isWaiveCharge() {
return getTransactionType().isWaiveCharge();
}
public boolean isAmountOnHold() {
return getTransactionType().isAmountOnHold();
}
public boolean isAmountRelease() {
return getTransactionType().isAmountRelease();
}
private boolean canOverriteSavingAccountRules() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isChargeTransaction() && chargePaidBy != null && chargePaidBy.canOverriteSavingAccountRules();
}
public boolean canProcessBalanceCheck() {
return isDebit() && !canOverriteSavingAccountRules();
}
public boolean isFeeCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isPayCharge() && chargePaidBy != null && chargePaidBy.isFeeCharge();
}
public boolean isPenaltyCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isPayCharge() && chargePaidBy != null && chargePaidBy.isPenaltyCharge();
}
public boolean isFeeChargeAndNotReversed() {
return isFeeCharge() && isNotReversed();
}
public boolean isPenaltyChargeAndNotReversed() {
return isPenaltyCharge() && isNotReversed();
}
public boolean isWaiveFeeCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isWaiveCharge() && chargePaidBy != null && chargePaidBy.isFeeCharge();
}
public boolean isWaivePenaltyCharge() {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return isWaiveCharge() && chargePaidBy != null && chargePaidBy.isPenaltyCharge();
}
public boolean isWaiveFeeChargeAndNotReversed() {
return isWaiveFeeCharge() && isNotReversed();
}
public boolean isWaivePenaltyChargeAndNotReversed() {
return isWaivePenaltyCharge() && isNotReversed();
}
private SavingsAccountChargePaidBy getSavingsAccountChargePaidBy() {
if (!CollectionUtils.isEmpty(this.savingsAccountChargesPaid)) {
return this.savingsAccountChargesPaid.iterator().next();
}
return null;
}
public boolean isPaymentForCurrentCharge(final SavingsAccountCharge savingsAccountCharge) {
final SavingsAccountChargePaidBy chargePaidBy = getSavingsAccountChargePaidBy();
return chargePaidBy != null && chargePaidBy.getSavingsAccountCharge().equals(savingsAccountCharge);
}
@Override
public Optional<OffsetDateTime> getCreatedDate() {
// #audit backward compatibility keep system datetime
return Optional.ofNullable(super.getCreatedDate()
.orElse(createdDateToRemove == null ? null : createdDateToRemove.atZone(getSystemZoneId()).toOffsetDateTime()));
}
public boolean isAmountOnHoldNotReleased() {
return (isAmountOnHold() && getReleaseIdOfHoldAmountTransaction() == null);
}
public SavingsAccountTransactionDetailsForPostingPeriod toSavingsAccountTransactionDetailsForPostingPeriod(MonetaryCurrency currency,
boolean isAllowOverDraft) {
return new SavingsAccountTransactionDetailsForPostingPeriod(getId(), this.dateOf, this.balanceEndDate, this.runningBalance,
this.amount, currency, this.balanceNumberOfDays, isDeposit(), isWithdrawal(), isAllowOverDraft,
isChargeTransactionAndNotReversed(), isDividendPayoutAndNotReversed());
}
}