This is an automated email from the ASF dual-hosted git repository.

danwatford pushed a commit to branch release22.01
in repository https://gitbox.apache.org/repos/asf/ofbiz-framework.git

commit 022ad6b596e18d4d162218af3cb83f6cb175a7e4
Author: Daniel Watford <dan...@watfordconsulting.com>
AuthorDate: Tue Feb 7 15:03:55 2023 +0000

    Improvement: Simplify code used to prepare the balance sheet (OFBIZ-12753)
    
    Refactored duplicate code into functions with the intention of improving
    readability of balance sheet related code.
---
 .../groovyScripts/reports/BalanceSheet.groovy      | 577 +++++++++------------
 1 file changed, 252 insertions(+), 325 deletions(-)

diff --git a/applications/accounting/groovyScripts/reports/BalanceSheet.groovy 
b/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
index a2dd416c7c..bee97d8e16 100644
--- a/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
+++ b/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
@@ -24,6 +24,7 @@ import org.apache.ofbiz.base.util.UtilMisc
 import org.apache.ofbiz.base.util.UtilProperties
 import org.apache.ofbiz.entity.GenericValue
 import org.apache.ofbiz.entity.condition.EntityCondition
+import org.apache.ofbiz.entity.condition.EntityExpr
 import org.apache.ofbiz.entity.condition.EntityOperator
 import org.apache.ofbiz.party.party.PartyWorker
 
