FINERACT-2312: Post interest with adjustments for savings accounts
This commit is contained in:
committed by
Adam Saghy
parent
ef1e2e1095
commit
a6fa1adc99
+131
-12
@@ -32,6 +32,7 @@ import java.util.stream.Collectors;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.apache.fineract.infrastructure.core.domain.LocalDateInterval;
|
||||
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.Money;
|
||||
import org.apache.fineract.portfolio.savings.DepositAccountType;
|
||||
@@ -81,11 +82,17 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||
final LocalDate interestPostingTransactionDate = interestPostingPeriod.dateOfPostingTransaction();
|
||||
final Money interestEarnedToBePostedForPeriod = interestPostingPeriod.getInterestEarned();
|
||||
final Boolean isOverdraft = interestPostingPeriod.isOverdraftInterest();
|
||||
|
||||
if (!DateUtils.isAfter(interestPostingTransactionDate, interestPostingUpToDate)) {
|
||||
interestPostedToDate = interestPostedToDate.plus(interestEarnedToBePostedForPeriod);
|
||||
final SavingsAccountTransactionData postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate,
|
||||
savingsAccountData);
|
||||
SavingsAccountTransactionData postingTransaction = null;
|
||||
if (this.depositAccountType(savingsAccountData).isSavingsDeposit() && savingsAccountData.isAllowOverdraft()) {
|
||||
postingTransaction = findInterestPostingTransactionForInterest(interestPostingTransactionDate, savingsAccountData,
|
||||
isOverdraft);
|
||||
} else {
|
||||
postingTransaction = findInterestPostingTransactionFor(interestPostingTransactionDate, savingsAccountData);
|
||||
}
|
||||
|
||||
if (postingTransaction == null) {
|
||||
SavingsAccountTransactionData newPostingTransaction;
|
||||
@@ -95,7 +102,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
} else {
|
||||
newPostingTransaction = SavingsAccountTransactionData.overdraftInterest(savingsAccountData,
|
||||
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated(),
|
||||
interestPostingPeriod.isUserPosting());
|
||||
interestPostingPeriod.isUserPosting(), isOverdraft);
|
||||
}
|
||||
|
||||
savingsAccountData.updateTransactions(newPostingTransaction);
|
||||
@@ -131,7 +138,7 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
} else {
|
||||
newPostingTransaction = SavingsAccountTransactionData.overdraftInterest(savingsAccountData,
|
||||
interestPostingTransactionDate, interestEarnedToBePostedForPeriod.negated(),
|
||||
interestPostingPeriod.isUserPosting());
|
||||
interestPostingPeriod.isUserPosting(), isOverdraft);
|
||||
}
|
||||
|
||||
savingsAccountData.updateTransactions(newPostingTransaction);
|
||||
@@ -190,6 +197,36 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
return transaction;
|
||||
}
|
||||
|
||||
private Money appendPostingPeriodIfAny(final LocalDateInterval periodInterval, Money periodStartingBalance,
|
||||
final List<SavingsAccountTransactionData> txs, final MonetaryCurrency monetaryCurrency,
|
||||
final SavingsCompoundingInterestPeriodType compoundingPeriodType, final SavingsInterestCalculationType interestCalculationType,
|
||||
final BigDecimal interestRateAsFraction, final int daysInYear, final LocalDate upToInterestCalculationDate,
|
||||
final Collection<Long> interestPostTransactions, final boolean isInterestTransfer, final Money minBalanceForInterestCalculation,
|
||||
final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final BigDecimal overdraftInterestRateAsFraction,
|
||||
final Money minOverdraftForInterestCalculation, final boolean isUserPosting, final Integer financialYearBeginningMonth,
|
||||
final boolean allowOverdraft, final List<PostingPeriod> allPostingPeriods, Boolean isOverdraftTransacction) {
|
||||
|
||||
if (txs == null || txs.isEmpty()) {
|
||||
return periodStartingBalance;
|
||||
}
|
||||
|
||||
final PostingPeriod postingPeriod = PostingPeriod.createFromDTO(periodInterval, periodStartingBalance, txs, monetaryCurrency,
|
||||
compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYear, upToInterestCalculationDate,
|
||||
interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation, isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||
overdraftInterestRateAsFraction, minOverdraftForInterestCalculation, isUserPosting, financialYearBeginningMonth,
|
||||
allowOverdraft);
|
||||
|
||||
periodStartingBalance = postingPeriod.closingBalance();
|
||||
postingPeriod.setOverdraftInterest(isOverdraftTransacction);
|
||||
|
||||
if (!(MathUtil.isZero(postingPeriod.getOpeningBalance().getAmount())
|
||||
&& MathUtil.isZero(postingPeriod.closingBalance().getAmount()))) {
|
||||
allPostingPeriods.add(postingPeriod);
|
||||
}
|
||||
|
||||
return periodStartingBalance;
|
||||
}
|
||||
|
||||
public List<PostingPeriod> calculateInterestUsing(final MathContext mc, final LocalDate upToInterestCalculationDate,
|
||||
boolean isInterestTransfer, final boolean isSavingsInterestPostingAtCurrentPeriodEnd, final Integer financialYearBeginningMonth,
|
||||
final LocalDate postInterestOnDate, final boolean backdatedTxnsAllowedTill, final SavingsAccountData savingsAccountData) {
|
||||
@@ -268,16 +305,40 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
if (postedAsOnDates.contains(periodInterval.endDate().plusDays(1))) {
|
||||
isUserPosting = true;
|
||||
}
|
||||
final PostingPeriod postingPeriod = PostingPeriod.createFromDTO(periodInterval, periodStartingBalance,
|
||||
retreiveOrderedNonInterestPostingTransactions(savingsAccountData), monetaryCurrency, compoundingPeriodType,
|
||||
interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate,
|
||||
interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
|
||||
isUserPosting, financialYearBeginningMonth, savingsAccountData.isAllowOverdraft());
|
||||
if (savingsAccountData.isAllowOverdraft() && !MathUtil.isZero(savingsAccountData.getGlAccountIdForInterestReceivable())) {
|
||||
|
||||
periodStartingBalance = postingPeriod.closingBalance();
|
||||
List<SavingsAccountTransactionData> overdraftTxs = listForOverdraft(savingsAccountData, periodInterval);
|
||||
List<SavingsAccountTransactionData> interestPostingTxs = listForInterestPosting(savingsAccountData, periodInterval,
|
||||
monetaryCurrency);
|
||||
|
||||
allPostingPeriods.add(postingPeriod);
|
||||
boolean isOverdraftAccountType = isOverdraftAccount(savingsAccountData, periodInterval, monetaryCurrency);
|
||||
|
||||
List<SavingsAccountTransactionData> primaryInterestPublication = isOverdraftAccountType ? overdraftTxs : interestPostingTxs;
|
||||
List<SavingsAccountTransactionData> secondaryInterestPublication = isOverdraftAccountType ? interestPostingTxs
|
||||
: overdraftTxs;
|
||||
|
||||
periodStartingBalance = appendPostingPeriodIfAny(periodInterval, periodStartingBalance, primaryInterestPublication,
|
||||
monetaryCurrency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
|
||||
upToInterestCalculationDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
|
||||
isUserPosting, financialYearBeginningMonth, savingsAccountData.isAllowOverdraft(), allPostingPeriods,
|
||||
isOverdraftAccountType ? true : false);
|
||||
|
||||
periodStartingBalance = appendPostingPeriodIfAny(periodInterval, periodStartingBalance, secondaryInterestPublication,
|
||||
monetaryCurrency, compoundingPeriodType, interestCalculationType, interestRateAsFraction, daysInYearType.getValue(),
|
||||
upToInterestCalculationDate, interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
|
||||
isUserPosting, financialYearBeginningMonth, savingsAccountData.isAllowOverdraft(), allPostingPeriods,
|
||||
isOverdraftAccountType ? false : true);
|
||||
|
||||
} else {
|
||||
periodStartingBalance = appendPostingPeriodIfAny(periodInterval, periodStartingBalance,
|
||||
retreiveOrderedNonInterestPostingTransactions(savingsAccountData), monetaryCurrency, compoundingPeriodType,
|
||||
interestCalculationType, interestRateAsFraction, daysInYearType.getValue(), upToInterestCalculationDate,
|
||||
interestPostTransactions, isInterestTransfer, minBalanceForInterestCalculation,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, overdraftInterestRateAsFraction, minOverdraftForInterestCalculation,
|
||||
isUserPosting, financialYearBeginningMonth, savingsAccountData.isAllowOverdraft(), allPostingPeriods, false);
|
||||
}
|
||||
}
|
||||
|
||||
this.savingsHelper.calculateInterestForAllPostingPeriods(monetaryCurrency, allPostingPeriods,
|
||||
@@ -297,6 +358,52 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
return allPostingPeriods;
|
||||
}
|
||||
|
||||
private List<SavingsAccountTransactionData> listForOverdraft(final SavingsAccountData savingsAccountData,
|
||||
final LocalDateInterval periodInterval) {
|
||||
List<SavingsAccountTransactionData> overdraftTransactionsInPeriod = new ArrayList<>();
|
||||
for (SavingsAccountTransactionData lists : retreiveOrderedNonInterestPostingTransactions(savingsAccountData)) {
|
||||
if (MathUtil.isLessThanZero(lists.getRunningBalance()) && periodInterval.startDate().getMonth() == lists.getDate().getMonth()) {
|
||||
overdraftTransactionsInPeriod.add(lists);
|
||||
|
||||
}
|
||||
}
|
||||
return overdraftTransactionsInPeriod;
|
||||
|
||||
}
|
||||
|
||||
private List<SavingsAccountTransactionData> listForInterestPosting(final SavingsAccountData savingsAccountData,
|
||||
final LocalDateInterval periodInterval, final MonetaryCurrency currency) {
|
||||
|
||||
final List<SavingsAccountTransactionData> nonOverdraftTransactions = new ArrayList<>();
|
||||
|
||||
for (final SavingsAccountTransactionData tx : retreiveOrderedNonInterestPostingTransactions(savingsAccountData)) {
|
||||
if (periodInterval.startDate().getMonth() == tx.getDate().getMonth()) {
|
||||
final Money runningBalance = Money.of(currency, tx.getRunningBalance());
|
||||
|
||||
if (runningBalance.isGreaterThanZero() && !runningBalance.isZero()) {
|
||||
nonOverdraftTransactions.add(tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nonOverdraftTransactions;
|
||||
}
|
||||
|
||||
private Boolean isOverdraftAccount(final SavingsAccountData savingsAccountData, final LocalDateInterval periodInterval,
|
||||
final MonetaryCurrency currency) {
|
||||
|
||||
for (SavingsAccountTransactionData tx : retreiveOrderedNonInterestPostingTransactions(savingsAccountData)) {
|
||||
if (MathUtil.isLessThanZero(tx.getRunningBalance()) && periodInterval.startDate().getMonth() == tx.getDate().getMonth()) {
|
||||
return true;
|
||||
} else if (periodInterval.startDate().getMonth() == tx.getDate().getMonth()) {
|
||||
final Money runningBalance = Money.of(currency, tx.getRunningBalance());
|
||||
if (!runningBalance.isZero()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private List<SavingsAccountTransactionData> retreiveOrderedNonInterestPostingTransactions(final SavingsAccountData savingsAccountData) {
|
||||
final List<SavingsAccountTransactionData> listOfTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
|
||||
|
||||
@@ -509,6 +616,18 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
return postingTransation;
|
||||
}
|
||||
|
||||
protected SavingsAccountTransactionData findInterestPostingTransactionForInterest(final LocalDate postingDate,
|
||||
final SavingsAccountData savingsAccountData, boolean isOverdraft) {
|
||||
SavingsAccountTransactionData postingTransation = null;
|
||||
List<SavingsAccountTransactionData> transactions = savingsAccountData.getSavingsAccountTransactionData();
|
||||
postingTransation = transactions.stream().filter(t -> {
|
||||
Boolean interestSearch = isOverdraft ? t.isOverdraftInterestAndNotReversed() : t.isInterestPostingAndNotReversed();
|
||||
return interestSearch && t.occursOn(postingDate) && !t.isReversalTransaction();
|
||||
}).findFirst().orElse(null);
|
||||
|
||||
return postingTransation;
|
||||
}
|
||||
|
||||
protected void resetAccountTransactionsEndOfDayBalances(final List<SavingsAccountTransactionData> accountTransactionsSorted,
|
||||
final LocalDate interestPostingUpToDate, final SavingsAccountData savingsAccountData) {
|
||||
// loop over transactions in reverse
|
||||
|
||||
+17
@@ -337,6 +337,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
"msac.id as chargeId, msac.amount as chargeAmount, msac.charge_time_enum as chargeTimeType, msac.is_penalty as isPenaltyCharge, ");
|
||||
sqlBuilder.append("txd.id as taxDetailsId, txd.amount as taxAmount, ");
|
||||
sqlBuilder.append("apm.gl_account_id as glAccountIdForInterestOnSavings, apm1.gl_account_id as glAccountIdForSavingsControl, ");
|
||||
sqlBuilder.append(
|
||||
"apm2.gl_account_id as glAccountIdForInterestReceivable,apm3.gl_account_id as glAccountIdForOverdraftPorfolio, ");
|
||||
sqlBuilder.append("apm4.gl_account_id as glAccountIdForInterestPayable, ");
|
||||
sqlBuilder.append(
|
||||
"mtc.id as taxComponentId, mtc.debit_account_id as debitAccountId, mtc.credit_account_id as creditAccountId, mtc.percentage as taxPercentage ");
|
||||
sqlBuilder.append("from m_savings_account sa ");
|
||||
@@ -356,6 +359,9 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
"left join acc_product_mapping apm on apm.product_type = 2 and apm.product_id = sp.id and apm.financial_account_type=3 ");
|
||||
sqlBuilder.append(
|
||||
"left join acc_product_mapping apm1 on apm1.product_type = 2 and apm1.product_id = sp.id and apm1.financial_account_type=2 ");
|
||||
sqlBuilder.append("left join acc_product_mapping apm2 on apm2.product_id = sp.id and apm2.financial_account_type=18 ");
|
||||
sqlBuilder.append("left join acc_product_mapping apm3 on apm3.product_id = sp.id and apm3.financial_account_type = 11 ");
|
||||
sqlBuilder.append("left join acc_product_mapping apm4 on apm4.product_id = sp.id and apm4.financial_account_type = 17 ");
|
||||
|
||||
this.schemaSql = sqlBuilder.toString();
|
||||
}
|
||||
@@ -409,6 +415,11 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
final Long glAccountIdForInterestOnSavings = rs.getLong("glAccountIdForInterestOnSavings");
|
||||
final Long glAccountIdForSavingsControl = rs.getLong("glAccountIdForSavingsControl");
|
||||
|
||||
final Long glAccountIdForOverdraftPorfolio = rs.getLong("glAccountIdForOverdraftPorfolio");
|
||||
final Long glAccountIdForInterestReceivable = rs.getLong("glAccountIdForInterestReceivable");
|
||||
|
||||
final Long glAccountIdForInterestPayable = rs.getLong("glAccountIdForInterestPayable");
|
||||
|
||||
final Long productId = rs.getLong("productId");
|
||||
final Integer accountType = rs.getInt("accountingType");
|
||||
final AccountingRuleType accountingRuleType = AccountingRuleType.fromInt(accountType);
|
||||
@@ -565,6 +576,12 @@ public class SavingsAccountReadPlatformServiceImpl implements SavingsAccountRead
|
||||
savingsAccountData.setClientData(clientData);
|
||||
savingsAccountData.setGroupGeneralData(groupGeneralData);
|
||||
savingsAccountData.setSavingsProduct(savingsProductData);
|
||||
|
||||
savingsAccountData.setGlAccountIdForInterestReceivable(glAccountIdForInterestReceivable);
|
||||
savingsAccountData.setGlAccountIdForOverdraftPorfolio(glAccountIdForOverdraftPorfolio);
|
||||
|
||||
savingsAccountData.setGlAccountIdForInterestPayable(glAccountIdForInterestPayable);
|
||||
|
||||
savingsAccountData.setGlAccountIdForInterestOnSavings(glAccountIdForInterestOnSavings);
|
||||
savingsAccountData.setGlAccountIdForSavingsControl(glAccountIdForSavingsControl);
|
||||
}
|
||||
|
||||
+18
@@ -595,6 +595,7 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
for (SavingsAccountTransactionData accountTransaction : transactions) {
|
||||
if (accountTransaction.getId() == null) {
|
||||
savingsAccountData.setNewSavingsAccountTransactionData(accountTransaction);
|
||||
selectAccountId(accountTransaction, savingsAccountData);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -604,6 +605,23 @@ public class SavingsAccountWritePlatformServiceJpaRepositoryImpl implements Savi
|
||||
return savingsAccountData;
|
||||
}
|
||||
|
||||
public void selectAccountId(SavingsAccountTransactionData accountTransaction, SavingsAccountData savingsAccountData) {
|
||||
SavingsAccountTransactionType transactionType = SavingsAccountTransactionType
|
||||
.fromInt(accountTransaction.getTransactionType().getId().intValue());
|
||||
if (transactionType.isOverDraftInterestPosting()) {
|
||||
if (MathUtil.isGreaterThanZero(accountTransaction.getRunningBalance())) {
|
||||
accountTransaction.setAccountDebit(savingsAccountData.getGlAccountIdForSavingsControl());
|
||||
accountTransaction.setAccountCredit(savingsAccountData.getGlAccountIdForInterestReceivable());
|
||||
} else {
|
||||
accountTransaction.setAccountDebit(savingsAccountData.getGlAccountIdForOverdraftPorfolio());
|
||||
accountTransaction.setAccountCredit(savingsAccountData.getGlAccountIdForInterestReceivable());
|
||||
}
|
||||
} else {
|
||||
accountTransaction.setAccountDebit(savingsAccountData.getGlAccountIdForInterestPayable());
|
||||
accountTransaction.setAccountCredit(savingsAccountData.getGlAccountIdForSavingsControl());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandProcessingResult reverseTransaction(final Long savingsId, final Long transactionId,
|
||||
final boolean allowAccountTransferModification, final JsonCommand command) {
|
||||
|
||||
Reference in New Issue
Block a user