Skip to content

Commit 352ef63

Browse files
a7med3del1973adamsaghy
authored andcommitted
FINERACT-2326: Refactor trial balance processing to reduce cognitive complexity
1 parent e5be162 commit 352ef63

File tree

4 files changed

+134
-44
lines changed

4 files changed

+134
-44
lines changed

fineract-accounting/src/main/java/org/apache/fineract/accounting/glaccount/domain/TrialBalanceRepository.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
package org.apache.fineract.accounting.glaccount.domain;
2020

21+
import java.math.BigDecimal;
22+
import java.time.LocalDate;
2123
import java.util.List;
2224
import org.springframework.data.jpa.repository.JpaRepository;
2325
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -28,4 +30,33 @@ public interface TrialBalanceRepository extends JpaRepository<TrialBalance, Long
2830

2931
@Query(value = "select * from m_trial_balance where office_id=:officeId and account_id=:accountId and closing_balance is null order by created_date, entry_date", nativeQuery = true)
3032
List<TrialBalance> findNewByOfficeAndAccount(@Param("officeId") Long officeId, @Param("accountId") Long accountId);
33+
34+
@Query("SELECT MAX(tb.transactionDate) FROM TrialBalance tb")
35+
LocalDate findMaxCreatedDate();
36+
37+
@Query("""
38+
SELECT tb.closingBalance
39+
FROM TrialBalance tb
40+
WHERE tb.officeId = :officeId
41+
AND tb.glAccountId = :accountId
42+
AND tb.closingBalance IS NOT NULL
43+
ORDER BY tb.transactionDate DESC, tb.entryDate DESC
44+
""")
45+
List<BigDecimal> findLastClosingBalance(Long officeId, Long accountId);
46+
47+
@Query("""
48+
SELECT DISTINCT tb.officeId
49+
FROM TrialBalance tb
50+
WHERE tb.closingBalance IS NULL
51+
""")
52+
List<Long> findDistinctOfficeIdsWithNullClosingBalance();
53+
54+
@Query("""
55+
SELECT DISTINCT tb.glAccountId
56+
FROM TrialBalance tb
57+
WHERE tb.officeId = :officeId
58+
AND tb.closingBalance IS NULL
59+
""")
60+
List<Long> findDistinctAccountIdsWithNullClosingBalanceByOfficeId(Long officeId);
61+
3162
}

fineract-accounting/src/main/java/org/apache/fineract/accounting/glaccount/jobs/updatetrialbalancedetails/UpdateTrialBalanceDetailsConfig.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919
package org.apache.fineract.accounting.glaccount.jobs.updatetrialbalancedetails;
2020