@@ -63,10 +64,6 @@ GenericValue longtermAssetGlAccountClass = 
from("GlAccountClass").where("glAccou
 List longtermAssetAccountClassIds = 
UtilAccounting.getDescendantGlAccountClassIds(longtermAssetGlAccountClass)
 GenericValue currentLiabilityGlAccountClass = 
from("GlAccountClass").where("glAccountClassId", 
"CURRENT_LIABILITY").cache(true).queryOne()
 List currentLiabilityAccountClassIds = 
UtilAccounting.getDescendantGlAccountClassIds(currentLiabilityGlAccountClass)
-GenericValue accumDepreciationGlAccountClass = 
from("GlAccountClass").where("glAccountClassId", 
"ACCUM_DEPRECIATION").cache(true).queryOne()
-List accumDepreciationAccountClassIds = 
UtilAccounting.getDescendantGlAccountClassIds(accumDepreciationGlAccountClass)
-GenericValue accumAmortizationGlAccountClass = 
from("GlAccountClass").where("glAccountClassId", 
"ACCUM_AMORTIZATION").cache(true).queryOne()
-List accumAmortizationAccountClassIds = 
UtilAccounting.getDescendantGlAccountClassIds(accumAmortizationGlAccountClass)
 
 // Find the last closed time period to get the fromDate for the transactions 
in the current period and the ending balances of the last closed period
 Map lastClosedTimePeriodResult = runService('findLastClosedDate', 
["organizationPartyId": organizationPartyId, "findDate": thruDate,"userLogin": 
userLogin])
@@ -74,359 +71,289 @@ Timestamp fromDate = 
(Timestamp)lastClosedTimePeriodResult.lastClosedDate
 if (!fromDate) {
     return
 }
+
 GenericValue lastClosedTimePeriod = 
(GenericValue)lastClosedTimePeriodResult.lastClosedTimePeriod
-// Get the opening balances of all the accounts
-Map assetOpeningBalances = [:]
-Map contraAssetOpeningBalances = [:]
-Map currentAssetOpeningBalances = [:]
-Map longtermAssetOpeningBalances = [:]
-Map liabilityOpeningBalances = [:]
-Map currentLiabilityOpeningBalances = [:]
-Map equityOpeningBalances = [:]
-if (lastClosedTimePeriod) {
-    List timePeriodAndExprs = []
-    
timePeriodAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, assetAccountClassIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS, lastClosedTimePeriod.customTimePeriodId))
-    List lastTimePeriodHistories = 
from("GlAccountAndHistory").where(timePeriodAndExprs).queryList()
-    lastTimePeriodHistories.each { lastTimePeriodHistory ->
-        Map accountMap = UtilMisc.toMap("glAccountId", 
lastTimePeriodHistory.glAccountId, "accountCode", 
lastTimePeriodHistory.accountCode, "accountName", 
lastTimePeriodHistory.accountName, "balance", 
lastTimePeriodHistory.getBigDecimal("endingBalance"), "D", 
lastTimePeriodHistory.getBigDecimal("postedDebits"), "C", 
lastTimePeriodHistory.getBigDecimal("postedCredits"))
-        assetOpeningBalances.put(lastTimePeriodHistory.glAccountId, accountMap)
-    }
-    timePeriodAndExprs = []
-    
timePeriodAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, contraAssetAccountClassIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS, lastClosedTimePeriod.customTimePeriodId))
-    lastTimePeriodHistories = 
from("GlAccountAndHistory").where(timePeriodAndExprs).queryList()
-    lastTimePeriodHistories.each { lastTimePeriodHistory ->
-        Map accountMap = UtilMisc.toMap("glAccountId", 
lastTimePeriodHistory.glAccountId, "accountCode", 
lastTimePeriodHistory.accountCode, "accountName", 
lastTimePeriodHistory.accountName, "balance", 
lastTimePeriodHistory.getBigDecimal("endingBalance"), "D", 
lastTimePeriodHistory.getBigDecimal("postedDebits"), "C", 
lastTimePeriodHistory.getBigDecimal("postedCredits"))
-        contraAssetOpeningBalances.put(lastTimePeriodHistory.glAccountId, 
accountMap)
-    }
-    timePeriodAndExprs = []
-    
timePeriodAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, liabilityAccountClassIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS, lastClosedTimePeriod.customTimePeriodId))
-    lastTimePeriodHistories = 
from("GlAccountAndHistory").where(timePeriodAndExprs).queryList()
-    lastTimePeriodHistories.each { lastTimePeriodHistory ->
-        Map accountMap = UtilMisc.toMap("glAccountId", 
lastTimePeriodHistory.glAccountId, "accountCode", 
lastTimePeriodHistory.accountCode, "accountName", 
lastTimePeriodHistory.accountName, "balance", 
lastTimePeriodHistory.getBigDecimal("endingBalance"), "D", 
lastTimePeriodHistory.getBigDecimal("postedDebits"), "C", 
lastTimePeriodHistory.getBigDecimal("postedCredits"))
-        liabilityOpeningBalances.put(lastTimePeriodHistory.glAccountId, 
accountMap)
-    }
-    timePeriodAndExprs = []
-    
timePeriodAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, equityAccountClassIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS, lastClosedTimePeriod.customTimePeriodId))
-    lastTimePeriodHistories = 
from("GlAccountAndHistory").where(timePeriodAndExprs).queryList()
-    lastTimePeriodHistories.each { lastTimePeriodHistory ->
-        Map accountMap = UtilMisc.toMap("glAccountId", 
lastTimePeriodHistory.glAccountId, "accountCode", 
lastTimePeriodHistory.accountCode, "accountName", 
lastTimePeriodHistory.accountName, "balance", 
lastTimePeriodHistory.getBigDecimal("endingBalance"), "D", 
lastTimePeriodHistory.getBigDecimal("postedDebits"), "C", 
lastTimePeriodHistory.getBigDecimal("postedCredits"))
-        equityOpeningBalances.put(lastTimePeriodHistory.glAccountId, 
accountMap)
-    }
-    timePeriodAndExprs = []
-    
timePeriodAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, currentAssetAccountClassIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS, lastClosedTimePeriod.customTimePeriodId))
-    lastTimePeriodHistories = 
from("GlAccountAndHistory").where(timePeriodAndExprs).queryList()
-    lastTimePeriodHistories.each { lastTimePeriodHistory ->
-        Map accountMap = UtilMisc.toMap("glAccountId", 
lastTimePeriodHistory.glAccountId, "accountCode", 
lastTimePeriodHistory.accountCode, "accountName", 
lastTimePeriodHistory.accountName, "balance", 
lastTimePeriodHistory.getBigDecimal("endingBalance"), "D", 
lastTimePeriodHistory.getBigDecimal("postedDebits"), "C", 
lastTimePeriodHistory.getBigDecimal("postedCredits"))
-        currentAssetOpeningBalances.put(lastTimePeriodHistory.glAccountId, 
accountMap)
-    }
-    timePeriodAndExprs = []
-    
timePeriodAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, longtermAssetAccountClassIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS, lastClosedTimePeriod.customTimePeriodId))
-    lastTimePeriodHistories = 
from("GlAccountAndHistory").where(timePeriodAndExprs).queryList()
-    lastTimePeriodHistories.each { lastTimePeriodHistory ->
-        Map accountMap = UtilMisc.toMap("glAccountId", 
lastTimePeriodHistory.glAccountId, "accountCode", 
lastTimePeriodHistory.accountCode, "accountName", 
lastTimePeriodHistory.accountName, "balance", 
lastTimePeriodHistory.getBigDecimal("endingBalance"), "D", 
lastTimePeriodHistory.getBigDecimal("postedDebits"), "C", 
lastTimePeriodHistory.getBigDecimal("postedCredits"))
-        longtermAssetOpeningBalances.put(lastTimePeriodHistory.glAccountId, 
accountMap)
-    }
-    timePeriodAndExprs = []
-    
timePeriodAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, currentLiabilityAccountClassIds))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO))
-    timePeriodAndExprs.add(EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS, lastClosedTimePeriod.customTimePeriodId))
-    lastTimePeriodHistories = 
from("GlAccountAndHistory").where(timePeriodAndExprs).queryList()
-    lastTimePeriodHistories.each { lastTimePeriodHistory ->
-        Map accountMap = UtilMisc.toMap("glAccountId", 
lastTimePeriodHistory.glAccountId, "accountCode", 
lastTimePeriodHistory.accountCode, "accountName", 
lastTimePeriodHistory.accountName, "balance", 
lastTimePeriodHistory.getBigDecimal("endingBalance"), "D", 
lastTimePeriodHistory.getBigDecimal("postedDebits"), "C", 
lastTimePeriodHistory.getBigDecimal("postedCredits"))
-        currentLiabilityOpeningBalances.put(lastTimePeriodHistory.glAccountId, 
accountMap)
+
+class AccountBalance {
+    String glAccountId
+    String accountCode
+    String accountName
+    BigDecimal balance
+    public BigDecimal D
+    public BigDecimal C
+    def asMap() {
+        [glAccountId: glAccountId, accountCode: accountCode, accountName: 
accountName,
+         balance: balance, D: D, C: C]
     }
 }
 
-List balanceTotalList = []
+/**
+ * Closure to retrieve a map of AccountBalances for the organization's GL 
Account which were active during the most
+ * recently closed time period - i.e. those accounts for which 
GlAccountHistory exists.
+ *
+ * AccountBalances are returned for those accounts which match the 
accountClassIds parameter.
+ *
+ * @param accountClassIds The set of GL Account Class IDs to return Ending 
Balances for.
+ *
+ * @return Map of GL Account IDs to AccountBalances for the 
lastClosedTimePeriod, or an empty map if
+ *  lastClosedTimePeriod is null
+ */
+def getLastPeriodClosingBalancesForAccountClassIds = { List<String> 
accountClassIds ->
+    Map<String, AccountBalance> retVal = [:]
 
-List mainAndExprs = []
-mainAndExprs.add(EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds))
-mainAndExprs.add(EntityCondition.makeCondition("isPosted", 
EntityOperator.EQUALS, "Y"))
-mainAndExprs.add(EntityCondition.makeCondition("glFiscalTypeId", 
EntityOperator.EQUALS, glFiscalTypeId))
-mainAndExprs.add(EntityCondition.makeCondition("acctgTransTypeId", 
EntityOperator.NOT_EQUAL, "PERIOD_CLOSING"))
-mainAndExprs.add(EntityCondition.makeCondition("transactionDate", 
EntityOperator.GREATER_THAN_EQUAL_TO, fromDate))
-mainAndExprs.add(EntityCondition.makeCondition("transactionDate", 
EntityOperator.LESS_THAN, thruDate))
+    if (lastClosedTimePeriod) {
+        def lastPeriodHistoryConditions = [
+                EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds),
+                EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, accountClassIds),
+                EntityCondition.makeCondition("endingBalance", 
EntityOperator.NOT_EQUAL, BigDecimal.ZERO),
+                EntityCondition.makeCondition("customTimePeriodId", 
EntityOperator.EQUALS,
+                        lastClosedTimePeriod.customTimePeriodId)
+        ]
 
