Commit bbbe264a authored by Jérome Perrin's avatar Jérome Perrin

accounting: update account list cache when modifying/validating accounts

Using a cache cookie that is incremented each time an account is added or modified,
we can keep an (almost) up to date list of accounts in the UI.

Fixes #20170221-1812E21

/reviewed-on nexedi/erp5!796
parent 14b0fcdc
<workflow_chain> <workflow_chain>
<chain> <chain>
<type>Account</type> <type>Account</type>
<workflow>account_workflow, edit_workflow</workflow> <workflow>account_cache_interaction_workflow, account_workflow, edit_workflow</workflow>
</chain> </chain>
<chain> <chain>
<type>Accounting Period</type> <type>Accounting Period</type>
......
...@@ -75,9 +75,11 @@ def getItemList(category=None, portal_path=None, mirror=0, omit_filter=0, ...@@ -75,9 +75,11 @@ def getItemList(category=None, portal_path=None, mirror=0, omit_filter=0,
filter=filter_dict) filter=filter_dict)
return item_list return item_list
# wrap the previous method in a cache # wrap the previous method in a cache, including the cache cookie that
# we reset everytime and account is validated or invalidated.
cache_cookie = portal.account_module.getCacheCookie('account_list')
getItemList = CachingMethod(getItemList, getItemList = CachingMethod(getItemList,
id='AccountingTransactionLine_getNodeItemList', id='AccountingTransactionLine_getNodeItemList-%s' % cache_cookie,
cache_factory='erp5_content_long') cache_factory='erp5_content_long')
# the cache vary with the simulation state of the current transaction, # the cache vary with the simulation state of the current transaction,
......
...@@ -21,8 +21,11 @@ def getAccountItemList(section_category, ...@@ -21,8 +21,11 @@ def getAccountItemList(section_category,
return account_list return account_list
# wrap the previous method in a cache, including the cache cookie that
# we reset everytime and account is validated or invalidated.
cache_cookie = portal.account_module.getCacheCookie('account_list')
getAccountItemList = CachingMethod(getAccountItemList, getAccountItemList = CachingMethod(getAccountItemList,
id=script.getId(), id='%s-%s' % (script.getId(), cache_cookie),
cache_factory='erp5_content_long') cache_factory='erp5_content_long')
return getAccountItemList(section_category, return getAccountItemList(section_category,
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="InteractionWorkflowDefinition" module="Products.ERP5.InteractionWorkflow"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>creation_guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string>Flush account cache when adding, removing or editing accounts</string> </value>
</item>
<item>
<key> <string>groups</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>account_cache_interaction_workflow</string> </value>
</item>
<item>
<key> <string>manager_bypass</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string>Interaction Workflow Definition</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Interaction" module="Products.ERP5.Interaction"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>interactions</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="InteractionDefinition" module="Products.ERP5.Interaction"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>activate_script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>before_commit_script_name</string> </key>
<value>
<list>
<string>Account_flushAccountListCache</string>
</list>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>change_state</string> </value>
</item>
<item>
<key> <string>method_id</string> </key>
<value>
<list>
<string>validate</string>
<string>invalidate</string>
<string>delete</string>
</list>
</value>
</item>
<item>
<key> <string>once_per_transaction</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<list>
<string>Account</string>
</list>
</value>
</item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="InteractionDefinition" module="Products.ERP5.Interaction"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>actbox_category</string> </key>
<value> <string>workflow</string> </value>
</item>
<item>
<key> <string>actbox_name</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>actbox_url</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>activate_script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>after_script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>before_commit_script_name</string> </key>
<value>
<list>
<string>Account_flushAccountListCacheIfAccountIsValidated</string>
</list>
</value>
</item>
<item>
<key> <string>description</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>guard</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>edit_validated_account</string> </value>
</item>
<item>
<key> <string>method_id</string> </key>
<value>
<list>
<string>_set.*</string>
</list>
</value>
</item>
<item>
<key> <string>once_per_transaction</string> </key>
<value> <int>1</int> </value>
</item>
<item>
<key> <string>portal_type_filter</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>portal_type_group_filter</string> </key>
<value>
<none/>
</value>
</item>
<item>
<key> <string>script_name</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>temporary_document_disallowed</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
<item>
<key> <string>trigger_type</string> </key>
<value> <int>2</int> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Scripts" module="Products.DCWorkflow.Scripts"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>_objects</string> </key>
<value>
<tuple/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>scripts</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""Increment cache cookie to flush the cache for list of accounts.
Because cache uses catalog, we can only flush cache once the account is reindex.
"""
tag = script.getId()
sci['object'].reindexObject(activate_kw={'tag': tag})
sci['object'].getPortalObject().account_module.activate(
after_tag=tag
).newCacheCookie("account_list")
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>sci</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Account_flushAccountListCache</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""When account is edited, we only need to update the cache if the account is validated
because only validated accounts are displayed in the cache.
"""
if sci['object'].getValidationState() == 'validated':
container.Account_flushAccountListCache(sci)
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>Script_magic</string> </key>
<value> <int>3</int> </value>
</item>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_container</string> </key>
<value> <string>container</string> </value>
</item>
<item>
<key> <string>name_context</string> </key>
<value> <string>context</string> </value>
</item>
<item>
<key> <string>name_m_self</string> </key>
<value> <string>script</string> </value>
</item>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>sci</string> </value>
</item>
<item>
<key> <string>_proxy_roles</string> </key>
<value>
<tuple>
<string>Manager</string>
</tuple>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>Account_flushAccountListCacheIfAccountIsValidated</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Variables" module="Products.DCWorkflow.Variables"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>variables</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="Worklists" module="Products.DCWorkflow.Worklists"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_mapping</string> </key>
<value>
<dictionary/>
</value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>worklists</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
Account | account_cache_interaction_workflow
Account | account_workflow Account | account_workflow
Account | edit_workflow Account | edit_workflow
Accounting Period | accounting_period_workflow Accounting Period | accounting_period_workflow
......
account_cache_interaction_workflow
account_workflow account_workflow
accounting_period_workflow accounting_period_workflow
accounting_workflow accounting_workflow
......
...@@ -295,6 +295,68 @@ class TestAccounts(AccountingTestCase): ...@@ -295,6 +295,68 @@ class TestAccounts(AccountingTestCase):
account.setCreditAccount(False) account.setCreditAccount(False)
self.assertFalse(account.isCreditAccount()) self.assertFalse(account.isCreditAccount())
def test_ERP5Site_getAccountItemList_cache(self):
# added accounts are directly availble
account = self.portal.account_module.newContent(
portal_type='Account',
gap='my_country/my_accounting_standards/1',
)
account.validate()
self.tic()
self.assertIn(
account.getRelativeUrl(),
[x[1] for x in self.portal.ERP5Site_getAccountItemList(
section_category='',
section_category_strict=False,
from_date=None)])
# changes to validated accounts are also reflected
account.setTitle('test account')
self.tic()
self.assertIn(
account.Account_getFormattedTitle(),
[x[0] for x in self.portal.ERP5Site_getAccountItemList(
section_category='',
section_category_strict=False,
from_date=None)])
# invalidated accounts are removed
account.invalidate()
self.tic()
self.assertNotIn(
account.getRelativeUrl(),
[x[1] for x in self.portal.ERP5Site_getAccountItemList(
section_category='',
section_category_strict=False,
from_date=None)])
def test_AccountingTransactionLine_getNodeItemList_cache(self):
accounting_line = self._makeOne(lines=(dict(id='income'),)).income
# added accounts are directly availble
account = self.portal.account_module.newContent(
portal_type='Account',
account_type='income'
)
account.validate()
self.tic()
self.assertIn(
account.getRelativeUrl(),
[x[1] for x in accounting_line.AccountingTransactionLine_getNodeItemList()])
# changes to validated accounts are also reflected
account.setTitle('test account')
self.tic()
self.assertIn(
account.Account_getFormattedTitle(),
[x[0] for x in accounting_line.AccountingTransactionLine_getNodeItemList()])
# invalidated accounts are removed
account.invalidate()
self.tic()
self.assertNotIn(
account.getRelativeUrl(),
[x[1] for x in accounting_line.AccountingTransactionLine_getNodeItemList()])
class TestTransactionValidation(AccountingTestCase): class TestTransactionValidation(AccountingTestCase):
"""Test validations of accounting transactions. """Test validations of accounting transactions.
......
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