2121
import lombok.RequiredArgsConstructor;
22+
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository;
2223
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper;
24+
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
2325
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
2426
import org.apache.fineract.infrastructure.jobs.service.JobName;
2527
import org.springframework.batch.core.Job;
@@ -40,6 +42,8 @@ public class UpdateTrialBalanceDetailsConfig {
4042
private final PlatformTransactionManager transactionManager;
4143
private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
4244
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
45+
private final TrialBalanceRepository trialBalanceRepository;
46+
private final JournalEntryRepository journalEntryRepository;
4347

4448
@Bean
4549
protected Step updateTrialBalanceDetailsStep() {
@@ -55,6 +59,7 @@ public Job updateTrialBalanceDetailsJob() {
5559

5660
@Bean
5761
public UpdateTrialBalanceDetailsTasklet updateTrialBalanceDetailsTasklet() {
58-
return new UpdateTrialBalanceDetailsTasklet(dataSourceServiceFactory, trialBalanceRepositoryWrapper);
62+
return new UpdateTrialBalanceDetailsTasklet(dataSourceServiceFactory, trialBalanceRepositoryWrapper, trialBalanceRepository,
63+
journalEntryRepository);
5964
}
6065
}

fineract-accounting/src/main/java/org/apache/fineract/accounting/glaccount/jobs/updatetrialbalancedetails/UpdateTrialBalanceDetailsTasklet.java

Lines changed: 73 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
import lombok.RequiredArgsConstructor;
2525
import lombok.extern.slf4j.Slf4j;
2626
import org.apache.fineract.accounting.glaccount.domain.TrialBalance;
27+
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository;
2728
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper;
29+
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
2830
import org.apache.fineract.infrastructure.core.service.DateUtils;
2931
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
3032
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
@@ -41,59 +43,87 @@ public class UpdateTrialBalanceDetailsTasklet implements Tasklet {
4143

4244
private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
4345
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
46+
private final TrialBalanceRepository trialBalanceRepository;
47+
private final JournalEntryRepository journalEntryRepository;
4448

4549
@Override
4650
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
4751
final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceServiceFactory.determineDataSourceService().retrieveDataSource());
48-
final StringBuilder tbGapSqlBuilder = new StringBuilder(500);
49-
tbGapSqlBuilder.append("select distinct(je.transaction_date) ").append("from acc_gl_journal_entry je ")
50-
.append("where je.transaction_date > (select coalesce(MAX(created_date),'2010-01-01') from m_trial_balance)");
51-
final List<LocalDate> tbGaps = jdbcTemplate.queryForList(tbGapSqlBuilder.toString(), LocalDate.class);
52+
53+
processTrialBalanceGaps(jdbcTemplate);
54+
updateClosingBalances(jdbcTemplate);
55+
56+
return RepeatStatus.FINISHED;
57+
}
58+
59+
private void processTrialBalanceGaps(JdbcTemplate jdbcTemplate) {
60+
LocalDate maxCreatedDate = trialBalanceRepository.findMaxCreatedDate();
61+
LocalDate baselineDate = maxCreatedDate != null ? maxCreatedDate : LocalDate.of(2010, 1, 1);
62+
List<LocalDate> tbGaps = journalEntryRepository.findTransactionDatesAfter(baselineDate);
5263
for (LocalDate tbGap : tbGaps) {
53-
int days = DateUtils.getExactDifferenceInDays(tbGap, DateUtils.getBusinessLocalDate());
54-
if (days < 1) {
64+
if (DateUtils.getExactDifferenceInDays(tbGap, DateUtils.getBusinessLocalDate()) < 1) {
5565
continue;
5666
}
57-
final StringBuilder sqlBuilder = new StringBuilder(600);
58-
sqlBuilder.append("Insert Into m_trial_balance(office_id, account_id, Amount, entry_date, created_date,closing_balance) ")
59-
.append("Select je.office_id, je.account_id, SUM(CASE WHEN je.type_enum=1 THEN (-1) * je.amount ELSE je.amount END) ")
60-
.append("as Amount, Date(je.entry_date) as Entry_Date, je.transaction_date as Created_Date,sum(je.amount) as closing_balance ")
61-
.append("from acc_gl_journal_entry je WHERE je.transaction_date = ? ")
62-
.append("group by je.account_id, je.office_id, je.transaction_date, Date(je.entry_date)");
63-
final int result = jdbcTemplate.update(sqlBuilder.toString(), tbGap);
64-
log.debug("{}: Records affected by updateTrialBalanceDetails: {}", ThreadLocalContextUtil.getTenant().getName(), result);
67+
insertTrialBalanceForDate(tbGap);
6568
}
66-
String distinctOfficeQuery = "select distinct(office_id) from m_trial_balance where closing_balance is null group by office_id";
67-
final List<Long> officeIds = jdbcTemplate.queryForList(distinctOfficeQuery, Long.class);
69+
}
70+
71+
private void insertTrialBalanceForDate(LocalDate tbGap) {
72+
List<Object[]> rows = journalEntryRepository.findTrialBalanceLinesForDate(tbGap);
73+
74+
List<TrialBalance> trialBalances = rows.stream().map(row -> {
75+
TrialBalance tb = new TrialBalance();
76+
tb.setOfficeId((Long) row[0]);
77+
tb.setGlAccountId((Long) row[1]);
78+
tb.setAmount((BigDecimal) row[2]);
79+
tb.setEntryDate((LocalDate) row[3]);
80+
tb.setTransactionDate((LocalDate) row[4]);
81+
tb.setClosingBalance((BigDecimal) row[5]);
82+
return tb;
83+
}).toList();
84+
85+
trialBalanceRepositoryWrapper.save(trialBalances);
86+
87+
log.debug("{}: Records affected by updateTrialBalanceDetails: {}", ThreadLocalContextUtil.getTenant().getName(),
88+
trialBalances.size());
89+
}
90+
91+
private void updateClosingBalances(JdbcTemplate jdbcTemplate) {
92+
final List<Long> officeIds = trialBalanceRepository.findDistinctOfficeIdsWithNullClosingBalance();
93+
6894
for (Long officeId : officeIds) {
69-
String distinctAccountQuery = "select distinct(account_id) from m_trial_balance where office_id=? and closing_balance is null group by account_id";
70-
final List<Long> accountIds = jdbcTemplate.queryForList(distinctAccountQuery, Long.class, officeId);
71-
for (Long accountId : accountIds) {
72-
final String closingBalanceQuery = "select closing_balance from m_trial_balance where office_id=? and account_id=? and closing_balance "
73-
+ "is not null order by created_date desc, entry_date desc limit 1";
74-
List<BigDecimal> closingBalanceData = jdbcTemplate.queryForList(closingBalanceQuery, BigDecimal.class, officeId, accountId);
75-
List<TrialBalance> tbRows = trialBalanceRepositoryWrapper.findNewByOfficeAndAccount(officeId, accountId);
76-
BigDecimal closingBalance = null;
77-
if (!CollectionUtils.isEmpty(closingBalanceData)) {
78-
closingBalance = closingBalanceData.get(0);
79-
}
80-
if (CollectionUtils.isEmpty(closingBalanceData)) {
81-
closingBalance = BigDecimal.ZERO;
82-
for (TrialBalance row : tbRows) {
83-
closingBalance = closingBalance.add(row.getAmount());
84-
row.setClosingBalance(closingBalance);
85-
}
86-
} else {
87-
for (TrialBalance tbRow : tbRows) {
88-
if (closingBalance != null) {
89-
closingBalance = closingBalance.add(tbRow.getAmount());
90-
}
91-
tbRow.setClosingBalance(closingBalance);
92-
}
93-
}
94-
trialBalanceRepositoryWrapper.save(tbRows);
95+
updateClosingBalancesForOffice(jdbcTemplate, officeId);
96+
}
97+
}
98+
99+
private void updateClosingBalancesForOffice(JdbcTemplate jdbcTemplate, Long officeId) {
100+
final List<Long> accountIds = trialBalanceRepository.findDistinctAccountIdsWithNullClosingBalanceByOfficeId(officeId);
101+
102+
for (Long accountId : accountIds) {
103+
updateClosingBalanceForAccount(jdbcTemplate, officeId, accountId);
104+
}
105+
}
106+
107+
private void updateClosingBalanceForAccount(JdbcTemplate jdbcTemplate, Long officeId, Long accountId) {
108+
BigDecimal closingBalance = getPreviousClosingBalance(officeId, accountId);
109+
List<TrialBalance> tbRows = trialBalanceRepositoryWrapper.findNewByOfficeAndAccount(officeId, accountId);
110+
111+
updateTrialBalanceRows(tbRows, closingBalance);
112+
}
113+
114+
private BigDecimal getPreviousClosingBalance(Long officeId, Long accountId) {
115+
List<BigDecimal> closingBalanceData = trialBalanceRepository.findLastClosingBalance(officeId, accountId);
116+
return CollectionUtils.isEmpty(closingBalanceData) ? BigDecimal.ZERO : closingBalanceData.getFirst();
117+
}
118+
119+
private void updateTrialBalanceRows(List<TrialBalance> tbRows, BigDecimal initialClosingBalance) {
120+
BigDecimal closingBalance = initialClosingBalance;
121+
122+
for (TrialBalance row : tbRows) {
123+
if (closingBalance != null) {
124+
closingBalance = closingBalance.add(row.getAmount());
95125
}
126+
row.setClosingBalance(closingBalance);
96127
}
97-
return RepeatStatus.FINISHED;
98128
}
99129
}

fineract-accounting/src/main/java/org/apache/fineract/accounting/journalentry/domain/JournalEntryRepository.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.apache.fineract.accounting.journalentry.domain;
2020

21+
import java.time.LocalDate;
2122
import java.util.List;
2223
import org.springframework.data.jpa.repository.JpaRepository;
2324
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
@@ -41,4 +42,27 @@ public interface JournalEntryRepository extends JpaRepository<JournalEntry, Long
4142
@Query("select journalEntry from JournalEntry journalEntry where journalEntry.transactionId= :transactionId and journalEntry.reversed=false and journalEntry.entityType = :entityType order by journalEntry.transactionDate asc, journalEntry.createdDate asc, journalEntry.id asc")
4243
List<JournalEntry> findJournalEntries(@Param("transactionId") String transactionId, @Param("entityType") Integer entityType);
4344

45+
@Query("""
46+
SELECT DISTINCT je.transactionDate
47+
FROM JournalEntry je
48+
WHERE je.transactionDate>:afterDate
49+
""")
50+
List<LocalDate> findTransactionDatesAfter(LocalDate afterDate);
51+
52+
@Query("""
53+
SELECT je.office.id,
54+
je.glAccount.id,
55+
SUM(CASE WHEN je.type = 1
56+
THEN -1 * je.amount
57+
ELSE je.amount
58+
END),
59+
je.transactionDate,
60+
je.createdDate,
61+
SUM(je.amount)
62+
FROM JournalEntry je
63+
WHERE je.transactionDate = :transactionDate
64+
GROUP BY je.office.id, je.glAccount.id, je.transactionDate, je.createdDate
65+
""")
66+
List<Object[]> findTrialBalanceLinesForDate(@Param("transactionDate") LocalDate transactionDate);
67+
4468
}

0 commit comments

Comments
 (0)