-// ASSETS
-// account balances
-accountBalanceList = []
-transactionTotals = []
-balanceTotal = BigDecimal.ZERO
-List assetAndExprs = mainAndExprs as LinkedList
-assetAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, assetAccountClassIds))
-transactionTotals = select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", 
"amount").from("AcctgTransEntrySums").where(assetAndExprs).orderBy("glAccountId").queryList()
-transactionTotalsMap = [:]
-transactionTotalsMap.putAll(assetOpeningBalances)
-transactionTotals.each { transactionTotal ->
-    Map accountMap = 
(Map)transactionTotalsMap.get(transactionTotal.glAccountId)
-    if (!accountMap) {
-        accountMap = UtilMisc.makeMapWritable(transactionTotal)
-        accountMap.remove("debitCreditFlag")
-        accountMap.remove("amount")
-        accountMap.put("D", BigDecimal.ZERO)
-        accountMap.put("C", BigDecimal.ZERO)
-        accountMap.put("balance", BigDecimal.ZERO)
+        
from("GlAccountAndHistory").where(lastPeriodHistoryConditions).queryList().collect
 {
+            history ->
+                new AccountBalance(
+                        glAccountId: history.glAccountId,
+                        accountCode: history.accountCode,
+                        accountName: history.accountName,
+                        balance: history.getBigDecimal("endingBalance"),
+                        D: history.getBigDecimal("postedDebits"),
+                        C: history.getBigDecimal("postedCredits")
+                )
+        }.each {
+            retVal.put(it.glAccountId, it)
+        }
     }
-    UtilMisc.addToBigDecimalInMap(accountMap, 
transactionTotal.debitCreditFlag, transactionTotal.amount)
-    BigDecimal debitAmount = (BigDecimal)accountMap.get("D")
-    BigDecimal creditAmount = (BigDecimal)accountMap.get("C")
-    // assets are accounts of class DEBIT: the balance is given by debits 
minus credits
-    BigDecimal balance = debitAmount.subtract(creditAmount)
-    accountMap.put("balance", balance)
-    transactionTotalsMap.put(transactionTotal.glAccountId, accountMap)
+
+    retVal
 }
