You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by da...@apache.org on 2023/02/09 18:09:47 UTC

[ofbiz-framework] branch release22.01 updated (6976414e6b -> e28715ea96)

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

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


    from 6976414e6b Fixed: Can‘t run job when user changed password (OFBIZ-10573)
     new 022ad6b596 Improvement: Simplify code used to prepare the balance sheet (OFBIZ-12753)
     new aefbea76d5 FIXED: Display contra accounts on balance sheet as negative values (OFBIZ-12754)
     new e28715ea96 Fixed: Use ending balance from GlAccountHistory when calculating balance sheet balances (OFBIZ-12755)

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../groovyScripts/reports/BalanceSheet.groovy      | 579 +++++++++------------
 1 file changed, 254 insertions(+), 325 deletions(-)


[ofbiz-framework] 03/03: Fixed: Use ending balance from GlAccountHistory when calculating balance sheet balances (OFBIZ-12755)

Posted by da...@apache.org.
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 e28715ea9664a267b3c03705c178220e3862dcd3
Author: Daniel Watford <da...@watfordconsulting.com>
AuthorDate: Thu Feb 9 17:52:40 2023 +0000

    Fixed: Use ending balance from GlAccountHistory when calculating balance sheet balances (OFBIZ-12755)
    
    GL Account balances including on the Balance Sheet now use the closing
    balance from the most recently closed time period's GlAccountHistory
    record, ensuring cumulative balances are calculated correctly.
---
 .../groovyScripts/reports/BalanceSheet.groovy      | 70 ++++++++++------------
 1 file changed, 30 insertions(+), 40 deletions(-)

diff --git a/applications/accounting/groovyScripts/reports/BalanceSheet.groovy b/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
index 0c91734d51..30ac59dee3 100644
--- a/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
+++ b/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
@@ -79,11 +79,8 @@ class AccountBalance {
     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]
+        [glAccountId: glAccountId, accountCode: accountCode, accountName: accountName, balance: balance]
     }
 }
 
