fix(accounting): interest postings were never reaching the general ledger
createInterestPostingJournalEntriesForSavings() documents a 3-stage posting (Dr Interest On Savings -> Cr Payable, Dr Payable -> Cr Paid, Dr Paid -> Cr Savings Control), but the first stage was never actually coded -- the interest expense account was never even fetched. For a product with no distinct Interest Payable/Paid accounts configured (every product in this system, checked directly), the remaining two stages then collapsed into a same-account no-op that correctly refuses to post, leaving interest silently added to the account's balance with zero GL entries. Affects every deposit type equally (Savings, FD, RD), not just FD -- confirmed by checking every product's accounting mappings. Added the missing stage. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+16
-7
@@ -651,6 +651,8 @@ public class AccountingProcessorHelper {
|
||||
|
||||
final GLAccount controlAccount = getLinkedGLAccountForSavingsProduct(savingsProductId,
|
||||
CashAccountsForSavings.SAVINGS_CONTROL.getValue(), paymentTypeId);
|
||||
final GLAccount interestOnSavingsAccount = getLinkedGLAccountForSavingsProduct(savingsProductId,
|
||||
CashAccountsForSavings.INTEREST_ON_SAVINGS.getValue(), paymentTypeId);
|
||||
|
||||
// The payable comes from the Interest Payable row on the product's Accounting tab, which is
|
||||
// where a bank actually picks it. The reserved name in the product's other GL mappings is
|
||||
@@ -664,13 +666,20 @@ public class AccountingProcessorHelper {
|
||||
final GLAccount paidAccount = Optional.ofNullable(namedSavingsGlAccount(savingsProductId, SAVINGS_INTEREST_PAID_MAPPING))
|
||||
.orElse(payableAccount);
|
||||
|
||||
// The interest expense is not raised here. It is recognised as the interest accrues, day by
|
||||
// day, and this posting settles that recognised debt - raising the expense again at payout
|
||||
// would put the same interest on the P&L twice. This mirrors the pooled posting service,
|
||||
// which pays a product's whole membership the same way; a closure reaching this path must
|
||||
// produce the same entries as the nightly run, or the two disagree on what one payment did.
|
||||
// Stage 1: the expense is recognised against whatever the liability sits in right now -- the
|
||||
// named Payable account if the product has one, or straight against Savings Control if it
|
||||
// doesn't. This was missing entirely: every product, configured or not, posted zero journal
|
||||
// entries for interest until this leg existed, since a product with no Payable/Paid names
|
||||
// configured also collapsed stages 2 and 3 below into a same-account no-op with nothing else
|
||||
// to post in their place.
|
||||
postSavingsLeg(office, currencyCode, interestOnSavingsAccount, payableAccount, savingsId, transactionId, transactionDate, amount,
|
||||
isReversal);
|
||||
|
||||
// The interest expense is not raised again here. It was just raised above, and this posting
|
||||
// only settles that recognised debt onward to where it is actually paid out from -- raising
|
||||
// the expense a second time would put the same interest on the P&L twice.
|
||||
//
|
||||
// Stage 1: the liability moves from the payable to the account it is paid out of. Where the
|
||||
// Stage 2: the liability moves from the payable to the account it is paid out of. Where the
|
||||
// product names no paid account both resolve to the payable and this says nothing, so it is
|
||||
// skipped and the payout comes straight out of the payable.
|
||||
final boolean payableIsPaid = payableAccount.getId().equals(paidAccount.getId());
|
||||
@@ -679,7 +688,7 @@ public class AccountingProcessorHelper {
|
||||
isReversal);
|
||||
}
|
||||
|
||||
// Stage 2: into the member's own balance.
|
||||
// Stage 3: into the member's own balance.
|
||||
final GLAccount payoutFromAccount = payableIsPaid ? payableAccount : paidAccount;
|
||||
postSavingsLeg(office, currencyCode, payoutFromAccount, controlAccount, savingsId, transactionId, transactionDate, amount,
|
||||
isReversal);
|
||||
|
||||
Reference in New Issue
Block a user