FINERACT-2312: Post interest with adjustments for savings accounts
This commit is contained in:
committed by
Adam Saghy
parent
ef1e2e1095
commit
a6fa1adc99
+10
@@ -29,6 +29,7 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||
import org.apache.fineract.infrastructure.core.data.EnumOptionData;
|
||||
@@ -47,6 +48,7 @@ import org.apache.fineract.portfolio.tax.data.TaxGroupData;
|
||||
/**
|
||||
* Immutable data object representing a savings account.
|
||||
*/
|
||||
@Setter
|
||||
@Getter
|
||||
@JsonLocalDateArrayFormat
|
||||
public final class SavingsAccountData implements Serializable {
|
||||
@@ -144,6 +146,13 @@ public final class SavingsAccountData implements Serializable {
|
||||
private transient Long glAccountIdForSavingsControl;
|
||||
private transient Long glAccountIdForInterestOnSavings;
|
||||
|
||||
private Long glAccountIdForInterestPayable;
|
||||
private Long glAccountIdForOverdraftPorfolio;
|
||||
private Long glAccountIdForInterestReceivable;
|
||||
|
||||
private BigDecimal interestPosting;
|
||||
private BigDecimal overdraftPosting;
|
||||
|
||||
public static SavingsAccountData importInstanceIndividual(Long clientId, Long productId, Long fieldOfficerId, LocalDate submittedOnDate,
|
||||
BigDecimal nominalAnnualInterestRate, EnumOptionData interestCompoundingPeriodTypeEnum,
|
||||
EnumOptionData interestPostingPeriodTypeEnum, EnumOptionData interestCalculationTypeEnum,
|
||||
@@ -964,4 +973,5 @@ public final class SavingsAccountData implements Serializable {
|
||||
public boolean isIsDormancyTrackingActive() {
|
||||
return this.isDormancyTrackingActive;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+54
-17
@@ -105,6 +105,10 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
private BigDecimal overdraftAmount;
|
||||
private transient Long modifiedId;
|
||||
private transient String refNo;
|
||||
private Boolean isOverdraft;
|
||||
|
||||
private Long accountCredit;
|
||||
private Long accountDebit;
|
||||
|
||||
private SavingsAccountTransactionData(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
||||
final PaymentDetailData paymentDetailData, final Long savingsId, final String savingsAccountNo, final LocalDate transactionDate,
|
||||
@@ -112,7 +116,7 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
final boolean reversed, final AccountTransferData transfer, final Collection<PaymentTypeData> paymentTypeOptions,
|
||||
final LocalDate submittedOnDate, final boolean interestedPostedAsOn, final String submittedByUsername, final String note,
|
||||
final Boolean isReversal, final Long originalTransactionId, boolean isManualTransaction, final Boolean lienTransaction,
|
||||
final Long releaseTransactionId, final String reasonForBlock) {
|
||||
final Long releaseTransactionId, final String reasonForBlock, final Boolean isOverdraft) {
|
||||
this.id = id;
|
||||
this.transactionType = transactionType;
|
||||
TransactionEntryType entryType = null;
|
||||
@@ -146,6 +150,7 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
this.lienTransaction = lienTransaction;
|
||||
this.releaseTransactionId = releaseTransactionId;
|
||||
this.reasonForBlock = reasonForBlock;
|
||||
this.isOverdraft = isOverdraft;
|
||||
}
|
||||
|
||||
private static SavingsAccountTransactionData createData(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
||||
@@ -156,7 +161,7 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
final Boolean lienTransaction) {
|
||||
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, accountId, accountNo, date, currency, amount,
|
||||
outstandingChargeAmount, runningBalance, reversed, transfer, paymentTypeOptions, submittedOnDate, interestedPostedAsOn,
|
||||
submittedByUsername, note, null, null, false, lienTransaction, null, null);
|
||||
submittedByUsername, note, null, null, false, lienTransaction, null, null, false);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
||||
@@ -167,7 +172,8 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
final Boolean lienTransaction, final Long releaseTransactionId, final String reasonForBlock) {
|
||||
return new SavingsAccountTransactionData(id, transactionType, paymentDetailData, savingsId, savingsAccountNo, date, currency,
|
||||
amount, outstandingChargeAmount, runningBalance, reversed, transfer, null, submittedOnDate, interestedPostedAsOn,
|
||||
submittedByUsername, note, isReversal, originalTransactionId, false, lienTransaction, releaseTransactionId, reasonForBlock);
|
||||
submittedByUsername, note, isReversal, originalTransactionId, false, lienTransaction, releaseTransactionId, reasonForBlock,
|
||||
false);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransactionData create(final Long id, final SavingsAccountTransactionEnumData transactionType,
|
||||
@@ -235,10 +241,10 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
private static SavingsAccountTransactionData createImport(final SavingsAccountTransactionEnumData transactionType,
|
||||
final PaymentDetailData paymentDetailData, final Long savingsAccountId, final String accountNumber,
|
||||
final LocalDate transactionDate, final BigDecimal transactionAmount, final boolean reversed, final LocalDate submittedOnDate,
|
||||
boolean isManualTransaction, final Boolean lienTransaction) {
|
||||
boolean isManualTransaction, final Boolean lienTransaction, final Boolean isOverdraft) {
|
||||
SavingsAccountTransactionData data = new SavingsAccountTransactionData(null, transactionType, paymentDetailData, savingsAccountId,
|
||||
accountNumber, transactionDate, null, transactionAmount, null, null, reversed, null, null, submittedOnDate, false, null,
|
||||
null, null, null, isManualTransaction, lienTransaction, null, null);
|
||||
null, null, null, isManualTransaction, lienTransaction, null, null, isOverdraft);
|
||||
// duplicated import fields
|
||||
data.savingsAccountId = savingsAccountId;
|
||||
data.accountNumber = accountNumber;
|
||||
@@ -251,14 +257,14 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
return createImport(accountTransaction.getTransactionType(), accountTransaction.getPaymentDetailData(),
|
||||
accountTransaction.getSavingsAccountId(), null, accountTransaction.getTransactionDate(), accountTransaction.getAmount(),
|
||||
accountTransaction.isReversed(), accountTransaction.getSubmittedOnDate(), accountTransaction.isManualTransaction(),
|
||||
accountTransaction.getLienTransaction());
|
||||
accountTransaction.getLienTransaction(), false);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransactionData importInstance(BigDecimal transactionAmount, LocalDate transactionDate, Long paymentTypeId,
|
||||
String accountNumber, String checkNumber, String routingCode, String receiptNumber, String bankNumber, String note,
|
||||
Long savingsAccountId, SavingsAccountTransactionEnumData transactionType, Integer rowIndex, String locale, String dateFormat) {
|
||||
SavingsAccountTransactionData data = createImport(transactionType, null, savingsAccountId, accountNumber, transactionDate,
|
||||
transactionAmount, false, transactionDate, false, false);
|
||||
transactionAmount, false, transactionDate, false, false, false);
|
||||
data.rowIndex = rowIndex;
|
||||
data.paymentTypeId = paymentTypeId;
|
||||
data.checkNumber = checkNumber;
|
||||
@@ -272,10 +278,11 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
}
|
||||
|
||||
private static SavingsAccountTransactionData createImport(SavingsAccountTransactionEnumData transactionType, Long savingsAccountId,
|
||||
LocalDate transactionDate, BigDecimal transactionAmount, final LocalDate submittedOnDate, boolean isManualTransaction) {
|
||||
LocalDate transactionDate, BigDecimal transactionAmount, final LocalDate submittedOnDate, boolean isManualTransaction,
|
||||
Boolean isOverdraft) {
|
||||
// import transaction
|
||||
return createImport(transactionType, null, savingsAccountId, null, transactionDate, transactionAmount, false, submittedOnDate,
|
||||
isManualTransaction, false);
|
||||
isManualTransaction, false, isOverdraft);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransactionData interestPosting(final SavingsAccountData savingsAccount, final LocalDate date,
|
||||
@@ -285,17 +292,28 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
SavingsAccountTransactionEnumData transactionType = new SavingsAccountTransactionEnumData(
|
||||
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
|
||||
savingsAccountTransactionType.getValue().toString());
|
||||
return createImport(transactionType, savingsAccount.getId(), date, amount.getAmount(), submittedOnDate, isManualTransaction);
|
||||
return createImport(transactionType, savingsAccount.getId(), date, amount.getAmount(), submittedOnDate, isManualTransaction, false);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransactionData accrual(final SavingsAccountData savingsAccount, final LocalDate date, final Money amount,
|
||||
final boolean isManualTransaction) {
|
||||
final LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
|
||||
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.ACCRUAL;
|
||||
SavingsAccountTransactionEnumData transactionType = new SavingsAccountTransactionEnumData(
|
||||
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
|
||||
savingsAccountTransactionType.getValue().toString());
|
||||
return createImport(transactionType, savingsAccount.getId(), date, amount.getAmount(), submittedOnDate, isManualTransaction, false);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransactionData overdraftInterest(final SavingsAccountData savingsAccount, final LocalDate date,
|
||||
final Money amount, final boolean isManualTransaction) {
|
||||
final Money amount, final boolean isManualTransaction, final Boolean isOverdraft) {
|
||||
final LocalDate submittedOnDate = DateUtils.getBusinessLocalDate();
|
||||
final SavingsAccountTransactionType savingsAccountTransactionType = SavingsAccountTransactionType.OVERDRAFT_INTEREST;
|
||||
SavingsAccountTransactionEnumData transactionType = new SavingsAccountTransactionEnumData(
|
||||
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
|
||||
savingsAccountTransactionType.getValue().toString());
|
||||
return createImport(transactionType, savingsAccount.getId(), date, amount.getAmount(), submittedOnDate, isManualTransaction);
|
||||
return createImport(transactionType, savingsAccount.getId(), date, amount.getAmount(), submittedOnDate, isManualTransaction,
|
||||
isOverdraft);
|
||||
}
|
||||
|
||||
public static SavingsAccountTransactionData withHoldTax(final SavingsAccountData savingsAccount, final LocalDate date,
|
||||
@@ -306,7 +324,7 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
savingsAccountTransactionType.getValue().longValue(), savingsAccountTransactionType.getCode(),
|
||||
savingsAccountTransactionType.getValue().toString());
|
||||
SavingsAccountTransactionData accountTransaction = createImport(transactionType, savingsAccount.getId(), date, amount.getAmount(),
|
||||
submittedOnDate, false);
|
||||
submittedOnDate, false, false);
|
||||
accountTransaction.addTaxDetails(taxDetails);
|
||||
return accountTransaction;
|
||||
}
|
||||
@@ -387,10 +405,11 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
final MonetaryCurrency currency = openingBalance.getCurrency();
|
||||
Money endOfDayBalance = openingBalance.copy();
|
||||
if (isDeposit() || isDividendPayoutAndNotReversed()) {
|
||||
endOfDayBalance = openingBalance.plus(getAmount());
|
||||
endOfDayBalance = Money.of(currency, this.runningBalance);
|
||||
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
|
||||
|
||||
if (openingBalance.isGreaterThanZero()) {
|
||||
if (isWithdrawal()) {
|
||||
endOfDayBalance = Money.of(currency, this.runningBalance);
|
||||
} else if (openingBalance.isGreaterThanZero()) {
|
||||
endOfDayBalance = openingBalance.minus(getAmount());
|
||||
} else {
|
||||
endOfDayBalance = Money.of(currency, this.runningBalance);
|
||||
@@ -400,6 +419,14 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance, this.balanceNumberOfDays);
|
||||
}
|
||||
|
||||
public EndOfDayBalance toEndOfDayBalanceDates(final Money openingBalance, LocalDateInterval date) {
|
||||
final MonetaryCurrency currency = openingBalance.getCurrency();
|
||||
Money endOfDayBalance = Money.of(currency, this.runningBalance);
|
||||
|
||||
return EndOfDayBalance.from(getTransactionDate(), openingBalance, endOfDayBalance,
|
||||
this.balanceNumberOfDays != null ? this.balanceNumberOfDays : date.endDate().getDayOfMonth());
|
||||
}
|
||||
|
||||
public boolean isChargeTransactionAndNotReversed() {
|
||||
return this.transactionType.isChargeTransaction() && isNotReversed();
|
||||
}
|
||||
@@ -427,7 +454,9 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
if (isDeposit() || isDividendPayoutAndNotReversed()) {
|
||||
endOfDayBalance = endOfDayBalance.plus(getAmount());
|
||||
} else if (isWithdrawal() || isChargeTransactionAndNotReversed()) {
|
||||
if (endOfDayBalance.isGreaterThanZero() || isAllowOverdraft) {
|
||||
if (endOfDayBalance.isLessThanZero() && isAllowOverdraft) {
|
||||
endOfDayBalance = Money.of(currency, this.runningBalance);
|
||||
} else if (endOfDayBalance.isGreaterThanZero() || isAllowOverdraft) {
|
||||
endOfDayBalance = endOfDayBalance.minus(getAmount());
|
||||
} else {
|
||||
endOfDayBalance = Money.of(currency, this.runningBalance);
|
||||
@@ -658,4 +687,12 @@ public final class SavingsAccountTransactionData implements Serializable {
|
||||
public TransactionEntryType getEntryType() {
|
||||
return entryType;
|
||||
}
|
||||
|
||||
public void setAccountCredit(Long accountCredit) {
|
||||
this.accountCredit = accountCredit;
|
||||
}
|
||||
|
||||
public void setAccountDebit(Long accountDebit) {
|
||||
this.accountDebit = accountDebit;
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -47,9 +47,14 @@ public class CompoundInterestHelper {
|
||||
// total interest earned in previous periods but not yet recognised
|
||||
BigDecimal compoundedInterest = BigDecimal.ZERO;
|
||||
BigDecimal unCompoundedInterest = BigDecimal.ZERO;
|
||||
LocalDate endDay = DateUtils.getBusinessLocalDate();
|
||||
final CompoundInterestValues compoundInterestValues = new CompoundInterestValues(compoundedInterest, unCompoundedInterest);
|
||||
for (final PostingPeriod postingPeriod : allPeriods) {
|
||||
|
||||
if (postingPeriod.dateOfPostingTransaction().getMonth() != endDay.getMonth()) {
|
||||
compoundInterestValues.setCompoundedInterest(interestEarned.getAmount());
|
||||
}
|
||||
|
||||
final BigDecimal interestEarnedThisPeriod = postingPeriod.calculateInterest(compoundInterestValues);
|
||||
|
||||
final Money moneyToBePostedForPeriod = Money.of(currency, interestEarnedThisPeriod);
|
||||
@@ -61,8 +66,9 @@ public class CompoundInterestHelper {
|
||||
// calculation.
|
||||
if (!(postingPeriod.isInterestTransfered() || !interestTransferEnabled
|
||||
|| (lockUntil != null && !DateUtils.isAfter(postingPeriod.dateOfPostingTransaction(), lockUntil)))) {
|
||||
compoundInterestValues.setcompoundedInterest(BigDecimal.ZERO);
|
||||
compoundInterestValues.setCompoundedInterest(BigDecimal.ZERO);
|
||||
}
|
||||
endDay = postingPeriod.dateOfPostingTransaction();
|
||||
}
|
||||
|
||||
return interestEarned;
|
||||
|
||||
+1
-1
@@ -38,7 +38,7 @@ public class CompoundInterestValues {
|
||||
return this.uncompoundedInterest;
|
||||
}
|
||||
|
||||
public void setcompoundedInterest(BigDecimal interestToBeCompounded) {
|
||||
public void setCompoundedInterest(BigDecimal interestToBeCompounded) {
|
||||
this.compoundedInterest = interestToBeCompounded;
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -26,6 +26,8 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.TreeSet;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
|
||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||
import org.apache.fineract.organisation.monetary.domain.MonetaryCurrency;
|
||||
@@ -34,6 +36,8 @@ import org.apache.fineract.portfolio.savings.SavingsCompoundingInterestPeriodTyp
|
||||
import org.apache.fineract.portfolio.savings.SavingsInterestCalculationType;
|
||||
import org.apache.fineract.portfolio.savings.data.SavingsAccountTransactionData;
|
||||
|
||||
@Setter
|
||||
@Getter
|
||||
public final class PostingPeriod {
|
||||
|
||||
private final LocalDateInterval periodInterval;
|
||||
@@ -64,6 +68,8 @@ public final class PostingPeriod {
|
||||
|
||||
private Integer financialYearBeginningMonth;
|
||||
|
||||
private boolean overdraftInterest = false;
|
||||
|
||||
public void setOverdraftInterestRateAsFraction(BigDecimal overdraftInterestRateAsFraction) {
|
||||
this.overdraftInterestRateAsFraction = overdraftInterestRateAsFraction;
|
||||
}
|
||||
@@ -301,7 +307,7 @@ public final class PostingPeriod {
|
||||
|
||||
if (compoundingPeriodEndDate.equals(compoundingPeriod.getPeriodInterval().endDate())) {
|
||||
BigDecimal interestCompounded = compoundInterestValues.getcompoundedInterest().add(unCompoundedInterest);
|
||||
compoundInterestValues.setcompoundedInterest(interestCompounded);
|
||||
compoundInterestValues.setCompoundedInterest(interestCompounded);
|
||||
compoundInterestValues.setZeroForInterestToBeUncompounded();
|
||||
}
|
||||
interestEarned = interestEarned.add(interestUnrounded);
|
||||
@@ -314,7 +320,7 @@ public final class PostingPeriod {
|
||||
}
|
||||
|
||||
public Money getInterestEarned() {
|
||||
return this.interestEarnedRounded;
|
||||
return this.interestEarnedRounded != null ? this.interestEarnedRounded : Money.zero(this.currency);
|
||||
}
|
||||
|
||||
private static List<CompoundingPeriod> compoundingPeriodsInPostingPeriod(final LocalDateInterval postingPeriodInterval,
|
||||
|
||||
Reference in New Issue
Block a user