Commit 68c37523 authored by Jérome Perrin's avatar Jérome Perrin

accounting: prevent user from creating balance transaction while previous one...

accounting: prevent user from creating balance transaction while previous one is still beeing reindexed
parent 35f4f631
......@@ -67,8 +67,8 @@ def roundCurrency(value, resource_relative_url):\n
qty_precision = precision_cache[resource_relative_url]\n
return round(value, qty_precision)\n
\n
# This tag is checked in accounting period workflow\n
activity_tag = \'BalanceTransactionCreation\'\n
activate_kw=dict(tag=activity_tag)\n
\n
at_date = context.getStopDate()\n
assert at_date\n
......@@ -109,7 +109,7 @@ def getDependantSectionList(group, main_section):\n
section_list.extend(getDependantSectionList(subgroup, main_section))\n
\n
return section_list\n
\n
\n
group_value = section.getGroupValue()\n
section_list = [section]\n
if group_value is not None:\n
......@@ -117,7 +117,6 @@ if group_value is not None:\n
\n
def createBalanceTransaction(section):\n
return portal.accounting_module.newContent(\n
activate_kw=activate_kw,\n
portal_type=\'Balance Transaction\',\n
start_date=(at_date + 1).earliestTime(),\n
title=context.getTitle() or Base_translateString(\'Balance Transaction\'),\n
......@@ -125,221 +124,214 @@ def createBalanceTransaction(section):\n
resource=section_currency,\n
causality_value=context)\n
\n
for section in section_list:\n
section_uid = section.getUid()\n
balance_transaction = None\n
\n
group_by_node_node_category_list = []\n
group_by_mirror_section_node_category_list = []\n
group_by_payment_node_category_list = []\n
profit_and_loss_node_category_list = []\n
\n
node_category_list = portal.portal_categories\\\n
.account_type.getCategoryChildValueList()\n
for node_category in node_category_list:\n
node_category_url = node_category.getRelativeUrl()\n
if node_category_url in (\n
\'account_type/asset/cash/bank\',):\n
group_by_payment_node_category_list.append(node_category_url)\n
elif node_category_url in (\n
\'account_type/asset/receivable\',\n
\'account_type/liability/payable\'):\n
group_by_mirror_section_node_category_list.append(node_category_url)\n
elif node_category.isMemberOf(\'account_type/income\') or \\\n
node_category.isMemberOf(\'account_type/expense\'):\n
profit_and_loss_node_category_list.append(node_category_url)\n
else:\n
group_by_node_node_category_list.append(node_category_url)\n
\n
getInventoryList = portal.portal_simulation.getInventoryList\n
\n
inventory_param_dict = dict(section_uid=section_uid,\n
simulation_state=(\'delivered\',),\n
precision=section_currency_precision,\n
portal_type=portal.getPortalAccountingMovementTypeList(),\n
at_date=at_date.latestTime(),)\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_strict_membership=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
resource=section_currency,\n
**inventory_param_dict)\n
with context.defaultActivateParameterDict({\'tag\': activity_tag}, placeless=True):\n
for section in section_list:\n
section_uid = section.getUid()\n
balance_transaction = None\n
\n
group_by_node_node_category_list = []\n
group_by_mirror_section_node_category_list = []\n
group_by_payment_node_category_list = []\n
profit_and_loss_node_category_list = []\n
\n
node_category_list = portal.portal_categories\\\n
.account_type.getCategoryChildValueList()\n
for node_category in node_category_list:\n
node_category_url = node_category.getRelativeUrl()\n
if node_category_url in (\n
\'account_type/asset/cash/bank\',):\n
group_by_payment_node_category_list.append(node_category_url)\n
elif node_category_url in (\n
\'account_type/asset/receivable\',\n
\'account_type/liability/payable\'):\n
group_by_mirror_section_node_category_list.append(node_category_url)\n
elif node_category.isMemberOf(\'account_type/income\') or \\\n
node_category.isMemberOf(\'account_type/expense\'):\n
profit_and_loss_node_category_list.append(node_category_url)\n
else:\n
group_by_node_node_category_list.append(node_category_url)\n
\n
getInventoryList = portal.portal_simulation.getInventoryList\n
\n
inventory_param_dict = dict(section_uid=section_uid,\n
simulation_state=(\'delivered\',),\n
precision=section_currency_precision,\n
portal_type=portal.getPortalAccountingMovementTypeList(),\n
at_date=at_date.latestTime(),)\n
\n
section_currency_uid = context.getParentValue().getPriceCurrencyUid()\n
\n
profit_and_loss_quantity = 0\n
line_count = 0\n
\n
for inventory in getInventoryList(\n
node_category_strict_membership=group_by_node_node_category_list,\n
group_by_node=1,\n
group_by_resource=1,\n
**inventory_param_dict):\n
\n
total_price = roundCurrency(inventory.total_price or 0, section_currency)\n
quantity = roundCurrency(inventory.total_quantity or 0,\n
inventory.resource_relative_url)\n
\n
if not total_price and not quantity:\n
continue\n
\n
line_count += 1\n
if inventory.resource_uid != section_currency_uid:\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_strict_membership=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
resource=section_currency,\n
**inventory_param_dict)\n
\n
section_currency_uid = context.getParentValue().getPriceCurrencyUid()\n
\n
profit_and_loss_quantity = 0\n
line_count = 0\n
\n
for inventory in getInventoryList(\n
node_category_strict_membership=group_by_node_node_category_list,\n
group_by_node=1,\n
group_by_resource=1,\n
**inventory_param_dict):\n
\n
total_price = roundCurrency(inventory.total_price or 0, section_currency)\n
quantity = roundCurrency(inventory.total_quantity or 0,\n
inventory.resource_relative_url)\n
\n
if not total_price and not quantity:\n
continue\n
\n
line_count += 1\n
if inventory.resource_uid != section_currency_uid:\n
profit_and_loss_quantity += total_price\n
\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
destination=inventory.node_relative_url,\n
resource=inventory.resource_relative_url,\n
quantity=quantity,\n
destination_total_asset_price=total_price)\n
else:\n
if total_price != quantity:\n
# If this fail for you, your accounting doesn\'t use currencies with\n
# consistency\n
raise ValueError(\'Different price: %s != %s \' % (\n
total_price, quantity))\n
\n
if inventory.node_relative_url != profit_and_loss_account:\n
profit_and_loss_quantity += total_price\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
destination=inventory.node_relative_url,\n
quantity=total_price)\n
\n
\n
for inventory in getInventoryList(\n
node_category_strict_membership=group_by_mirror_section_node_category_list,\n
group_by_node=1,\n
group_by_mirror_section=1,\n
group_by_resource=1,\n
**inventory_param_dict):\n
\n
total_price = roundCurrency(inventory.total_price or 0, section_currency)\n
quantity = roundCurrency(inventory.total_quantity or 0,\n
inventory.resource_relative_url)\n
\n
if not total_price and not quantity:\n
continue\n
profit_and_loss_quantity += total_price\n
\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
line_count += 1\n
\n
if inventory.resource_uid != section_currency_uid:\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
activate_kw=activate_kw,\n
destination=inventory.node_relative_url,\n
source_section_uid=inventory.mirror_section_uid,\n
resource=inventory.resource_relative_url,\n
quantity=quantity,\n
destination_total_asset_price=total_price)\n
else:\n
if total_price != quantity:\n
# If this fail for you, your accounting doesn\'t use currencies with\n
# consistency\n
raise ValueError(\'Different price: %s != %s \' % (\n
total_price, quantity))\n
\n
if inventory.node_relative_url != profit_and_loss_account:\n
profit_and_loss_quantity += total_price\n
else:\n
if total_price != quantity:\n
raise ValueError(\'Different price: %s != %s \' % (\n
total_price, quantity))\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
activate_kw=activate_kw,\n
destination=inventory.node_relative_url,\n
source_section_uid=inventory.mirror_section_uid,\n
quantity=total_price)\n
\n
\n
for inventory in getInventoryList(\n
node_category_strict_membership=group_by_mirror_section_node_category_list,\n
group_by_node=1,\n
group_by_mirror_section=1,\n
group_by_resource=1,\n
**inventory_param_dict):\n
\n
total_price = roundCurrency(inventory.total_price or 0, section_currency)\n
quantity = roundCurrency(inventory.total_quantity or 0,\n
inventory.resource_relative_url)\n
\n
if not total_price and not quantity:\n
continue\n
profit_and_loss_quantity += total_price\n
line_count += 1\n
\n
if inventory.resource_uid != section_currency_uid:\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
activate_kw=activate_kw,\n
destination=inventory.node_relative_url,\n
source_section_uid=inventory.mirror_section_uid,\n
resource=inventory.resource_relative_url,\n
quantity=quantity,\n
destination_total_asset_price=total_price)\n
else:\n
if total_price != quantity:\n
raise ValueError(\'Different price: %s != %s \' % (\n
total_price, quantity))\n
if balance_transaction is None:\n
for inventory in getInventoryList(\n
node_category_strict_membership=group_by_payment_node_category_list,\n
group_by_node=1,\n
group_by_payment=1,\n
group_by_resource=1,\n
**inventory_param_dict):\n
\n
total_price = roundCurrency(inventory.total_price or 0, section_currency)\n
quantity = roundCurrency(inventory.total_quantity or 0,\n
inventory.resource_relative_url)\n
\n
if not total_price and not quantity:\n
continue\n
profit_and_loss_quantity += total_price\n
\n
line_count += 1\n
\n
if inventory.resource_uid != section_currency_uid:\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
destination=inventory.node_relative_url,\n
resource=inventory.resource_relative_url,\n
quantity=quantity,\n
destination_payment_uid=inventory.payment_uid,\n
destination_total_asset_price=total_price)\n
else:\n
if total_price != quantity:\n
raise ValueError(\'Different price: %s != %s \' % (\n
total_price, quantity))\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
destination=inventory.node_relative_url,\n
destination_payment_uid=inventory.payment_uid,\n
quantity=total_price)\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
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
activate_kw=activate_kw,\n
destination=inventory.node_relative_url,\n
source_section_uid=inventory.mirror_section_uid,\n
quantity=total_price)\n
\n
\n
for inventory in getInventoryList(\n
node_category_strict_membership=group_by_payment_node_category_list,\n
group_by_node=1,\n
group_by_payment=1,\n
group_by_resource=1,\n
**inventory_param_dict):\n
\n
total_price = roundCurrency(inventory.total_price or 0, section_currency)\n
quantity = roundCurrency(inventory.total_quantity or 0,\n
inventory.resource_relative_url)\n
\n
if not total_price and not quantity:\n
balance_transaction.newContent(\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
profit_and_loss_quantity += total_price\n
\n
line_count += 1\n
\n
if inventory.resource_uid != section_currency_uid:\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
activate_kw=activate_kw,\n
destination=inventory.node_relative_url,\n
resource=inventory.resource_relative_url,\n
quantity=quantity,\n
destination_payment_uid=inventory.payment_uid,\n
destination_total_asset_price=total_price)\n
else:\n
if total_price != quantity:\n
raise ValueError(\'Different price: %s != %s \' % (\n
total_price, quantity))\n
if balance_transaction is None:\n
balance_transaction = createBalanceTransaction(section)\n
balance_transaction.newContent(\n
id=\'%03d\' % line_count,\n
portal_type=\'Balance Transaction Line\',\n
activate_kw=activate_kw,\n
destination=inventory.node_relative_url,\n
destination_payment_uid=inventory.payment_uid,\n
quantity=total_price)\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) == roundCurrency(\n
- roundCurrency(selected_profit_and_loss_account_balance, section_currency)\n
- roundCurrency(profit_and_loss_quantity, section_currency), section_currency)\n
\n
# add a final line for p&l\n
balance_transaction.newContent(\n
id=\'%03d\' % (line_count + 1),\n
activate_kw=activate_kw,\n
portal_type=\'Balance Transaction Line\',\n
destination=profit_and_loss_account,\n
quantity=-profit_and_loss_quantity)\n
assert roundCurrency(profit_and_loss_accounts_balance, section_currency) == roundCurrency(\n
- roundCurrency(selected_profit_and_loss_account_balance, section_currency)\n
- roundCurrency(profit_and_loss_quantity, section_currency), section_currency)\n
\n
# add a final line for p&l\n
balance_transaction.newContent(\n
id=\'%03d\' % (line_count + 1),\n
portal_type=\'Balance Transaction Line\',\n
destination=profit_and_loss_account,\n
quantity=-profit_and_loss_quantity)\n
\n
# and go to delivered state directly (the user is not supposed to edit this document)\n
balance_transaction.stop()\n
balance_transaction.deliver()\n
# and go to delivered state directly (the user is not supposed to edit this document)\n
balance_transaction.stop()\n
balance_transaction.deliver()\n
\n
# make sure this Accounting Period has an activity pending during the indexing\n
# of the balance transaction.\n
......
......@@ -60,6 +60,10 @@ portal = period.getPortalObject()\n
\n
period.Base_checkConsistency()\n
\n
# This tag is used in AccountingPeriod_createBalanceTransaction\n
if portal.portal_activities.countMessageWithTag(\'BalanceTransactionCreation\'):\n
raise ValidationFailed(translateString("Balance transaction creation already in progress. Please try again later."))\n
\n
valid_simulation_state_list = [\'cancelled\', \'delivered\', \'deleted\', \'rejected\']\n
all_state_list = [x[1] for x in\n
portal.Base_getTranslatedWorkflowStateItemList(wf_id=\'accounting_workflow\')]\n
......
......@@ -2499,6 +2499,40 @@ class TestClosingPeriod(AccountingTestCase):
section_uid=self.section.getUid(),
node_uid=self.account_module.receivable.getUid()))
def test_ParrallelClosingRefused(self):
organisation_module = self.organisation_module
stool = self.portal.portal_simulation
period = self.section.newContent(portal_type='Accounting Period')
period.setStartDate(DateTime(2006, 1, 1))
period.setStopDate(DateTime(2006, 12, 31))
period.start()
period2 = self.section.newContent(portal_type='Accounting Period')
period2.setStartDate(DateTime(2007, 1, 1))
period2.setStopDate(DateTime(2007, 12, 31))
period2.start()
pl = self.portal.account_module.newContent(
portal_type='Account',
account_type='equity')
transaction1 = self._makeOne(
start_date=DateTime(2006, 1, 1),
destination_section_value=organisation_module.client_1,
portal_type='Sale Invoice Transaction',
simulation_state='delivered',
lines=(dict(source_value=self.account_module.goods_sales,
source_credit=100),
dict(source_value=self.account_module.receivable,
source_debit=100)))
self.portal.portal_workflow.doActionFor(
period, 'stop_action',
profit_and_loss_account=pl.getRelativeUrl())
self.assertRaises(ValidationFailed,
self.getPortal().portal_workflow.doActionFor,
period2, 'stop_action' )
class TestAccountingExport(AccountingTestCase):
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment