fix(savings): post GL entries for facility-charge collections at edit approval

Charges collected when a maker-checker Nominee/Digital-Feature edit is
approved deducted the account balance and appeared in the passbook,
but never posted a journal entry -- collectCharges() saved the charge
transaction directly and skipped the accounting bridge entirely, so
the GL never balanced against the subledger.

Also fixes an unrelated NPE this exposed: collectCharges() used
saveAll() instead of saveAllAndFlush(), so the new charge transaction
had no id yet when the journal-entry code tried to read it.
This commit is contained in:
2026-08-19 10:35:18 +05:30
parent 231919b62b
commit e227e8ae0f
3 changed files with 15 additions and 2 deletions
@@ -220,10 +220,16 @@ public class SavingsAccountEditWritePlatformServiceJpaRepositoryImpl implements
this.savingsAccountWritePlatformService.releaseAmount(savingsId, pendingEdit.getHoldTransactionId());
}
// 2) Post the real charge(s) for newly-enabled facilities, today.
// 2) Post the real charge(s) for newly-enabled facilities, today -- and post the matching
// GL journal entries for those charge transactions (collectCharges() only books them
// against the account itself; it does not touch the general ledger).
if (!newlyEnabledFacilityTypes.isEmpty()) {
final Set<Long> existingTransactionIds = new HashSet<>(account.findExistingTransactionIds());
final Set<Long> existingReversedTransactionIds = new HashSet<>(account.findExistingReversedTransactionIds());
final var chargesAdded = this.facilityChargeAutoApplyService.applyChargesForFacilities(account, newlyEnabledFacilityTypes);
this.facilityChargeAutoApplyService.collectCharges(account, chargesAdded, DateUtils.getBusinessLocalDate());
this.savingsAccountWritePlatformService.postJournalEntriesForSavingsAccount(account, existingTransactionIds,
existingReversedTransactionIds, false);
}
// 3) Apply the facility-config toggles to the real row.
@@ -160,7 +160,11 @@ public class FacilityChargeAutoApplyService {
}
}
if (!transactions.isEmpty()) {
savingsAccountTransactionRepository.saveAll(transactions);
// Flush immediately (not just save) so each transaction gets its DB-generated id right
// away -- the caller posts GL journal entries for these transactions straight after this
// call returns, and that requires a real id on each one, not one that only appears at
// end-of-transaction commit.
savingsAccountTransactionRepository.saveAllAndFlush(transactions);
account.recalculateBalancesAfterFacilityCharges(transactionDate);
}
return transactions;
@@ -79,6 +79,9 @@ public interface SavingsAccountWritePlatformService {
void processPostActiveActions(SavingsAccount account, DateTimeFormatter fmt, Set<Long> existingTransactionIds,
Set<Long> existingReversedTransactionIds);
void postJournalEntriesForSavingsAccount(SavingsAccount account, Set<Long> existingTransactionIds,
Set<Long> existingReversedTransactionIds, boolean backdatedTxnsAllowedTill);
CommandProcessingResult modifyWithHoldTax(Long savingsAccountId, JsonCommand command);
void setSubStatusInactive(Long savingsId);