-accountBalanceList = UtilMisc.sortMaps(transactionTotalsMap.values().asList(), 
UtilMisc.toList("accountCode"))
-accountBalanceList.each { accountBalance ->
-    balanceTotal = balanceTotal + accountBalance.balance
+
+// Get the opening balances of all the accounts
+def assetOpeningBalances = 
getLastPeriodClosingBalancesForAccountClassIds(assetAccountClassIds)
+def contraAssetOpeningBalances = 
getLastPeriodClosingBalancesForAccountClassIds(contraAssetAccountClassIds)
+def currentAssetOpeningBalances = 
getLastPeriodClosingBalancesForAccountClassIds(currentAssetAccountClassIds)
+def longtermAssetOpeningBalances = 
getLastPeriodClosingBalancesForAccountClassIds(longtermAssetAccountClassIds)
+def liabilityOpeningBalances = 
getLastPeriodClosingBalancesForAccountClassIds(liabilityAccountClassIds)
+def currentLiabilityOpeningBalances = 
getLastPeriodClosingBalancesForAccountClassIds(currentLiabilityAccountClassIds)
+def equityOpeningBalances = 
getLastPeriodClosingBalancesForAccountClassIds(equityAccountClassIds)
+
+List balanceTotalList = []
+
+class AccountEntrySum {
+    String glAccountId
+    String accountCode
+    String accountName
+    String debitCreditFlag
+    BigDecimal amount
 }
-context.assetAccountBalanceList = accountBalanceList
-context.assetAccountBalanceList.add(UtilMisc.toMap("accountName", 
uiLabelMap.AccountingTotalAssets, "balance", balanceTotal))
-context.assetBalanceTotal = balanceTotal
 
-// CURRENT ASSETS
-// account balances
-accountBalanceList = []
-transactionTotals = []
-balanceTotal = BigDecimal.ZERO
-List currentAssetAndExprs = mainAndExprs as LinkedList
-currentAssetAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, currentAssetAccountClassIds))
-transactionTotals = select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", 
"amount").from("AcctgTransEntrySums").where(currentAssetAndExprs).orderBy("glAccountId").queryList()
-transactionTotalsMap = [:]
-transactionTotalsMap.putAll(currentAssetOpeningBalances)
-transactionTotals.each { transactionTotal ->
-    Map accountMap = 
(Map)transactionTotalsMap.get(transactionTotal.glAccountId)
-    if (!accountMap) {
-        accountMap = UtilMisc.makeMapWritable(transactionTotal)
-        accountMap.remove("debitCreditFlag")
-        accountMap.remove("amount")
-        accountMap.put("D", BigDecimal.ZERO)
-        accountMap.put("C", BigDecimal.ZERO)
-        accountMap.put("balance", BigDecimal.ZERO)
-    }
-    UtilMisc.addToBigDecimalInMap(accountMap, 
transactionTotal.debitCreditFlag, transactionTotal.amount)
-    BigDecimal debitAmount = (BigDecimal)accountMap.get("D")
-    BigDecimal creditAmount = (BigDecimal)accountMap.get("C")
-    // assets are accounts of class DEBIT: the balance is given by debits 
minus credits
-    BigDecimal balance = debitAmount.subtract(creditAmount)
-    accountMap.put("balance", balance)
-    transactionTotalsMap.put(transactionTotal.glAccountId, accountMap)
+/**
+ * Retrieve a collection of AccountEntrySum objects corresponding to the 
AcctgTransEntrySums entities query controlled
+ * by the given conditions.
+ *
+ * @param conditions The list of conditions to be ANDed together and form the 
WHERE clause for the query of
+ * AcctgTransEntrySums.
+ *
+ * @return A collection of AccountEntrySum objects for the conditions.
+ */
+def getAccountEntrySumsForCondition = { Collection<EntityExpr> conditions ->
+    from("AcctgTransEntrySums")
+            .where(conditions)
+            .orderBy("glAccountId")
+            .select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", "amount")
+            .queryList()
+            .collect { entrySum ->
+                new AccountEntrySum(
+                        glAccountId: entrySum.glAccountId,
+                        accountName: entrySum.accountName,
+                        accountCode: entrySum.accountCode,
+                        debitCreditFlag: entrySum.debitCreditFlag,
+                        amount: entrySum.getBigDecimal("amount")
+                )
+            }
 }
-accountBalanceList = UtilMisc.sortMaps(transactionTotalsMap.values().asList(), 
UtilMisc.toList("accountCode"))
-accountBalanceList.each { accountBalance ->
-    balanceTotal = balanceTotal + accountBalance.balance
+
+/**
+ * Retrieve a collection of AccountEntrySum objects corresponding to the 
organization's AcctgTransEntrySums entities
+ * which match the given collection of Account Class IDs.
+ *
+ * @param accountClassIds The collection of Account Class IDs to filter by.
+ *
+ * @return A collection of AccountEntrySum objects corresponding to the given 
accountClassIds.
+ */
+def getAccountEntrySumsForClassIds = { Collection<String> accountClassIds ->
+    def conditions = [
+            EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, accountClassIds),
+            EntityCondition.makeCondition("organizationPartyId", 
EntityOperator.IN, partyIds),
+            EntityCondition.makeCondition("isPosted", EntityOperator.EQUALS, 
"Y"),
+            EntityCondition.makeCondition("glFiscalTypeId", 
EntityOperator.EQUALS, glFiscalTypeId),
+            EntityCondition.makeCondition("acctgTransTypeId", 
EntityOperator.NOT_EQUAL, "PERIOD_CLOSING"),
+            EntityCondition.makeCondition("transactionDate", 
EntityOperator.GREATER_THAN_EQUAL_TO, fromDate),
+            EntityCondition.makeCondition("transactionDate", 
EntityOperator.LESS_THAN, thruDate)
+    ]
+
+    getAccountEntrySumsForCondition(conditions)
 }
-context.currentAssetBalanceTotal = balanceTotal
-balanceTotalList.add(UtilMisc.toMap("totalName", "AccountingCurrentAssets", 
"balance", balanceTotal))
 
-// LONGTERM ASSETS
-// account balances
-accountBalanceList = []
-transactionTotals = []
-balanceTotal = BigDecimal.ZERO
-List longtermAssetAndExprs = mainAndExprs as LinkedList
-longtermAssetAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, longtermAssetAccountClassIds))
-transactionTotals = select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", 
"amount").from("AcctgTransEntrySums").where(longtermAssetAndExprs).orderBy("glAccountId").queryList()
-transactionTotalsMap = [:]
-transactionTotalsMap.putAll(longtermAssetOpeningBalances)
-transactionTotals.each { transactionTotal ->
-    Map accountMap = 
(Map)transactionTotalsMap.get(transactionTotal.glAccountId)
-    if (!accountMap) {
-        accountMap = UtilMisc.makeMapWritable(transactionTotal)
-        accountMap.remove("debitCreditFlag")
-        accountMap.remove("amount")
-        accountMap.put("D", BigDecimal.ZERO)
-        accountMap.put("C", BigDecimal.ZERO)
-        accountMap.put("balance", BigDecimal.ZERO)
+enum RootClass {DEBIT, CREDIT}
+
+/**
+ * Calculates balances of the organization's GL Accounts which correspond to 
the given collection of Account Class IDs.
+ * Balances are calculated by taking each account's opening balance from the 
given Map, and then adding all debit and
+ * credit transaction entries from the current time period.
+ *
+ * @param openingBalances Map of GL Account IDs to AccountBalance objects 
representing the opening balance of the GL
+ * Account for the current time period.
+ * @param accountClassIds The collection of Account Class IDs used to define 
the queried GL Accounts.
+ * @param rootClass Define whether the collection of Account Class IDs are 
ultimately Debit or Credit accounts. This
+ * controls how the balance of the account is calculated:
+ *  Debit account balance = totalDebits - totalCredits
+ *  Credit account balance = totalCredits - totalDebits
+ */
+def calculateBalances = { Map<String, AccountBalance> openingBalances,
+                          Collection<String> accountClassIds,
+                          RootClass rootClass ->
+
+    def accountBalancesByGlAccountId = [:] << openingBalances
+
+    getAccountEntrySumsForClassIds(accountClassIds).each { entrySum ->
+        def existingAccountBalance = accountBalancesByGlAccountId.getOrDefault(
+                entrySum.glAccountId,
+                new AccountBalance(
+                        glAccountId: entrySum.glAccountId,
+                        accountCode: entrySum.accountCode,
+                        accountName: entrySum.accountName,
+                        balance: 0.0,
+                        D: 0.0,
+                        C: 0.0,
+                ))
+
+        def combinedDebitAmount = existingAccountBalance.D +
+                (entrySum.debitCreditFlag == "D" ? entrySum.amount : 0.0)
+        def combinedCreditAmount = existingAccountBalance.C +
+                (entrySum.debitCreditFlag == "C" ? entrySum.amount : 0.0)
+
+        def combinedBalance = rootClass == RootClass.DEBIT ?
+                combinedDebitAmount - combinedCreditAmount :
+                combinedCreditAmount - combinedDebitAmount
+
+        accountBalancesByGlAccountId.put(entrySum.glAccountId, new 
AccountBalance(
+                glAccountId: entrySum.glAccountId,
+                accountCode: entrySum.accountCode,
+                accountName: entrySum.accountName,
+                balance: combinedBalance,
+                D: combinedDebitAmount,
+                C: combinedCreditAmount
+        ))
     }
-    UtilMisc.addToBigDecimalInMap(accountMap, 
transactionTotal.debitCreditFlag, transactionTotal.amount)
-    BigDecimal debitAmount = (BigDecimal)accountMap.get("D")
-    BigDecimal creditAmount = (BigDecimal)accountMap.get("C")
-    // assets are accounts of class DEBIT: the balance is given by debits 
minus credits
-    BigDecimal balance = debitAmount.subtract(creditAmount)
-    accountMap.put("balance", balance)
-    transactionTotalsMap.put(transactionTotal.glAccountId, accountMap)
-}
-accountBalanceList = UtilMisc.sortMaps(transactionTotalsMap.values().asList(), 
UtilMisc.toList("accountCode"))
-accountBalanceList.each { accountBalance ->
-    balanceTotal = balanceTotal + accountBalance.balance
+
+    accountBalancesByGlAccountId
 }
-context.longtermAssetBalanceTotal = balanceTotal
-balanceTotalList.add(UtilMisc.toMap("totalName", "AccountingLongTermAssets", 
"balance", balanceTotal))
 
-// CONTRA ASSETS
-// account balances
-accountBalanceList = []
-transactionTotals = []
-balanceTotal = BigDecimal.ZERO
-List contraAssetAndExprs = mainAndExprs as LinkedList
-contraAssetAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, contraAssetAccountClassIds))
-transactionTotals = select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", 
"amount").from("AcctgTransEntrySums").where(contraAssetAndExprs).orderBy("glAccountId").queryList()
-
-transactionTotalsMap = [:]
-transactionTotalsMap.putAll(contraAssetOpeningBalances)
-transactionTotals.each { transactionTotal ->
-    Map accountMap = 
(Map)transactionTotalsMap.get(transactionTotal.glAccountId)
-    if (!accountMap) {
-        accountMap = UtilMisc.makeMapWritable(transactionTotal)
-        accountMap.remove("debitCreditFlag")
-        accountMap.remove("amount")
-        accountMap.put("D", BigDecimal.ZERO)
-        accountMap.put("C", BigDecimal.ZERO)
-        accountMap.put("balance", BigDecimal.ZERO)
-    }
-    UtilMisc.addToBigDecimalInMap(accountMap, 
transactionTotal.debitCreditFlag, transactionTotal.amount)
-    BigDecimal debitAmount = (BigDecimal)accountMap.get("D")
-    BigDecimal creditAmount = (BigDecimal)accountMap.get("C")
-    // contra assets are accounts of class CREDIT: the balance is given by 
credits minus debits
-    BigDecimal balance = debitAmount.subtract(creditAmount)
-    accountMap.put("balance", balance)
-    transactionTotalsMap.put(transactionTotal.glAccountId, accountMap)
+static def sortAccountBalancesConvertToMaps(Collection<AccountBalance> 
accountBalances) {
+    accountBalances.sort { a, b -> (a.accountCode <=> b.accountCode) }
+            .collect { it.asMap() }
 }
-accountBalanceList = UtilMisc.sortMaps(transactionTotalsMap.values().asList(), 
UtilMisc.toList("accountCode"))
-accountBalanceList.each { accountBalance ->
-    balanceTotal = balanceTotal + accountBalance.balance
+
+static def sumAccountBalances(Collection<AccountBalance> accountBalances) {
+    accountBalances.collect { it.balance }
+            .inject(BigDecimal.ZERO) { acc, val -> acc + val }
 }
-context.assetAccountBalanceList.addAll(accountBalanceList)
-context.assetAccountBalanceList.add(UtilMisc.toMap("accountName", 
uiLabelMap.AccountingTotalAccumulatedDepreciation, "balance", balanceTotal))
-context.contraAssetBalanceTotal = balanceTotal
-balanceTotalList.add(UtilMisc.toMap("totalName", 
"AccountingTotalAccumulatedDepreciation", "balance", balanceTotal))
-balanceTotalList.add(UtilMisc.toMap("totalName", "AccountingTotalAssets", 
"balance", (context.currentAssetBalanceTotal + 
context.longtermAssetBalanceTotal + balanceTotal)))
+
+// ASSETS
+def assetAccountBalances = calculateBalances(assetOpeningBalances, 
assetAccountClassIds, RootClass.DEBIT)
+def assetBalanceTotal = sumAccountBalances(assetAccountBalances.values())
+def assetAccountBalanceList = 
sortAccountBalancesConvertToMaps(assetAccountBalances.values())
+assetAccountBalanceList.add([accountName: uiLabelMap.AccountingTotalAssets, 
balance: assetBalanceTotal]
+        as LinkedHashMap<String, Serializable>)
+
+// CURRENT ASSETS
+def currentAssetAccountBalances =
+        calculateBalances(currentAssetOpeningBalances, 
currentAssetAccountClassIds, RootClass.DEBIT)
+def currentAssetBalanceTotal = 
sumAccountBalances(currentAssetAccountBalances.values())
+
+// LONGTERM ASSETS
+def longtermAssetAccountBalances =
+        calculateBalances(longtermAssetOpeningBalances, 
longtermAssetAccountClassIds, RootClass.DEBIT)
+def longtermAssetBalanceTotal = 
sumAccountBalances(longtermAssetAccountBalances.values())
+
+// CONTRA ASSETS
+// Contra assets are accounts of class CREDIT, but for the purposes of the 
balance sheet, they will be listed alongside
+// regular asset accounts in order to offset the total of all assets. We 
therefore, when calculating balances, treat
+// these accounts as DEBIT accounts, resulting in zero or negative balances.
+def contraAssetAccountBalances =
+        calculateBalances(contraAssetOpeningBalances, 
contraAssetAccountClassIds, RootClass.DEBIT)
+def contraAssetBalanceTotal = 
sumAccountBalances(contraAssetAccountBalances.values())
+def contraAssetAccountBalanceList = 
sortAccountBalancesConvertToMaps(contraAssetAccountBalances.values())
+assetAccountBalanceList.addAll(contraAssetAccountBalanceList)
+assetAccountBalanceList.add(["accountName": 
uiLabelMap.AccountingTotalAccumulatedDepreciation,
+                             "balance": contraAssetBalanceTotal]
+        as LinkedHashMap<String, Serializable>)
 
 // LIABILITY
-// account balances
-accountBalanceList = []
-transactionTotals = []
-balanceTotal = BigDecimal.ZERO
-List liabilityAndExprs = mainAndExprs as LinkedList
-liabilityAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, liabilityAccountClassIds))
-transactionTotals = select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", 
"amount").from("AcctgTransEntrySums").where(liabilityAndExprs).orderBy("glAccountId").queryList()
-transactionTotalsMap = [:]
-transactionTotalsMap.putAll(liabilityOpeningBalances)
-transactionTotals.each { transactionTotal ->
-    Map accountMap = 
(Map)transactionTotalsMap.get(transactionTotal.glAccountId)
-    if (!accountMap) {
-        accountMap = UtilMisc.makeMapWritable(transactionTotal)
-        accountMap.remove("debitCreditFlag")
-        accountMap.remove("amount")
-        accountMap.put("D", BigDecimal.ZERO)
-        accountMap.put("C", BigDecimal.ZERO)
-        accountMap.put("balance", BigDecimal.ZERO)
-    }
-    UtilMisc.addToBigDecimalInMap(accountMap, 
transactionTotal.debitCreditFlag, transactionTotal.amount)
-    BigDecimal debitAmount = (BigDecimal)accountMap.get("D")
-    BigDecimal creditAmount = (BigDecimal)accountMap.get("C")
-    // liabilities are accounts of class CREDIT: the balance is given by 
credits minus debits
-    BigDecimal balance = creditAmount.subtract(debitAmount)
-    accountMap.put("balance", balance)
-    transactionTotalsMap.put(transactionTotal.glAccountId, accountMap)
-}
-accountBalanceList = UtilMisc.sortMaps(transactionTotalsMap.values().asList(), 
UtilMisc.toList("accountCode"))
-accountBalanceList.each { accountBalance ->
-    balanceTotal = balanceTotal + accountBalance.balance
-}
-context.liabilityAccountBalanceList = accountBalanceList
-context.liabilityAccountBalanceList.add(UtilMisc.toMap("accountName", 
uiLabelMap.AccountingTotalLiabilities, "balance", balanceTotal))
-context.liabilityBalanceTotal = balanceTotal
+def liabilityAccountBalances = calculateBalances(liabilityOpeningBalances, 
liabilityAccountClassIds, RootClass.CREDIT)
+def liabilityBalanceTotal = 
sumAccountBalances(liabilityAccountBalances.values())
+def liabilityAccountBalanceList = 
sortAccountBalancesConvertToMaps(liabilityAccountBalances.values())
+liabilityAccountBalanceList.add([accountName: 
uiLabelMap.AccountingTotalLiabilities, balance: liabilityBalanceTotal]
+        as LinkedHashMap<String, Serializable>)
 
 // CURRENT LIABILITY
