Commit 439ff1b3 authored by Jérome Perrin's avatar Jérome Perrin

accounting: reset grouping reference in a "context free" script

Introduce a new ERP5Site_resetAccountingTransactionLineGroupingReference
script to reset grouping reference on accounting transaction lines.

This solve a problem with the way we activated on
AccountingTransactionLine_resetGroupingReference the context of the
line, if line was removed from OFS before activity was executed, the
activity was discarded and grouping references on related lines was not
reset.

Also drop the unused `keep_if_valid_group` parameter, it was not making
sense.

/reviewed-on nexedi/erp5!849
"""Resets grouping reference on this line and all related lines.
If the parameter keep_if_valid_group is true, then the grouping reference
will be kept as is if the group is still valid, ie. the total quantity
of all accounting lines in the group is 0.
Returns the list of ungroupped lines.
This runs by default asynchronously, but can be call with `async=False` to
run synchronously and returns the list of ungrouped lines. With `async=True`,
the returned list is always empty.
"""
if not context.getGroupingReference():
# The line grouping reference can alredy have been removed, for example when two
# The line grouping reference can already have been removed, for example when two
# lines of the same transaction have the same grouping reference.
return []
portal = context.getPortalObject()
precision = context.getResourceValue(portal_type='Currency').getQuantityPrecision()
if context.AccountingTransaction_isSourceView():
node_uid = context.getSourceUid()
section_category = None
section = context.getSourceSectionValue()
if section is not None:
section = section.Organisation_getMappingRelatedOrganisation()
section_category = section.getGroup(base=1)
mirror_section_uid = context.getDestinationSectionUid()
else:
node_uid = context.getDestinationUid()
section = context.getDestinationSectionValue()
if section is not None:
section = section.Organisation_getMappingRelatedOrganisation()
section_category = section.getGroup(base=1)
mirror_section_uid = context.getSourceSectionUid()
line_list = portal.portal_simulation.getMovementHistoryList(
portal_type=portal.getPortalAccountingMovementTypeList(),
grouping_reference=context.getGroupingReference(),
node_uid=node_uid,
section_category=section_category,
mirror_section_uid=mirror_section_uid)
# If the group is still valid, we may want to keep it as is.
if keep_if_valid_group and round(sum([(l.total_price or 0) for l in line_list]), precision) == 0:
return
for line in line_list:
line.setGroupingReference(None)
line.setGroupingDate(None)
return line_list
resetGroupingReference = portal.ERP5Site_resetAccountingTransactionLineGroupingReference
if async:
resetGroupingReference = portal.portal_simulation.activate(
after_tag='accounting_grouping_reference'
).ERP5Site_resetAccountingTransactionLineGroupingReference
ungrouped_line_list = []
for (section_value, node_uid, mirror_section_uid) in (
(context.getSourceSectionValue(), context.getSourceUid(), context.getDestinationSectionUid(),),
(context.getDestinationSectionValue(), context.getDestinationUid(), context.getSourceSectionUid(),),
):
if section_value is not None:
section_value = section_value.Organisation_getMappingRelatedOrganisation()
section_category = section_value.getGroup(base=True)
if section_category:
ungrouped_line_list.extend(resetGroupingReference(
section_category=section_category,
node_uid=node_uid,
mirror_section_uid=mirror_section_uid,
grouping_reference=context.getGroupingReference()
) or [])
return ungrouped_line_list
......@@ -50,7 +50,7 @@
</item>
<item>
<key> <string>_params</string> </key>
<value> <string>keep_if_valid_group=0</string> </value>
<value> <string>async=True</string> </value>
</item>
<item>
<key> <string>id</string> </key>
......
......@@ -114,7 +114,9 @@ else:
ungrouped_line_list = []
for line in line_list:
if line.getGroupingReference():
ungrouped_line_list.extend(line.AccountingTransactionLine_resetGroupingReference())
# Call AccountingTransactionLine_resetGroupingReference synchronously
# to know the number of ungrouped lines.
ungrouped_line_list.extend(line.AccountingTransactionLine_resetGroupingReference(async=False))
psm = Base_translateString('${ungrouped_line_count} lines ungrouped.',
mapping=dict(ungrouped_line_count=len(ungrouped_line_list)))
......
portal = context.getPortalObject()
line_list = []
for brain in portal.portal_simulation.getMovementHistoryList(
portal_type=portal.getPortalAccountingMovementTypeList(),
grouping_reference=grouping_reference,
node_uid=node_uid,
section_category=section_category,
mirror_section_uid=mirror_section_uid):
line = brain.getObject()
line.setGroupingReference(None)
line.setGroupingDate(None)
line_list.append(line)
return line_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>section_category, node_uid, mirror_section_uid, grouping_reference</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_resetAccountingTransactionLineGroupingReference</string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
"""When transaction is restarted, we'll break existing grouping references on lines.
"""When transaction is restarted, we'll unset existing grouping references on lines.
"""
transaction = sci['object']
for line in transaction.getMovementList(
portal_type=sci.getPortal().getPortalAccountingMovementTypeList()):
if line.getGroupingReference():
line.activate(
after_tag='accounting_grouping_reference'
).AccountingTransactionLine_resetGroupingReference()
line.AccountingTransactionLine_resetGroupingReference()
......@@ -3675,7 +3675,7 @@ class TestTransactions(AccountingTestCase):
# reset from the payment line, the invoice line from the same group will be
# ungrouped
payment_line.AccountingTransactionLine_resetGroupingReference()
payment_line.AccountingTransactionLine_resetGroupingReference(async=False)
self.assertFalse(payment_line.getGroupingReference())
self.assertFalse(payment_line.getGroupingDate())
self.assertFalse(invoice_line.getGroupingReference())
......@@ -3688,6 +3688,41 @@ class TestTransactions(AccountingTestCase):
self.assertTrue(other_section_line.getGroupingDate())
self.assertTrue(other_letter_line.getGroupingDate())
def test_GroupingReferenceResetedOnCancelWithDeleteRaceCondition(self):
"""Reproduction for a bug when transaction is cancelled and grouped line
is deleted before the reset-grouping-reference activity is executed, the
related grouped lines where not reset.
"""
invoice = self._makeOne(
title='First Invoice',
destination_section_value=self.organisation_module.client_1,
lines=(dict(source_value=self.account_module.goods_purchase,
source_debit=100),
dict(source_value=self.account_module.receivable,
source_credit=100,
id='line_with_grouping_reference',
grouping_date=DateTime(),
grouping_reference='A'),))
payment = self._makeOne(
title='First Invoice Payment',
portal_type='Payment Transaction',
source_payment_value=self.section.newContent(
portal_type='Bank Account'),
destination_section_value=self.organisation_module.client_1,
lines=(dict(source_value=self.account_module.receivable,
id='line_with_grouping_reference',
grouping_reference='A',
grouping_date=DateTime(),
source_debit=100),
dict(source_value=self.account_module.bank,
source_credit=100,)))
payment_line = payment.line_with_grouping_reference
self.tic()
invoice.cancel()
invoice.manage_delObjects([line.getId() for line in invoice.contentValues()])
self.tic()
self.assertFalse(payment.line_with_grouping_reference.getGroupingReference())
def test_automatically_setting_grouping_reference(self):
invoice = self._makeOne(
title='First Invoice',
......
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