FINERACT-2326: Refactor trial balance processing to reduce cognitive complexity
This commit is contained in:
+31
@@ -18,6 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.accounting.glaccount.domain;
|
package org.apache.fineract.accounting.glaccount.domain;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
@@ -28,4 +30,33 @@ public interface TrialBalanceRepository extends JpaRepository<TrialBalance, Long
|
|||||||
|
|
||||||
@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)
|
@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)
|
||||||
List<TrialBalance> findNewByOfficeAndAccount(@Param("officeId") Long officeId, @Param("accountId") Long accountId);
|
List<TrialBalance> findNewByOfficeAndAccount(@Param("officeId") Long officeId, @Param("accountId") Long accountId);
|
||||||
|
|
||||||
|
@Query("SELECT MAX(tb.transactionDate) FROM TrialBalance tb")
|
||||||
|
LocalDate findMaxCreatedDate();
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT tb.closingBalance
|
||||||
|
FROM TrialBalance tb
|
||||||
|
WHERE tb.officeId = :officeId
|
||||||
|
AND tb.glAccountId = :accountId
|
||||||
|
AND tb.closingBalance IS NOT NULL
|
||||||
|
ORDER BY tb.transactionDate DESC, tb.entryDate DESC
|
||||||
|
""")
|
||||||
|
List<BigDecimal> findLastClosingBalance(Long officeId, Long accountId);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT DISTINCT tb.officeId
|
||||||
|
FROM TrialBalance tb
|
||||||
|
WHERE tb.closingBalance IS NULL
|
||||||
|
""")
|
||||||
|
List<Long> findDistinctOfficeIdsWithNullClosingBalance();
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT DISTINCT tb.glAccountId
|
||||||
|
FROM TrialBalance tb
|
||||||
|
WHERE tb.officeId = :officeId
|
||||||
|
AND tb.closingBalance IS NULL
|
||||||
|
""")
|
||||||
|
List<Long> findDistinctAccountIdsWithNullClosingBalanceByOfficeId(Long officeId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-1
@@ -19,7 +19,9 @@
|
|||||||
package org.apache.fineract.accounting.glaccount.jobs.updatetrialbalancedetails;
|
package org.apache.fineract.accounting.glaccount.jobs.updatetrialbalancedetails;
|
||||||
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper;
|
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
||||||
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
|
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
|
||||||
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
import org.apache.fineract.infrastructure.jobs.service.JobName;
|
||||||
import org.springframework.batch.core.Job;
|
import org.springframework.batch.core.Job;
|
||||||
@@ -40,6 +42,8 @@ public class UpdateTrialBalanceDetailsConfig {
|
|||||||
private final PlatformTransactionManager transactionManager;
|
private final PlatformTransactionManager transactionManager;
|
||||||
private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
|
private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
|
||||||
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
|
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
|
||||||
|
private final TrialBalanceRepository trialBalanceRepository;
|
||||||
|
private final JournalEntryRepository journalEntryRepository;
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
protected Step updateTrialBalanceDetailsStep() {
|
protected Step updateTrialBalanceDetailsStep() {
|
||||||
@@ -55,6 +59,7 @@ public class UpdateTrialBalanceDetailsConfig {
|
|||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public UpdateTrialBalanceDetailsTasklet updateTrialBalanceDetailsTasklet() {
|
public UpdateTrialBalanceDetailsTasklet updateTrialBalanceDetailsTasklet() {
|
||||||
return new UpdateTrialBalanceDetailsTasklet(dataSourceServiceFactory, trialBalanceRepositoryWrapper);
|
return new UpdateTrialBalanceDetailsTasklet(dataSourceServiceFactory, trialBalanceRepositoryWrapper, trialBalanceRepository,
|
||||||
|
journalEntryRepository);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+79
-49
@@ -24,7 +24,9 @@ import java.util.List;
|
|||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.TrialBalance;
|
import org.apache.fineract.accounting.glaccount.domain.TrialBalance;
|
||||||
|
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepository;
|
||||||
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper;
|
import org.apache.fineract.accounting.glaccount.domain.TrialBalanceRepositoryWrapper;
|
||||||
|
import org.apache.fineract.accounting.journalentry.domain.JournalEntryRepository;
|
||||||
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
import org.apache.fineract.infrastructure.core.service.DateUtils;
|
||||||
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
import org.apache.fineract.infrastructure.core.service.ThreadLocalContextUtil;
|
||||||
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
|
import org.apache.fineract.infrastructure.core.service.database.RoutingDataSourceServiceFactory;
|
||||||
@@ -41,59 +43,87 @@ public class UpdateTrialBalanceDetailsTasklet implements Tasklet {
|
|||||||
|
|
||||||
private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
|
private final RoutingDataSourceServiceFactory dataSourceServiceFactory;
|
||||||
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
|
private final TrialBalanceRepositoryWrapper trialBalanceRepositoryWrapper;
|
||||||
|
private final TrialBalanceRepository trialBalanceRepository;
|
||||||
|
private final JournalEntryRepository journalEntryRepository;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
|
||||||
final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceServiceFactory.determineDataSourceService().retrieveDataSource());
|
final JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSourceServiceFactory.determineDataSourceService().retrieveDataSource());
|
||||||
final StringBuilder tbGapSqlBuilder = new StringBuilder(500);
|
|
||||||
tbGapSqlBuilder.append("select distinct(je.transaction_date) ").append("from acc_gl_journal_entry je ")
|
processTrialBalanceGaps(jdbcTemplate);
|
||||||
.append("where je.transaction_date > (select coalesce(MAX(created_date),'2010-01-01') from m_trial_balance)");
|
updateClosingBalances(jdbcTemplate);
|
||||||
final List<LocalDate> tbGaps = jdbcTemplate.queryForList(tbGapSqlBuilder.toString(), LocalDate.class);
|
|
||||||
for (LocalDate tbGap : tbGaps) {
|
|
||||||
int days = DateUtils.getExactDifferenceInDays(tbGap, DateUtils.getBusinessLocalDate());
|
|
||||||
if (days < 1) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
final StringBuilder sqlBuilder = new StringBuilder(600);
|
|
||||||
sqlBuilder.append("Insert Into m_trial_balance(office_id, account_id, Amount, entry_date, created_date,closing_balance) ")
|
|
||||||
.append("Select je.office_id, je.account_id, SUM(CASE WHEN je.type_enum=1 THEN (-1) * je.amount ELSE je.amount END) ")
|
|
||||||
.append("as Amount, Date(je.entry_date) as Entry_Date, je.transaction_date as Created_Date,sum(je.amount) as closing_balance ")
|
|
||||||
.append("from acc_gl_journal_entry je WHERE je.transaction_date = ? ")
|
|
||||||
.append("group by je.account_id, je.office_id, je.transaction_date, Date(je.entry_date)");
|
|
||||||
final int result = jdbcTemplate.update(sqlBuilder.toString(), tbGap);
|
|
||||||
log.debug("{}: Records affected by updateTrialBalanceDetails: {}", ThreadLocalContextUtil.getTenant().getName(), result);
|
|
||||||
}
|
|
||||||
String distinctOfficeQuery = "select distinct(office_id) from m_trial_balance where closing_balance is null group by office_id";
|
|
||||||
final List<Long> officeIds = jdbcTemplate.queryForList(distinctOfficeQuery, Long.class);
|
|
||||||
for (Long officeId : officeIds) {
|
|
||||||
String distinctAccountQuery = "select distinct(account_id) from m_trial_balance where office_id=? and closing_balance is null group by account_id";
|
|
||||||
final List<Long> accountIds = jdbcTemplate.queryForList(distinctAccountQuery, Long.class, officeId);
|
|
||||||
for (Long accountId : accountIds) {
|
|
||||||
final String closingBalanceQuery = "select closing_balance from m_trial_balance where office_id=? and account_id=? and closing_balance "
|
|
||||||
+ "is not null order by created_date desc, entry_date desc limit 1";
|
|
||||||
List<BigDecimal> closingBalanceData = jdbcTemplate.queryForList(closingBalanceQuery, BigDecimal.class, officeId, accountId);
|
|
||||||
List<TrialBalance> tbRows = trialBalanceRepositoryWrapper.findNewByOfficeAndAccount(officeId, accountId);
|
|
||||||
BigDecimal closingBalance = null;
|
|
||||||
if (!CollectionUtils.isEmpty(closingBalanceData)) {
|
|
||||||
closingBalance = closingBalanceData.get(0);
|
|
||||||
}
|
|
||||||
if (CollectionUtils.isEmpty(closingBalanceData)) {
|
|
||||||
closingBalance = BigDecimal.ZERO;
|
|
||||||
for (TrialBalance row : tbRows) {
|
|
||||||
closingBalance = closingBalance.add(row.getAmount());
|
|
||||||
row.setClosingBalance(closingBalance);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (TrialBalance tbRow : tbRows) {
|
|
||||||
if (closingBalance != null) {
|
|
||||||
closingBalance = closingBalance.add(tbRow.getAmount());
|
|
||||||
}
|
|
||||||
tbRow.setClosingBalance(closingBalance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
trialBalanceRepositoryWrapper.save(tbRows);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return RepeatStatus.FINISHED;
|
return RepeatStatus.FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void processTrialBalanceGaps(JdbcTemplate jdbcTemplate) {
|
||||||
|
LocalDate maxCreatedDate = trialBalanceRepository.findMaxCreatedDate();
|
||||||
|
LocalDate baselineDate = maxCreatedDate != null ? maxCreatedDate : LocalDate.of(2010, 1, 1);
|
||||||
|
List<LocalDate> tbGaps = journalEntryRepository.findTransactionDatesAfter(baselineDate);
|
||||||
|
for (LocalDate tbGap : tbGaps) {
|
||||||
|
if (DateUtils.getExactDifferenceInDays(tbGap, DateUtils.getBusinessLocalDate()) < 1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
insertTrialBalanceForDate(tbGap);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void insertTrialBalanceForDate(LocalDate tbGap) {
|
||||||
|
List<Object[]> rows = journalEntryRepository.findTrialBalanceLinesForDate(tbGap);
|
||||||
|
|
||||||
|
List<TrialBalance> trialBalances = rows.stream().map(row -> {
|
||||||
|
TrialBalance tb = new TrialBalance();
|
||||||
|
tb.setOfficeId((Long) row[0]);
|
||||||
|
tb.setGlAccountId((Long) row[1]);
|
||||||
|
tb.setAmount((BigDecimal) row[2]);
|
||||||
|
tb.setEntryDate((LocalDate) row[3]);
|
||||||
|
tb.setTransactionDate((LocalDate) row[4]);
|
||||||
|
tb.setClosingBalance((BigDecimal) row[5]);
|
||||||
|
return tb;
|
||||||
|
}).toList();
|
||||||
|
|
||||||
|
trialBalanceRepositoryWrapper.save(trialBalances);
|
||||||
|
|
||||||
|
log.debug("{}: Records affected by updateTrialBalanceDetails: {}", ThreadLocalContextUtil.getTenant().getName(),
|
||||||
|
trialBalances.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateClosingBalances(JdbcTemplate jdbcTemplate) {
|
||||||
|
final List<Long> officeIds = trialBalanceRepository.findDistinctOfficeIdsWithNullClosingBalance();
|
||||||
|
|
||||||
|
for (Long officeId : officeIds) {
|
||||||
|
updateClosingBalancesForOffice(jdbcTemplate, officeId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateClosingBalancesForOffice(JdbcTemplate jdbcTemplate, Long officeId) {
|
||||||
|
final List<Long> accountIds = trialBalanceRepository.findDistinctAccountIdsWithNullClosingBalanceByOfficeId(officeId);
|
||||||
|
|
||||||
|
for (Long accountId : accountIds) {
|
||||||
|
updateClosingBalanceForAccount(jdbcTemplate, officeId, accountId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateClosingBalanceForAccount(JdbcTemplate jdbcTemplate, Long officeId, Long accountId) {
|
||||||
|
BigDecimal closingBalance = getPreviousClosingBalance(officeId, accountId);
|
||||||
|
List<TrialBalance> tbRows = trialBalanceRepositoryWrapper.findNewByOfficeAndAccount(officeId, accountId);
|
||||||
|
|
||||||
|
updateTrialBalanceRows(tbRows, closingBalance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BigDecimal getPreviousClosingBalance(Long officeId, Long accountId) {
|
||||||
|
List<BigDecimal> closingBalanceData = trialBalanceRepository.findLastClosingBalance(officeId, accountId);
|
||||||
|
return CollectionUtils.isEmpty(closingBalanceData) ? BigDecimal.ZERO : closingBalanceData.getFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTrialBalanceRows(List<TrialBalance> tbRows, BigDecimal initialClosingBalance) {
|
||||||
|
BigDecimal closingBalance = initialClosingBalance;
|
||||||
|
|
||||||
|
for (TrialBalance row : tbRows) {
|
||||||
|
if (closingBalance != null) {
|
||||||
|
closingBalance = closingBalance.add(row.getAmount());
|
||||||
|
}
|
||||||
|
row.setClosingBalance(closingBalance);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
@@ -18,6 +18,7 @@
|
|||||||
*/
|
*/
|
||||||
package org.apache.fineract.accounting.journalentry.domain;
|
package org.apache.fineract.accounting.journalentry.domain;
|
||||||
|
|
||||||
|
import java.time.LocalDate;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||||
@@ -41,4 +42,27 @@ public interface JournalEntryRepository extends JpaRepository<JournalEntry, Long
|
|||||||
@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")
|
@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")
|
||||||
List<JournalEntry> findJournalEntries(@Param("transactionId") String transactionId, @Param("entityType") Integer entityType);
|
List<JournalEntry> findJournalEntries(@Param("transactionId") String transactionId, @Param("entityType") Integer entityType);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT DISTINCT je.transactionDate
|
||||||
|
FROM JournalEntry je
|
||||||
|
WHERE je.transactionDate>:afterDate
|
||||||
|
""")
|
||||||
|
List<LocalDate> findTransactionDatesAfter(LocalDate afterDate);
|
||||||
|
|
||||||
|
@Query("""
|
||||||
|
SELECT je.office.id,
|
||||||
|
je.glAccount.id,
|
||||||
|
SUM(CASE WHEN je.type = 1
|
||||||
|
THEN -1 * je.amount
|
||||||
|
ELSE je.amount
|
||||||
|
END),
|
||||||
|
je.transactionDate,
|
||||||
|
je.createdDate,
|
||||||
|
SUM(je.amount)
|
||||||
|
FROM JournalEntry je
|
||||||
|
WHERE je.transactionDate = :transactionDate
|
||||||
|
GROUP BY je.office.id, je.glAccount.id, je.transactionDate, je.createdDate
|
||||||
|
""")
|
||||||
|
List<Object[]> findTrialBalanceLinesForDate(@Param("transactionDate") LocalDate transactionDate);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user