-// account balances
-accountBalanceList = []
-transactionTotals = []
-balanceTotal = BigDecimal.ZERO
-List currentLiabilityAndExprs = mainAndExprs as LinkedList
-currentLiabilityAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, currentLiabilityAccountClassIds))
-transactionTotals = select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", 
"amount").from("AcctgTransEntrySums").where(currentLiabilityAndExprs).orderBy("glAccountId").queryList()
-transactionTotalsMap = [:]
-transactionTotalsMap.putAll(currentLiabilityOpeningBalances)
-transactionTotals.each { transactionTotal ->
-    Map accountMap = 
(Map)transactionTotalsMap.get(transactionTotal.glAccountId)
-    if (!accountMap) {
-        accountMap = UtilMisc.makeMapWritable(transactionTotal)
-        accountMap.remove("debitCreditFlag")
-        accountMap.remove("amount")
-        accountMap.put("D", BigDecimal.ZERO)
-        accountMap.put("C", BigDecimal.ZERO)
-        accountMap.put("balance", BigDecimal.ZERO)
-    }
-    UtilMisc.addToBigDecimalInMap(accountMap, 
transactionTotal.debitCreditFlag, transactionTotal.amount)
-    BigDecimal debitAmount = (BigDecimal)accountMap.get("D")
-    BigDecimal creditAmount = (BigDecimal)accountMap.get("C")
-    // liabilities are accounts of class CREDIT: the balance is given by 
credits minus debits
-    BigDecimal balance = creditAmount.subtract(debitAmount)
-    accountMap.put("balance", balance)
-    transactionTotalsMap.put(transactionTotal.glAccountId, accountMap)
-}
-accountBalanceList = UtilMisc.sortMaps(transactionTotalsMap.values().asList(), 
UtilMisc.toList("accountCode"))
-accountBalanceList.each { accountBalance ->
-    balanceTotal = balanceTotal + accountBalance.balance
-}
-context.currentLiabilityBalanceTotal = balanceTotal
-balanceTotalList.add(UtilMisc.toMap("totalName", 
"AccountingCurrentLiabilities", "balance", balanceTotal))
+def currentLiabilityAccountBalances =
+        calculateBalances(currentLiabilityOpeningBalances, 
currentLiabilityAccountClassIds, RootClass.CREDIT)
+def currentLiabilityBalanceTotal = 
sumAccountBalances(currentLiabilityAccountBalances.values())
 
 // EQUITY
