fix(savings,accounting): fix premature interest calculation date bounds and load interest payable GL mapping on edit
Details of changes: 1. Premature Closure 1-Day Shortfall Fix (FixedDepositAccount & RecurringDepositAccount): - In calculatePreMatureInterest, explicitly reset transaction end-of-day balances to preMatureDate before invoking calculateInterestPayable. - Ensure end-of-day balances are restored to current business local date in a finally block. - Resolves the issue where premature closure missed 1 day of interest (e.g., -17.80 clawback) due to transaction end-of-day balances defaulting to the previous day when premature date matched current business date. 2. Interest Transfer Exclusions (SavingsAccountInterestPostingServiceImpl): - Exclude interest-transfer withdrawal transactions from non-interest posting transactions and end-of-day cumulative balance updates for fixed deposits transferring interest to linked accounts. 3. Interest Payable GL Load on Edit (AccountingConstants & ProductToGLAccountMappingReadPlatformServiceImpl): - Added INTEREST_PAYABLE(17) enum constant to CashAccountsForSavings in AccountingConstants. - Mapped CashAccountsForSavings.INTEREST_PAYABLE to SavingProductAccountingDataParams.INTEREST_PAYABLE in ProductToGLAccountMappingReadPlatformServiceImpl. - Fixes the bug where Liability -> Interest Payable Account GL was not loading in the product edit form (e.g., /products/fixed-deposit-products/:id/edit) even after being saved.
This commit is contained in:
+2
@@ -477,6 +477,8 @@ public class ProductToGLAccountMappingReadPlatformServiceImpl implements Product
|
||||
accountMappingDetails.put(SavingProductAccountingDataParams.SAVINGS_CONTROL.getValue(), glAccountData);
|
||||
} else if (glAccountForSavings.equals(CashAccountsForSavings.TRANSFERS_SUSPENSE)) {
|
||||
accountMappingDetails.put(SavingProductAccountingDataParams.TRANSFERS_SUSPENSE.getValue(), glAccountData);
|
||||
} else if (glAccountForSavings.equals(CashAccountsForSavings.INTEREST_PAYABLE)) {
|
||||
accountMappingDetails.put(SavingProductAccountingDataParams.INTEREST_PAYABLE.getValue(), glAccountData);
|
||||
// Income
|
||||
} else if (glAccountForSavings.equals(CashAccountsForSavings.INCOME_FROM_FEES)) {
|
||||
accountMappingDetails.put(SavingProductAccountingDataParams.INCOME_FROM_FEES.getValue(), glAccountData);
|
||||
|
||||
+1
@@ -270,6 +270,7 @@ public final class AccountingConstants {
|
||||
INCOME_FROM_INTEREST(12), //
|
||||
LOSSES_WRITTEN_OFF(13), //
|
||||
ESCHEAT_LIABILITY(14), //
|
||||
INTEREST_PAYABLE(17), //
|
||||
/**
|
||||
* Where the paise dropped from a cash closure are absorbed.
|
||||
*
|
||||
|
||||
+13
-8
@@ -829,17 +829,22 @@ public class FixedDepositAccount extends SavingsAccount {
|
||||
final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||
final Integer financialYearBeginningMonth) {
|
||||
final MathContext mc = MathContext.DECIMAL64;
|
||||
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
|
||||
this.resetAccountTransactionsEndOfDayBalances(transactions, preMatureDate);
|
||||
try {
|
||||
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
|
||||
|
||||
Money interestOnMaturity = Money.zero(this.currency);
|
||||
Money interestOnMaturity = Money.zero(this.currency);
|
||||
|
||||
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
||||
interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod);
|
||||
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
||||
interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod);
|
||||
}
|
||||
|
||||
return interestOnMaturity;
|
||||
} finally {
|
||||
this.resetAccountTransactionsEndOfDayBalances(transactions, DateUtils.getBusinessLocalDate());
|
||||
}
|
||||
|
||||
return interestOnMaturity;
|
||||
}
|
||||
|
||||
public void postInterest(final MathContext mc, final LocalDate postingDate, boolean isInterestTransfer,
|
||||
|
||||
+13
-8
@@ -759,17 +759,22 @@ public class RecurringDepositAccount extends SavingsAccount {
|
||||
final boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||
final Integer financialYearBeginningMonth) {
|
||||
final MathContext mc = MathContext.DECIMAL64;
|
||||
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
|
||||
this.resetAccountTransactionsEndOfDayBalances(transactions, preMatureDate);
|
||||
try {
|
||||
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure,
|
||||
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
|
||||
|
||||
Money interestOnMaturity = Money.zero(this.currency);
|
||||
Money interestOnMaturity = Money.zero(this.currency);
|
||||
|
||||
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
||||
interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod);
|
||||
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
||||
interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod);
|
||||
}
|
||||
this.summary.updateFromInterestPeriodSummaries(this.currency, postingPeriods);
|
||||
return interestOnMaturity;
|
||||
} finally {
|
||||
this.resetAccountTransactionsEndOfDayBalances(transactions, DateUtils.getBusinessLocalDate());
|
||||
}
|
||||
this.summary.updateFromInterestPeriodSummaries(this.currency, postingPeriods);
|
||||
return interestOnMaturity;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+29
-3
@@ -437,14 +437,38 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
return false;
|
||||
}
|
||||
|
||||
protected Collection<Long> interestTransferWithdrawalTransactionIds(final SavingsAccountData savingsAccountData) {
|
||||
if (savingsAccountData.getId() == null || this.savingsHelper == null || !isTransferInterestToOtherAccount(savingsAccountData)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return this.savingsHelper.fetchPostInterestTransactionIds(savingsAccountData.getId());
|
||||
}
|
||||
|
||||
private boolean isTransferInterestToOtherAccount(final SavingsAccountData savingsAccountData) {
|
||||
return savingsAccountData.isTransferInterestToLinkedAccount()
|
||||
&& DepositAccountType.FIXED_DEPOSIT.getValue().equals(savingsAccountData.getDepositTypeId());
|
||||
}
|
||||
|
||||
protected List<SavingsAccountTransactionData> excludeInterestTransferWithdrawals(
|
||||
final List<SavingsAccountTransactionData> transactions, final SavingsAccountData savingsAccountData) {
|
||||
final Collection<Long> interestTransferWithdrawalIds = interestTransferWithdrawalTransactionIds(savingsAccountData);
|
||||
if (interestTransferWithdrawalIds.isEmpty()) {
|
||||
return transactions;
|
||||
}
|
||||
return transactions.stream().filter(transaction -> !interestTransferWithdrawalIds.contains(transaction.getId()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<SavingsAccountTransactionData> retreiveOrderedNonInterestPostingTransactions(final SavingsAccountData savingsAccountData) {
|
||||
final List<SavingsAccountTransactionData> listOfTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
|
||||
final Collection<Long> interestTransferWithdrawalIds = interestTransferWithdrawalTransactionIds(savingsAccountData);
|
||||
|
||||
final List<SavingsAccountTransactionData> orderedNonInterestPostingTransactions = new ArrayList<>();
|
||||
|
||||
for (final SavingsAccountTransactionData transaction : listOfTransactionsSorted) {
|
||||
if (!(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
|
||||
&& transaction.isNotReversed() && !transaction.isReversalTransaction()) {
|
||||
&& transaction.isNotReversed() && !transaction.isReversalTransaction()
|
||||
&& !interestTransferWithdrawalIds.contains(transaction.getId())) {
|
||||
orderedNonInterestPostingTransactions.add(transaction);
|
||||
}
|
||||
}
|
||||
@@ -664,10 +688,12 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
||||
|
||||
protected void resetAccountTransactionsEndOfDayBalances(final List<SavingsAccountTransactionData> accountTransactionsSorted,
|
||||
final LocalDate interestPostingUpToDate, final SavingsAccountData savingsAccountData) {
|
||||
final List<SavingsAccountTransactionData> filteredTransactions = excludeInterestTransferWithdrawals(accountTransactionsSorted,
|
||||
savingsAccountData);
|
||||
// loop over transactions in reverse
|
||||
LocalDate endOfBalanceDate = interestPostingUpToDate;
|
||||
for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) {
|
||||
final SavingsAccountTransactionData transaction = accountTransactionsSorted.get(i);
|
||||
for (int i = filteredTransactions.size() - 1; i >= 0; i--) {
|
||||
final SavingsAccountTransactionData transaction = filteredTransactions.get(i);
|
||||
if (transaction.isNotReversed() && !transaction.isReversalTransaction()
|
||||
&& !(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())) {
|
||||
transaction.updateCumulativeBalanceAndDates(MonetaryCurrency.fromCurrencyData(savingsAccountData.getCurrency()),
|
||||
|
||||
Reference in New Issue
Block a user