From ed21149a5f66c37d3e779442e5108336c71c062d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Mon, 28 Jul 2014 15:32:02 +0200
Subject: [PATCH] Accounting: Fix bug with closing period when all accounts
 have 0 balance

---
 ...ountingPeriod_createBalanceTransaction.xml | 30 +++++++++-
 bt5/erp5_accounting/bt/revision               |  2 +-
 product/ERP5/tests/testAccounting.py          | 56 +++++++++++++++++++
 3 files changed, 86 insertions(+), 2 deletions(-)

diff --git a/bt5/erp5_accounting/SkinTemplateItem/portal_skins/erp5_accounting/AccountingPeriod_createBalanceTransaction.xml b/bt5/erp5_accounting/SkinTemplateItem/portal_skins/erp5_accounting/AccountingPeriod_createBalanceTransaction.xml
index 72ba82c5db..37276ddf3d 100644
--- a/bt5/erp5_accounting/SkinTemplateItem/portal_skins/erp5_accounting/AccountingPeriod_createBalanceTransaction.xml
+++ b/bt5/erp5_accounting/SkinTemplateItem/portal_skins/erp5_accounting/AccountingPeriod_createBalanceTransaction.xml
@@ -158,7 +158,17 @@ for section in section_list:\n
                               precision=section_currency_precision,\n
                               portal_type=portal.getPortalAccountingMovementTypeList(),\n
                               at_date=at_date.latestTime(),)\n
-                              \n
+  \n
+  # Calculate the sum of profit and loss accounts balances for that period.\n
+  # This must match the difference between assets, liability and equity accounts.\n
+  profit_and_loss_accounts_balance = portal.portal_simulation.getInventoryAssetPrice(\n
+    from_date=context.getStartDate(),\n
+    node_category=profit_and_loss_node_category_list,\n
+    **inventory_param_dict)\n
+  selected_profit_and_loss_account_balance = portal.portal_simulation.getInventoryAssetPrice(\n
+    node=profit_and_loss_account,\n
+    **inventory_param_dict)\n
+  \n
   section_currency_uid = context.getParentValue().getPriceCurrencyUid()\n
 \n
   profit_and_loss_quantity = 0\n
@@ -300,8 +310,26 @@ for section in section_list:\n
 \n
   if balance_transaction is None:\n
     # we did not have any transaction for this section\n
+    \n
+    # One possible corner case is that we have only transactions that brings\n
+    # the balance of all balance sheets accounts to 0. In this case we want to\n
+    # create a balance transaction that notes that the current balance of profit\n
+    # and loss account is 0, so that the delta gets indexed. \n
+    if profit_and_loss_accounts_balance:\n
+      balance_transaction = createBalanceTransaction(section)\n
+      balance_transaction.newContent(\n
+            activate_kw=activate_kw,\n
+            portal_type=\'Balance Transaction Line\',\n
+            destination=profit_and_loss_account,\n
+            quantity=0)\n
+      balance_transaction.stop()\n
+      balance_transaction.deliver()\n
     continue\n
 \n
+  assert roundCurrency(profit_and_loss_accounts_balance, section_currency) == (\n
+       - roundCurrency(selected_profit_and_loss_account_balance, section_currency)\n
+       - roundCurrency(profit_and_loss_quantity, section_currency))\n
+  \n
   # add a final line for p&l\n
   balance_transaction.newContent(\n
             id=\'%03d\' % (line_count + 1),\n
diff --git a/bt5/erp5_accounting/bt/revision b/bt5/erp5_accounting/bt/revision
index a907bf0316..139fc46884 100644
--- a/bt5/erp5_accounting/bt/revision
+++ b/bt5/erp5_accounting/bt/revision
@@ -1 +1 @@
-1571
\ No newline at end of file
+1572
\ No newline at end of file
diff --git a/product/ERP5/tests/testAccounting.py b/product/ERP5/tests/testAccounting.py
index 3bc8a6e0fc..0550d0d840 100644
--- a/product/ERP5/tests/testAccounting.py
+++ b/product/ERP5/tests/testAccounting.py
@@ -1854,6 +1854,62 @@ class TestClosingPeriod(AccountingTestCase):
     balance_transaction.reindexObject()
     self.tic()
 
+  def test_BalanceTransactionWhenProfitAndLossBalanceIsZero(self):
+    # The case of a balance transaction after all accounts have a 0 balance.
+    period1 = self.section.newContent(portal_type='Accounting Period')
+    period1.setStartDate(DateTime(2006, 1, 1))
+    period1.setStopDate(DateTime(2006, 12, 31))
+    period2 = self.section.newContent(portal_type='Accounting Period')
+    period2.setStartDate(DateTime(2007, 1, 1))
+    period2.setStopDate(DateTime(2007, 12, 31))
+    pl = self.portal.account_module.newContent(
+              portal_type='Account',
+              account_type='equity')
+
+    transaction1 = self._makeOne(
+        start_date=DateTime(2006, 1, 1),
+        portal_type='Sale Invoice Transaction',
+        destination_section_value=self.organisation_module.client_1,
+        simulation_state='delivered',
+        lines=(dict(source_value=self.account_module.receivable,
+                    source_debit=100),
+               dict(source_value=self.account_module.goods_sales,
+                    source_credit=100)))
+    self.tic()
+
+    period1.AccountingPeriod_createBalanceTransaction(
+                             profit_and_loss_account=pl.getRelativeUrl())
+    year_1_accounting_transaction_list = self.accounting_module.contentValues()
+    self.assertEqual(2, len(year_1_accounting_transaction_list))
+    self.tic()
+
+    transaction2 = self._makeOne(
+        start_date=DateTime(2007, 1, 1),
+        portal_type='Sale Invoice Transaction',
+        destination_section_value=self.organisation_module.client_1,
+        simulation_state='delivered',
+        lines=(dict(source_value=self.account_module.receivable,
+                    source_debit=-100),
+               dict(source_value=self.account_module.goods_sales,
+                    source_credit=-100)))
+    self.tic()
+
+    period2.AccountingPeriod_createBalanceTransaction(
+                             profit_and_loss_account=pl.getRelativeUrl())
+    accounting_transaction_list = self.accounting_module.contentValues()
+    self.assertEqual(4, len(accounting_transaction_list))
+    self.tic()
+
+    balance_transaction, = [t for t in accounting_transaction_list if t not in
+        year_1_accounting_transaction_list and t != transaction2]
+    # Maybe we want to add line for each account in that case ?
+    line, = balance_transaction.contentValues()
+
+    self.assertEquals(line.getDestinationValue(), pl)
+    self.assertEquals(line.getQuantity(), 0)
+    self.assertEquals(line.getDestinationTotalAssetPrice(), None)
+
+
   def test_InventoryIndexingNodeAndMirrorSection(self):
     # Balance Transactions are indexed as Inventories.
     transaction1 = self._makeOne(
-- 
2.30.9