-// account balances
-accountBalanceList = []
-transactionTotals = []
-balanceTotal = BigDecimal.ZERO
-List equityAndExprs = mainAndExprs as LinkedList
-equityAndExprs.add(EntityCondition.makeCondition("glAccountClassId", 
EntityOperator.IN, equityAccountClassIds))
-transactionTotals = select("glAccountId", "accountName", "accountCode", 
"debitCreditFlag", 
"amount").from("AcctgTransEntrySums").where(equityAndExprs).orderBy("glAccountId").queryList()
-transactionTotalsMap = [:]
-transactionTotalsMap.putAll(equityOpeningBalances)
-transactionTotals.each { transactionTotal ->
-    Map accountMap = 
(Map)transactionTotalsMap.get(transactionTotal.glAccountId)
-    if (!accountMap) {
-        accountMap = UtilMisc.makeMapWritable(transactionTotal)
-        accountMap.remove("debitCreditFlag")
-        accountMap.remove("amount")
-        accountMap.put("D", BigDecimal.ZERO)
-        accountMap.put("C", BigDecimal.ZERO)
-        accountMap.put("balance", BigDecimal.ZERO)
-    }
-    UtilMisc.addToBigDecimalInMap(accountMap, 
transactionTotal.debitCreditFlag, transactionTotal.amount)
-    BigDecimal debitAmount = (BigDecimal)accountMap.get("D")
-    BigDecimal creditAmount = (BigDecimal)accountMap.get("C")
-    // equities are accounts of class CREDIT: the balance is given by credits 
minus debits
-    BigDecimal balance = creditAmount.subtract(debitAmount)
-    accountMap.put("balance", balance)
-    transactionTotalsMap.put(transactionTotal.glAccountId, accountMap)
-}
+def equityAccountBalances = calculateBalances(equityOpeningBalances, 
equityAccountClassIds, RootClass.CREDIT)
+
 // Add the "retained earnings" account
 Map netIncomeResult = runService('prepareIncomeStatement', 
["organizationPartyId": organizationPartyId, "glFiscalTypeId": glFiscalTypeId, 
"fromDate": fromDate, "thruDate": thruDate, "userLogin": userLogin])
 BigDecimal netIncome = (BigDecimal)netIncomeResult.totalNetIncome
 GenericValue retainedEarningsAccount = 
