From ffbc7a343279f7235f37e2ed00721d4656573b4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Krier?= Date: Wed, 17 Sep 2014 22:19:37 +0200 Subject: [PATCH] Add Tax rounding configuration issue4106 review11401002 --- CHANGELOG | 2 + __init__.py | 2 + account.py | 56 +++++++++++++++++++++- account.xml | 36 +++++++++++++++ invoice.py | 59 ++++++++++++++++-------- view/configuration_form.xml | 12 +++++ view/configuration_tax_rounding_form.xml | 13 ++++++ view/configuration_tax_rounding_list.xml | 8 ++++ 8 files changed, 167 insertions(+), 21 deletions(-) create mode 100644 view/configuration_form.xml create mode 100644 view/configuration_tax_rounding_form.xml create mode 100644 view/configuration_tax_rounding_list.xml diff --git a/CHANGELOG b/CHANGELOG index c1b4b553bd..2aa8a7702f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +* Add Tax rounding configuration + Version 3.2.0 - 2014-04-21 * Bug fixes (see mercurial logs for details) * Use account of write-off journal as default diff --git a/__init__.py b/__init__.py index 4636280ed2..e87051c86d 100644 --- a/__init__.py +++ b/__init__.py @@ -27,6 +27,8 @@ def register(): Period, Move, Reconciliation, + Configuration, + ConfigurationTaxRounding, module='account_invoice', type_='model') Pool.register( PrintInvoice, diff --git a/account.py b/account.py index 9c6bb236c6..edb143df99 100644 --- a/account.py +++ b/account.py @@ -1,12 +1,64 @@ #This file is part of Tryton. The COPYRIGHT file at the top level of #this repository contains the full copyright notices and license terms. -from trytond.model import fields +from trytond.model import ModelSQL, ModelView, MatchMixin, fields from trytond.pyson import Eval from trytond.pool import Pool, PoolMeta +from trytond.transaction import Transaction -__all__ = ['FiscalYear', 'Period', 'Move', 'Reconciliation'] +__all__ = ['Configuration', 'ConfigurationTaxRounding', + 'FiscalYear', 'Period', 'Move', 'Reconciliation'] __metaclass__ = PoolMeta +tax_roundings = [ + ('document', 'Per Document'), + ('line', 'Per Line'), + ] + + +class Configuration: + __name__ = 'account.configuration' + tax_rounding = fields.Function(fields.Selection(tax_roundings, + 'Tax Rounding'), 'on_change_with_tax_rounding') + tax_roundings = fields.One2Many('account.configuration.tax_rounding', + 'configuration', 'Tax Roundings') + + @fields.depends('tax_roundings') + def on_change_with_tax_rounding(self, name=None, pattern=None): + context = Transaction().context + if pattern is None: + pattern = {} + pattern = pattern.copy() + pattern['company'] = context.get('company') + + for line in self.tax_roundings: + if line.match(pattern): + return line.method + return 'document' + + +class ConfigurationTaxRounding(ModelSQL, ModelView, MatchMixin): + 'Account Configuration Tax Rounding' + __name__ = 'account.configuration.tax_rounding' + configuration = fields.Many2One('account.configuration', 'Configuration', + required=True, ondelete='CASCADE') + sequence = fields.Integer('Sequence') + company = fields.Many2One('company.company', 'Company') + method = fields.Selection(tax_roundings, 'Method', required=True) + + @classmethod + def __setup__(cls): + super(ConfigurationTaxRounding, cls).__setup__() + cls._order.insert(0, ('sequence', 'ASC')) + + @staticmethod + def order_sequence(tables): + table, _ = tables[None] + return [table.sequence == None, table.sequence] + + @staticmethod + def default_method(): + return 'document' + class FiscalYear: __name__ = 'account.fiscalyear' diff --git a/account.xml b/account.xml index fda39f504e..3937f8fc96 100644 --- a/account.xml +++ b/account.xml @@ -3,6 +3,42 @@ this repository contains the full copyright notices and license terms. --> + + account.configuration + + configuration_form + + + + account.configuration.tax_rounding + form + configuration_tax_rounding_form + + + account.configuration.tax_rounding + tree + configuration_tax_rounding_list + + + + + + + + + + + + + + + + + + account.fiscalyear diff --git a/invoice.py b/invoice.py index 5c0e45b5a6..d0ac1ced64 100644 --- a/invoice.py +++ b/invoice.py @@ -436,6 +436,10 @@ class Invoice(Workflow, ModelSQL, ModelView): InvoiceTax = pool.get('account.invoice.tax') Account = pool.get('account.account') TaxCode = pool.get('account.tax.code') + Configuration = pool.get('account.configuration') + + config = Configuration(1) + res = { 'untaxed_amount': Decimal('0.0'), 'tax_amount': Decimal('0.0'), @@ -443,6 +447,13 @@ class Invoice(Workflow, ModelSQL, ModelView): 'taxes': {}, } computed_taxes = {} + + def round_taxes(): + if self.currency: + for value in computed_taxes.itervalues(): + for field in ('base', 'amount'): + value[field] = self.currency.round(value[field]) + if self.lines: context = self.get_tax_context() for line in self.lines: @@ -457,16 +468,16 @@ class Invoice(Workflow, ModelSQL, ModelView): for tax in taxes: key, val = self._compute_tax(tax, self.type or 'out_invoice') - if not key in computed_taxes: + if key not in computed_taxes: computed_taxes[key] = val else: computed_taxes[key]['base'] += val['base'] computed_taxes[key]['amount'] += val['amount'] - if self.currency: - for key in computed_taxes: - for field in ('base', 'amount'): - computed_taxes[key][field] = self.currency.round( - computed_taxes[key][field]) + if config.tax_rounding == 'line': + round_taxes() + if config.tax_rounding == 'document': + round_taxes() + tax_keys = [] for tax in (self.taxes or []): if tax.manual: @@ -808,31 +819,41 @@ class Invoice(Workflow, ModelSQL, ModelView): return key, val def _compute_taxes(self): - Tax = Pool().get('account.tax') + pool = Pool() + Tax = pool.get('account.tax') + Configuration = pool.get('account.configuration') + + config = Configuration(1) context = self.get_tax_context() - res = {} + taxes = {} + + def round_taxes(): + for value in taxes.itervalues(): + for field in ('base', 'amount'): + value[field] = self.currency.round(value[field]) + for line in self.lines: - # Don't round on each line to handle rounding error if line.type != 'line': continue with Transaction().set_context(**context): - taxes = Tax.compute(line.taxes, line.unit_price, + tax_list = Tax.compute(line.taxes, line.unit_price, line.quantity, date=self.accounting_date or self.invoice_date) - for tax in taxes: + for tax in tax_list: key, val = self._compute_tax(tax, self.type) val['invoice'] = self.id - if not key in res: - res[key] = val + if key not in taxes: + taxes[key] = val else: - res[key]['base'] += val['base'] - res[key]['amount'] += val['amount'] - for key in res: - for field in ('base', 'amount'): - res[key][field] = self.currency.round(res[key][field]) - return res + taxes[key]['base'] += val['base'] + taxes[key]['amount'] += val['amount'] + if config.tax_rounding == 'line': + round_taxes() + if config.tax_rounding == 'document': + round_taxes() + return taxes @classmethod def update_taxes(cls, invoices, exception=False): diff --git a/view/configuration_form.xml b/view/configuration_form.xml new file mode 100644 index 0000000000..47ca93c178 --- /dev/null +++ b/view/configuration_form.xml @@ -0,0 +1,12 @@ + + + + + + + + + diff --git a/view/configuration_tax_rounding_form.xml b/view/configuration_tax_rounding_form.xml new file mode 100644 index 0000000000..92cac9c7cf --- /dev/null +++ b/view/configuration_tax_rounding_form.xml @@ -0,0 +1,13 @@ + + +
+