@@ -117,8 +114,6 @@ def getLastPeriodClosingBalancesForAccountClassIds = { List<String> accountClass
                         accountCode: history.accountCode,
                         accountName: history.accountName,
                         balance: history.getBigDecimal("endingBalance"),
-                        D: history.getBigDecimal("postedDebits"),
-                        C: history.getBigDecimal("postedCredits")
                 )
         }.each {
             retVal.put(it.glAccountId, it)
@@ -209,24 +204,15 @@ enum RootClass {DEBIT, CREDIT}
  * This controls how the balance of the account is calculated:
  *  Debit account balance = totalDebits - totalCredits
  *  Credit account balance = totalCredits - totalDebits
- * @param Specify whether the opening account balances should be negated, having the effect of switching the credits
- * to debits, and vice-versa.
+ * @param negateBalances Specify whether balances should be negated after they have been calculated according to the
+ * debit/credit flag of any accounts for which transaction entries are found.
  */
 def calculateBalances = { Map<String, AccountBalance> openingBalances,
                           Collection<String> accountClassIds,
                           RootClass rootClass,
-                          boolean negateOpeningBalances = false ->
-
-    Map<String, AccountBalance> accountBalancesByGlAccountId = openingBalances.collectEntries {
-        glAccountId, accountBalance ->
-            [glAccountId, new AccountBalance(
-                    glAccountId: glAccountId,
-                    accountCode: accountBalance.accountCode,
-                    accountName: accountBalance.accountName,
-                    balance: negateOpeningBalances ? accountBalance.balance.negate() : accountBalance.balance,
-                    D: accountBalance.D,
-                    C: accountBalance.C,)]
-    }
+                          boolean negateBalances = false ->
+
+    Map<String, AccountBalance> accountBalancesByGlAccountId = [*:openingBalances]
 
     getAccountEntrySumsForClassIds(accountClassIds).each { entrySum ->
         def existingAccountBalance = accountBalancesByGlAccountId.getOrDefault(
@@ -235,30 +221,37 @@ def calculateBalances = { Map<String, AccountBalance> openingBalances,
                         glAccountId: entrySum.glAccountId,
                         accountCode: entrySum.accountCode,
                         accountName: entrySum.accountName,
-                        balance: 0.0,
-                        D: 0.0,
-                        C: 0.0,
+                        balance: 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 transactionSumsDebitAmount = entrySum.debitCreditFlag == "D" ? entrySum.amount : 0.0
+        def transactionSumsCreditAmount = entrySum.debitCreditFlag == "C" ? entrySum.amount : 0.0
 
+        def currentBalance = existingAccountBalance.balance
         def combinedBalance = rootClass == RootClass.DEBIT ?
-                combinedDebitAmount - combinedCreditAmount :
-                combinedCreditAmount - combinedDebitAmount
+                currentBalance + transactionSumsDebitAmount - transactionSumsCreditAmount :
+                currentBalance + transactionSumsCreditAmount - transactionSumsDebitAmount
 
         accountBalancesByGlAccountId.put(entrySum.glAccountId, new AccountBalance(
                 glAccountId: entrySum.glAccountId,
                 accountCode: entrySum.accountCode,
                 accountName: entrySum.accountName,
-                balance: combinedBalance,
-                D: combinedDebitAmount,
-                C: combinedCreditAmount
+                balance: combinedBalance
         ))
     }
 
+    if (negateBalances) {
+        accountBalancesByGlAccountId = accountBalancesByGlAccountId.collectEntries {
+            glAccountId, accountBalance ->
+                [(glAccountId): new AccountBalance(
+                        glAccountId: glAccountId,
+                        accountCode: accountBalance.accountCode,
+                        accountName: accountBalance.accountName,
+                        balance: negateBalances ? accountBalance.balance.negate() : accountBalance.balance)]
+
+        } as Map<String, AccountBalance>
+    }
+
     accountBalancesByGlAccountId
 }
 
@@ -291,10 +284,10 @@ def longtermAssetBalanceTotal = sumAccountBalances(longtermAssetAccountBalances.
 
 // 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.
+// regular asset accounts in order to offset the total of all assets. We therefore negate these balances before
+// including them in sums with the asset accounts.
 def contraAssetAccountBalances =
-        calculateBalances(contraAssetOpeningBalances, contraAssetAccountClassIds, RootClass.DEBIT, true)
+        calculateBalances(contraAssetOpeningBalances, contraAssetAccountClassIds, RootClass.CREDIT, true)
 def contraAssetBalanceTotal = sumAccountBalances(contraAssetAccountBalances.values())
 def contraAssetAccountBalanceList = sortAccountBalancesConvertToMaps(contraAssetAccountBalances.values())
 assetAccountBalanceList.addAll(contraAssetAccountBalanceList)
@@ -328,15 +321,12 @@ if (retainedEarningsAccount) {
             glAccountId: retainedEarningsGlAccount.glAccountId,
             accountCode: retainedEarningsGlAccount.accountCode,
             accountName: retainedEarningsGlAccount.accountName,
-            balance: 0.0,
-            D: 0.0,
-            C: 0.0,
+            balance: 0.0
     ))
 
-    retainedEarningsAccountBalance.C += netIncome
     retainedEarningsAccountBalance.balance += netIncome
 
-    equityAccountBalances.put(retainedEarningsGlAccount.glAccountId, retainedEarningsAccountBalance)
+    equityAccountBalances.put(retainedEarningsGlAccount.glAccountId as String, retainedEarningsAccountBalance)
 }
 
 def equityBalanceTotal = sumAccountBalances(equityAccountBalances.values())


[ofbiz-framework] 02/03: FIXED: Display contra accounts on balance sheet as negative values (OFBIZ-12754)

Posted by da...@apache.org.
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 aefbea76d571c84e62b66ffb2c13354db1431499
Author: Daniel Watford <da...@watfordconsulting.com>
AuthorDate: Tue Feb 7 17:33:17 2023 +0000

    FIXED: Display contra accounts on balance sheet as negative values (OFBIZ-12754)
    
    Convert the ending balances read from GlAccountHistory for contra
    accounts from credit to debit values by negating them. This ensure they
    correctly reduce the value of the asset accounts they correspond to.
---
 .../groovyScripts/reports/BalanceSheet.groovy      | 24 ++++++++++++++++------
 1 file changed, 18 insertions(+), 6 deletions(-)

diff --git a/applications/accounting/groovyScripts/reports/BalanceSheet.groovy b/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
index bee97d8e16..0c91734d51 100644
--- a/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
+++ b/applications/accounting/groovyScripts/reports/BalanceSheet.groovy
@@ -205,16 +205,28 @@ enum RootClass {DEBIT, CREDIT}
  * @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:
+ * @param rootClass Define whether the collection of Account Class IDs should be treated as Debit or Credit accounts.
+ * This controls how the balance of the account is calculated:
  *  Debit account balance = totalDebits - totalCredits
  *  Credit account balance = totalCredits - totalDebits
+ * @param Specify whether the opening account balances should be negated, having the effect of switching the credits
+ * to debits, and vice-versa.
  */
 def calculateBalances = { Map<String, AccountBalance> openingBalances,
                           Collection<String> accountClassIds,
-                          RootClass rootClass ->
-
-    def accountBalancesByGlAccountId = [:] << openingBalances
+                          RootClass rootClass,
+                          boolean negateOpeningBalances = false ->
+
+    Map<String, AccountBalance> accountBalancesByGlAccountId = openingBalances.collectEntries {
+        glAccountId, accountBalance ->
+            [glAccountId, new AccountBalance(
+                    glAccountId: glAccountId,
+                    accountCode: accountBalance.accountCode,
+                    accountName: accountBalance.accountName,
+                    balance: negateOpeningBalances ? accountBalance.balance.negate() : accountBalance.balance,
+                    D: accountBalance.D,
+                    C: accountBalance.C,)]
+    }
 
     getAccountEntrySumsForClassIds(accountClassIds).each { entrySum ->
         def existingAccountBalance = accountBalancesByGlAccountId.getOrDefault(
@@ -282,7 +294,7 @@ def longtermAssetBalanceTotal = sumAccountBalances(longtermAssetAccountBalances.
 // 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)
+        calculateBalances(contraAssetOpeningBalances, contraAssetAccountClassIds, RootClass.DEBIT, true)
 def contraAssetBalanceTotal = sumAccountBalances(contraAssetAccountBalances.values())
 def contraAssetAccountBalanceList = sortAccountBalancesConvertToMaps(contraAssetAccountBalances.values())
 assetAccountBalanceList.addAll(contraAssetAccountBalanceList)


[ofbiz-framework] 01/03: Improvement: Simplify code used to prepare the balance sheet (OFBIZ-12753)

Posted by da...@apache.org.
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 <da...@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))