from("GlAccountTypeDefault").where("glAccountTypeId", "RETAINED_EARNINGS", 
"organizationPartyId", organizationPartyId).cache(true).queryOne()
 if (retainedEarningsAccount) {
     GenericValue retainedEarningsGlAccount = 
retainedEarningsAccount.getRelatedOne("GlAccount", false)
-    transactionTotalsMap.put(retainedEarningsGlAccount.glAccountId, 
UtilMisc.toMap("glAccountId", 
retainedEarningsGlAccount.glAccountId,"accountName", 
retainedEarningsGlAccount.accountName, "accountCode", 
retainedEarningsGlAccount.accountCode, "balance", netIncome))
-}
-accountBalanceList = UtilMisc.sortMaps(transactionTotalsMap.values().asList(), 
UtilMisc.toList("accountCode"))
-accountBalanceList.each { accountBalance ->
-    balanceTotal = balanceTotal + accountBalance.balance
+
+    def retainedEarningsAccountBalance = 
equityAccountBalances.getOrDefault(retainedEarningsGlAccount.glAccountId, new 
AccountBalance(
+            glAccountId: retainedEarningsGlAccount.glAccountId,
+            accountCode: retainedEarningsGlAccount.accountCode,
+            accountName: retainedEarningsGlAccount.accountName,
+            balance: 0.0,
+            D: 0.0,
+            C: 0.0,
+    ))
+
+    retainedEarningsAccountBalance.C += netIncome
+    retainedEarningsAccountBalance.balance += netIncome
+
+    equityAccountBalances.put(retainedEarningsGlAccount.glAccountId, 
retainedEarningsAccountBalance)
 }
