@@ -67,16 +67,10 @@ public class SavingsSchedularInterestPoster {
6767 public void postInterest () throws JobExecutionException {
6868 if (!savingAccounts .isEmpty ()) {
6969 List <Throwable > errors = new ArrayList <>();
70- LocalDate yesterday = DateUtils .getBusinessLocalDate ().minusDays (1 );
7170 for (SavingsAccountData savingsAccountData : savingAccounts ) {
7271 boolean postInterestAsOn = false ;
7372 LocalDate transactionDate = null ;
7473 try {
75- if (isInterestAlreadyPostedForPeriod (savingsAccountData , yesterday )) {
76- log .debug ("Interest already posted for savings account {} up to date {}, skipping" , savingsAccountData .getId (),
77- savingsAccountData .getSummary ().getInterestPostedTillDate ());
78- continue ;
79- }
8074 SavingsAccountData savingsAccountDataRet = savingsAccountWritePlatformService .postInterest (savingsAccountData ,
8175 postInterestAsOn , transactionDate , backdatedTxnsAllowedTill );
8276 savingsAccountDataList .add (savingsAccountDataRet );
@@ -168,15 +162,18 @@ private void batchUpdate(final List<SavingsAccountData> savingsAccountDataList)
168162 String queryForSavingsUpdate = batchQueryForSavingsSummaryUpdate ();
169163 String queryForTransactionInsertion = batchQueryForTransactionInsertion ();
170164 String queryForTransactionUpdate = batchQueryForTransactionsUpdate ();
171- List <Object []> paramsForTransactionInsertion = new ArrayList <>();
172- List <Object []> paramsForSavingsSummary = new ArrayList <>();
173- List <Object []> paramsForTransactionUpdate = new ArrayList <>();
174- List <String > transRefNo = new ArrayList <>();
175165 LocalDate currentDate = DateUtils .getBusinessLocalDate ();
176166 Long userId = platformSecurityContext .authenticatedUser ().getId ();
167+
168+ List <Object []> paramsForSavingsSummary = new ArrayList <>();
169+ List <List <String >> perAccountRefNos = new ArrayList <>();
170+ List <List <Object []>> perAccountInsertionParams = new ArrayList <>();
171+ List <List <Object []>> perAccountUpdateParams = new ArrayList <>();
172+
177173 for (SavingsAccountData savingsAccountData : savingsAccountDataList ) {
178174 OffsetDateTime auditTime = DateUtils .getAuditOffsetDateTime ();
179175 SavingsAccountSummaryData savingsAccountSummaryData = savingsAccountData .getSummary ();
176+
180177 paramsForSavingsSummary .add (new Object [] { savingsAccountSummaryData .getTotalDeposits (),
181178 savingsAccountSummaryData .getTotalWithdrawals (), savingsAccountSummaryData .getTotalInterestEarned (),
182179 savingsAccountSummaryData .getTotalInterestPosted (), savingsAccountSummaryData .getTotalWithdrawalFees (),
@@ -186,14 +183,19 @@ private void batchUpdate(final List<SavingsAccountData> savingsAccountDataList)
186183 savingsAccountSummaryData .getLastInterestCalculationDate (),
187184 savingsAccountSummaryData .getInterestPostedTillDate () != null ? savingsAccountSummaryData .getInterestPostedTillDate ()
188185 : savingsAccountSummaryData .getLastInterestCalculationDate (),
189- auditTime , userId , savingsAccountData .getId () });
186+ auditTime , userId , savingsAccountData .getId (), savingsAccountData .getVersion () });
187+
188+ List <String > transRefNo = new ArrayList <>();
189+ List <Object []> insertionParams = new ArrayList <>();
190+ List <Object []> updateParams = new ArrayList <>();
191+
190192 List <SavingsAccountTransactionData > savingsAccountTransactionDataList = savingsAccountData .getSavingsAccountTransactionData ();
191193 for (SavingsAccountTransactionData savingsAccountTransactionData : savingsAccountTransactionDataList ) {
192194 if (savingsAccountTransactionData .getId () == null && !MathUtil .isZero (savingsAccountTransactionData .getAmount ())) {
193195 UUID uuid = UUID .randomUUID ();
194196 savingsAccountTransactionData .setRefNo (uuid .toString ());
195197 transRefNo .add (uuid .toString ());
196- paramsForTransactionInsertion .add (new Object [] { savingsAccountData .getId (), savingsAccountData .getOfficeId (),
198+ insertionParams .add (new Object [] { savingsAccountData .getId (), savingsAccountData .getOfficeId (),
197199 savingsAccountTransactionData .isReversed (), savingsAccountTransactionData .getTransactionType ().getId (),
198200 savingsAccountTransactionData .getTransactionDate (), savingsAccountTransactionData .getAmount (),
199201 savingsAccountTransactionData .getBalanceEndDate (), savingsAccountTransactionData .getBalanceNumberOfDays (),
@@ -202,35 +204,58 @@ private void batchUpdate(final List<SavingsAccountData> savingsAccountDataList)
202204 savingsAccountTransactionData .getRefNo (), savingsAccountTransactionData .isReversalTransaction (),
203205 savingsAccountTransactionData .getOverdraftAmount (), currentDate });
204206 } else {
205- paramsForTransactionUpdate .add (new Object [] { savingsAccountTransactionData .isReversed (),
206- savingsAccountTransactionData .getAmount (), savingsAccountTransactionData .getOverdraftAmount (),
207- savingsAccountTransactionData .getBalanceEndDate (), savingsAccountTransactionData .getBalanceNumberOfDays (),
208- savingsAccountTransactionData .getRunningBalance (), savingsAccountTransactionData .getCumulativeBalance (),
209- savingsAccountTransactionData .isReversalTransaction (), auditTime , userId ,
210- savingsAccountTransactionData .getId () });
207+ updateParams .add (new Object [] { savingsAccountTransactionData .isReversed (), savingsAccountTransactionData .getAmount (),
208+ savingsAccountTransactionData .getOverdraftAmount (), savingsAccountTransactionData .getBalanceEndDate (),
209+ savingsAccountTransactionData .getBalanceNumberOfDays (), savingsAccountTransactionData .getRunningBalance (),
210+ savingsAccountTransactionData .getCumulativeBalance (), savingsAccountTransactionData .isReversalTransaction (),
211+ auditTime , userId , savingsAccountTransactionData .getId () });
211212 }
212213 }
213214 savingsAccountData .setUpdatedTransactions (savingsAccountTransactionDataList );
215+
216+ perAccountRefNos .add (transRefNo );
217+ perAccountInsertionParams .add (insertionParams );
218+ perAccountUpdateParams .add (updateParams );
214219 }
215220
216- if (transRefNo .size () > 0 ) {
217- this .jdbcTemplate .batchUpdate (queryForSavingsUpdate , paramsForSavingsSummary );
218- this .jdbcTemplate .batchUpdate (queryForTransactionInsertion , paramsForTransactionInsertion );
219- this .jdbcTemplate .batchUpdate (queryForTransactionUpdate , paramsForTransactionUpdate );
220- log .debug ("`Total No Of Interest Posting:` {}" , transRefNo .size ());
221- List <SavingsAccountTransactionData > savingsAccountTransactionDataList = fetchTransactionsFromIds (transRefNo );
222- if (savingsAccountDataList != null ) {
223- log .debug ("Fetched Transactions from DB: {}" , savingsAccountTransactionDataList .size ());
221+ boolean anyNewTransactions = perAccountRefNos .stream ().anyMatch (list -> !list .isEmpty ());
222+ if (!anyNewTransactions ) {
223+ return ;
224+ }
225+
226+ int [] updateCounts = this .jdbcTemplate .batchUpdate (queryForSavingsUpdate , paramsForSavingsSummary );
227+
228+ List <String > allTransRefNo = new ArrayList <>();
229+ List <Object []> allInsertionParams = new ArrayList <>();
230+ List <Object []> allUpdateParams = new ArrayList <>();
231+ List <SavingsAccountData > successfulAccounts = new ArrayList <>();
232+
233+ for (int i = 0 ; i < updateCounts .length ; i ++) {
234+ if (updateCounts [i ] == 0 ) {
235+ log .warn ("Optimistic lock failure for savings account id={} — concurrent modification detected."
236+ + " Skipping. Will retry on next run." , savingsAccountDataList .get (i ).getId ());
237+ continue ;
224238 }
239+ allTransRefNo .addAll (perAccountRefNos .get (i ));
240+ allInsertionParams .addAll (perAccountInsertionParams .get (i ));
241+ allUpdateParams .addAll (perAccountUpdateParams .get (i ));
242+ successfulAccounts .add (savingsAccountDataList .get (i ));
243+ }
244+
245+ if (!allTransRefNo .isEmpty ()) {
246+ this .jdbcTemplate .batchUpdate (queryForTransactionInsertion , allInsertionParams );
247+ this .jdbcTemplate .batchUpdate (queryForTransactionUpdate , allUpdateParams );
248+ log .debug ("`Total No Of Interest Posting:` {}" , allTransRefNo .size ());
249+ List <SavingsAccountTransactionData > fetchedTransactions = fetchTransactionsFromIds (allTransRefNo );
250+ log .debug ("Fetched Transactions from DB: {}" , fetchedTransactions .size ());
225251
226252 HashMap <String , SavingsAccountTransactionData > savingsAccountTransactionMap = new HashMap <>();
227- for (SavingsAccountTransactionData savingsAccountTransactionData : savingsAccountTransactionDataList ) {
253+ for (SavingsAccountTransactionData savingsAccountTransactionData : fetchedTransactions ) {
228254 final String key = savingsAccountTransactionData .getRefNo ();
229255 savingsAccountTransactionMap .put (key , savingsAccountTransactionData );
230256 }
231- batchUpdateJournalEntries (savingsAccountDataList , savingsAccountTransactionMap );
257+ batchUpdateJournalEntries (successfulAccounts , savingsAccountTransactionMap );
232258 }
233-
234259 }
235260
236261 private String batchQueryForTransactionInsertion () {
@@ -245,20 +270,12 @@ private String batchQueryForSavingsSummaryUpdate() {
245270 return "update m_savings_account set total_deposits_derived=?, total_withdrawals_derived=?, total_interest_earned_derived=?, total_interest_posted_derived=?, total_withdrawal_fees_derived=?, "
246271 + "total_fees_charge_derived=?, total_penalty_charge_derived=?, total_annual_fees_derived=?, account_balance_derived=?, total_overdraft_interest_derived=?, total_withhold_tax_derived=?, "
247272 + "last_interest_calculation_date=?, interest_posted_till_date=?, " + LAST_MODIFIED_DATE_DB_FIELD + " = ?, "
248- + LAST_MODIFIED_BY_DB_FIELD + " = ? WHERE id=? " ;
273+ + LAST_MODIFIED_BY_DB_FIELD + " = ?, version = version + 1 WHERE id=? AND version=? " ;
249274 }
250275
251276 private String batchQueryForTransactionsUpdate () {
252277 return "UPDATE m_savings_account_transaction "
253278 + "SET is_reversed=?, amount=?, overdraft_amount_derived=?, balance_end_date_derived=?, balance_number_of_days_derived=?, running_balance_derived=?, cumulative_balance_derived=?, is_reversal=?, "
254279 + LAST_MODIFIED_DATE_DB_FIELD + " = ?, " + LAST_MODIFIED_BY_DB_FIELD + " = ? " + "WHERE id=?" ;
255280 }
256-
257- private boolean isInterestAlreadyPostedForPeriod (SavingsAccountData savingsAccountData , LocalDate yesterday ) {
258- LocalDate interestPostedTillDate = savingsAccountData .getSummary ().getInterestPostedTillDate ();
259- if (interestPostedTillDate == null ) {
260- return false ;
261- }
262- return !interestPostedTillDate .isBefore (yesterday );
263- }
264281}
0 commit comments