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);
|
accountMappingDetails.put(SavingProductAccountingDataParams.SAVINGS_CONTROL.getValue(), glAccountData);
|
||||||
} else if (glAccountForSavings.equals(CashAccountsForSavings.TRANSFERS_SUSPENSE)) {
|
} else if (glAccountForSavings.equals(CashAccountsForSavings.TRANSFERS_SUSPENSE)) {
|
||||||
accountMappingDetails.put(SavingProductAccountingDataParams.TRANSFERS_SUSPENSE.getValue(), glAccountData);
|
accountMappingDetails.put(SavingProductAccountingDataParams.TRANSFERS_SUSPENSE.getValue(), glAccountData);
|
||||||
|
} else if (glAccountForSavings.equals(CashAccountsForSavings.INTEREST_PAYABLE)) {
|
||||||
|
accountMappingDetails.put(SavingProductAccountingDataParams.INTEREST_PAYABLE.getValue(), glAccountData);
|
||||||
// Income
|
// Income
|
||||||
} else if (glAccountForSavings.equals(CashAccountsForSavings.INCOME_FROM_FEES)) {
|
} else if (glAccountForSavings.equals(CashAccountsForSavings.INCOME_FROM_FEES)) {
|
||||||
accountMappingDetails.put(SavingProductAccountingDataParams.INCOME_FROM_FEES.getValue(), glAccountData);
|
accountMappingDetails.put(SavingProductAccountingDataParams.INCOME_FROM_FEES.getValue(), glAccountData);
|
||||||
|
|||||||
+1
@@ -270,6 +270,7 @@ public final class AccountingConstants {
|
|||||||
INCOME_FROM_INTEREST(12), //
|
INCOME_FROM_INTEREST(12), //
|
||||||
LOSSES_WRITTEN_OFF(13), //
|
LOSSES_WRITTEN_OFF(13), //
|
||||||
ESCHEAT_LIABILITY(14), //
|
ESCHEAT_LIABILITY(14), //
|
||||||
|
INTEREST_PAYABLE(17), //
|
||||||
/**
|
/**
|
||||||
* Where the paise dropped from a cash closure are absorbed.
|
* 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 boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||||
final Integer financialYearBeginningMonth) {
|
final Integer financialYearBeginningMonth) {
|
||||||
final MathContext mc = MathContext.DECIMAL64;
|
final MathContext mc = MathContext.DECIMAL64;
|
||||||
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure,
|
this.resetAccountTransactionsEndOfDayBalances(transactions, preMatureDate);
|
||||||
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
|
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) {
|
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||||
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
||||||
interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod);
|
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,
|
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 boolean isPreMatureClosure, final boolean isSavingsInterestPostingAtCurrentPeriodEnd,
|
||||||
final Integer financialYearBeginningMonth) {
|
final Integer financialYearBeginningMonth) {
|
||||||
final MathContext mc = MathContext.DECIMAL64;
|
final MathContext mc = MathContext.DECIMAL64;
|
||||||
final List<PostingPeriod> postingPeriods = calculateInterestPayable(mc, preMatureDate, transactions, isPreMatureClosure,
|
this.resetAccountTransactionsEndOfDayBalances(transactions, preMatureDate);
|
||||||
isSavingsInterestPostingAtCurrentPeriodEnd, financialYearBeginningMonth);
|
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) {
|
for (final PostingPeriod interestPostingPeriod : postingPeriods) {
|
||||||
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
final Money interestEarnedForPeriod = interestPostingPeriod.getInterestEarned();
|
||||||
interestOnMaturity = interestOnMaturity.plus(interestEarnedForPeriod);
|
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
|
@Override
|
||||||
|
|||||||
+29
-3
@@ -437,14 +437,38 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
|||||||
return false;
|
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) {
|
private List<SavingsAccountTransactionData> retreiveOrderedNonInterestPostingTransactions(final SavingsAccountData savingsAccountData) {
|
||||||
final List<SavingsAccountTransactionData> listOfTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
|
final List<SavingsAccountTransactionData> listOfTransactionsSorted = retrieveListOfTransactions(savingsAccountData);
|
||||||
|
final Collection<Long> interestTransferWithdrawalIds = interestTransferWithdrawalTransactionIds(savingsAccountData);
|
||||||
|
|
||||||
final List<SavingsAccountTransactionData> orderedNonInterestPostingTransactions = new ArrayList<>();
|
final List<SavingsAccountTransactionData> orderedNonInterestPostingTransactions = new ArrayList<>();
|
||||||
|
|
||||||
for (final SavingsAccountTransactionData transaction : listOfTransactionsSorted) {
|
for (final SavingsAccountTransactionData transaction : listOfTransactionsSorted) {
|
||||||
if (!(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
|
if (!(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())
|
||||||
&& transaction.isNotReversed() && !transaction.isReversalTransaction()) {
|
&& transaction.isNotReversed() && !transaction.isReversalTransaction()
|
||||||
|
&& !interestTransferWithdrawalIds.contains(transaction.getId())) {
|
||||||
orderedNonInterestPostingTransactions.add(transaction);
|
orderedNonInterestPostingTransactions.add(transaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -664,10 +688,12 @@ public class SavingsAccountInterestPostingServiceImpl implements SavingsAccountI
|
|||||||
|
|
||||||
protected void resetAccountTransactionsEndOfDayBalances(final List<SavingsAccountTransactionData> accountTransactionsSorted,
|
protected void resetAccountTransactionsEndOfDayBalances(final List<SavingsAccountTransactionData> accountTransactionsSorted,
|
||||||
final LocalDate interestPostingUpToDate, final SavingsAccountData savingsAccountData) {
|
final LocalDate interestPostingUpToDate, final SavingsAccountData savingsAccountData) {
|
||||||
|
final List<SavingsAccountTransactionData> filteredTransactions = excludeInterestTransferWithdrawals(accountTransactionsSorted,
|
||||||
|
savingsAccountData);
|
||||||
// loop over transactions in reverse
|
// loop over transactions in reverse
|
||||||
LocalDate endOfBalanceDate = interestPostingUpToDate;
|
LocalDate endOfBalanceDate = interestPostingUpToDate;
|
||||||
for (int i = accountTransactionsSorted.size() - 1; i >= 0; i--) {
|
for (int i = filteredTransactions.size() - 1; i >= 0; i--) {
|
||||||
final SavingsAccountTransactionData transaction = accountTransactionsSorted.get(i);
|
final SavingsAccountTransactionData transaction = filteredTransactions.get(i);
|
||||||
if (transaction.isNotReversed() && !transaction.isReversalTransaction()
|
if (transaction.isNotReversed() && !transaction.isReversalTransaction()
|
||||||
&& !(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())) {
|
&& !(transaction.isInterestPostingAndNotReversed() || transaction.isOverdraftInterestAndNotReversed())) {
|
||||||
transaction.updateCumulativeBalanceAndDates(MonetaryCurrency.fromCurrencyData(savingsAccountData.getCurrency()),
|
transaction.updateCumulativeBalanceAndDates(MonetaryCurrency.fromCurrencyData(savingsAccountData.getCurrency()),
|
||||||
|
|||||||
Reference in New Issue
Block a user