-context.equityAccountBalanceList = accountBalanceList
-context.equityAccountBalanceList.add(UtilMisc.toMap("accountName", 
uiLabelMap.AccountingTotalEquities, "balance", balanceTotal))
-context.equityBalanceTotal = balanceTotal
 
-context.liabilityEquityBalanceTotal = context.liabilityBalanceTotal + 
context.equityBalanceTotal
+def equityBalanceTotal = sumAccountBalances(equityAccountBalances.values())
+def equityAccountBalanceList = 
sortAccountBalancesConvertToMaps(equityAccountBalances.values())
+equityAccountBalanceList.add(UtilMisc.toMap("accountName", 
uiLabelMap.AccountingTotalEquities, "balance", equityBalanceTotal))
+
+context.assetBalanceTotal = assetBalanceTotal
+context.assetAccountBalanceList = assetAccountBalanceList
+
+context.currentAssetBalanceTotal = currentAssetBalanceTotal
+balanceTotalList.add(UtilMisc.toMap("totalName", "AccountingCurrentAssets", 
"balance", currentAssetBalanceTotal))
+
+context.longtermAssetBalanceTotal = longtermAssetBalanceTotal
+balanceTotalList.add(UtilMisc.toMap("totalName", "AccountingLongTermAssets", 
"balance", longtermAssetBalanceTotal))
+
+context.contraAssetBalanceTotal = contraAssetBalanceTotal
+balanceTotalList.add(UtilMisc.toMap("totalName", 
"AccountingTotalAccumulatedDepreciation", "balance", contraAssetBalanceTotal))
+balanceTotalList.add(UtilMisc.toMap("totalName", "AccountingTotalAssets", 
"balance", (context.currentAssetBalanceTotal + 
context.longtermAssetBalanceTotal + contraAssetBalanceTotal)))
+
+context.liabilityAccountBalanceList = liabilityAccountBalanceList
+context.liabilityBalanceTotal = liabilityBalanceTotal
+
+context.currentLiabilityBalanceTotal = currentLiabilityBalanceTotal
+balanceTotalList.add(UtilMisc.toMap("totalName", 
"AccountingCurrentLiabilities", "balance", currentLiabilityBalanceTotal))
+
+context.equityAccountBalanceList = equityAccountBalanceList
+context.equityBalanceTotal = equityBalanceTotal
+
+context.liabilityEquityBalanceTotal = liabilityBalanceTotal + 
equityBalanceTotal
 balanceTotalList.add(UtilMisc.toMap("totalName", "AccountingEquities", 
"balance", context.equityBalanceTotal))
 balanceTotalList.add(UtilMisc.toMap("totalName", 
"AccountingTotalLiabilitiesAndEquities", "balance", 
context.liabilityEquityBalanceTotal))
 


Reply via email to