Commit 3e5ca320 authored by Jérome Perrin's avatar Jérome Perrin

accounting: round in grouping when no section currency is set

Grouping feature checks that the sum of all selected lines == 0, which
is often not the case as the values are float. For that, our approach
is to round the values with the precision of the accounting currency,
since these precisions are usually small (typically 0, 2 or 3), we
don't have problems with rounding. Using the section currency is not
just a workaround for rounding, it's also correct because we don't
consider more precise amounts in accounting transaction lines.

The problem with this approach was for the case where no accounting
currency is set on the section organisation, in that case we did not
round and this sometimes led to "grouping is impossible" errors that
are hard to find for users. At this level it's better to use a default
rounding precision that would make it possible to use the grouping
feature even when section currency is not set.
parent 0162ede8
...@@ -75,12 +75,14 @@ changed_line_list = [] ...@@ -75,12 +75,14 @@ changed_line_list = []
for (node, section, mirror_section, _), line_info_list in lines_per_node.items(): for (node, section, mirror_section, _), line_info_list in lines_per_node.items():
if node is None: if node is None:
continue continue
total_price = sum([l['total_price'] for l in line_info_list]) # get the currency rounding for this section, with a fallback that something that would
# get the currency rounding for this section # allow grouping in case precision is not defined.
currency_precision = 5
if section: if section:
default_currency = portal.restrictedTraverse(section).getPriceCurrencyValue() default_currency = portal.restrictedTraverse(section).getPriceCurrencyValue()
if default_currency is not None: if default_currency is not None:
total_price = round(total_price, default_currency.getQuantityPrecision()) currency_precision = default_currency.getQuantityPrecision()
total_price = round(sum([l['total_price'] for l in line_info_list]), currency_precision)
if total_price == 0 or allow_grouping_with_different_quantity: if total_price == 0 or allow_grouping_with_different_quantity:
# we should include mirror node in the id_group, but this would reset # we should include mirror node in the id_group, but this would reset
# id generators and generate grouping references that were already used. # id generators and generate grouping references that were already used.
......
...@@ -3786,6 +3786,61 @@ class TestTransactions(AccountingTestCase): ...@@ -3786,6 +3786,61 @@ class TestTransactions(AccountingTestCase):
self.tic() self.tic()
self.assertFalse(payment.line_with_grouping_reference.getGroupingReference()) self.assertFalse(payment.line_with_grouping_reference.getGroupingReference())
def test_grouping_reference_rounding(self):
"""Reproduction of a bug that grouping was not possible because of rounding error
>>> 0.1 + 0.2 - 0.3 == 0
False
"""
invoice = self._makeOne(
title='Invoice',
destination_section_value=self.organisation_module.client_1,
lines=(dict(source_value=self.account_module.goods_purchase,
source_debit=0.3),
dict(source_value=self.account_module.receivable,
source_credit=0.1,
id='line_1'),
dict(source_value=self.account_module.receivable,
source_credit=0.2,
id='line_2')))
payment = self._makeOne(
title='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_3',
source_debit=0.3),
dict(source_value=self.account_module.bank,
source_credit=0.3,)))
self.tic()
grouped = invoice.AccountingTransaction_guessGroupedLines(
accounting_transaction_line_uid_list=(
invoice.line_1.getUid(),
invoice.line_2.getUid(),
payment.line_3.getUid(),
))
self.assertEqual(
sorted(grouped),
sorted([
invoice.line_1.getRelativeUrl(),
invoice.line_2.getRelativeUrl(),
payment.line_3.getRelativeUrl(),
]))
self.tic()
def test_grouping_reference_rounding_without_accounting_currency_on_section(self):
accounting_currency = self.section.getPriceCurrency()
self.assertTrue(accounting_currency)
self.section.setPriceCurrency(None)
try:
self.test_grouping_reference_rounding()
finally:
self.abort()
self.section.setPriceCurrency(accounting_currency)
self.tic()
def test_automatically_setting_grouping_reference(self): def test_automatically_setting_grouping_reference(self):
invoice = self._makeOne( invoice = self._makeOne(
title='First Invoice', 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