diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 5151ec75b44..5a7ad534da7 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -4,7 +4,7 @@ import time import math from odoo.osv import expression -from odoo.tools.float_utils import float_round as round, float_is_zero as is_zero +from odoo.tools.float_utils import float_round as round from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT from odoo.exceptions import UserError, ValidationError from odoo import api, fields, models, _ @@ -682,7 +682,6 @@ class AccountTax(models.Model): price_unit * quantity eventually affected by previous taxes (if tax is include_base_amount XOR price_include) """ self.ensure_one() - price_include = self._context.get('force_price_include', self.price_include) if self.amount_type == 'fixed': # Use copysign to take into account the sign of the base amount which includes the sign # of the quantity and the sign of the price_unit @@ -696,11 +695,11 @@ class AccountTax(models.Model): return math.copysign(quantity, base_amount) * self.amount else: return quantity * self.amount - if (self.amount_type == 'percent' and not price_include) or (self.amount_type == 'division' and self.price_include): + if (self.amount_type == 'percent' and not self.price_include) or (self.amount_type == 'division' and self.price_include): return base_amount * self.amount / 100 - if self.amount_type == 'percent' and price_include: + if self.amount_type == 'percent' and self.price_include: return base_amount - (base_amount / (1 + self.amount / 100)) - if self.amount_type == 'division' and not price_include: + if self.amount_type == 'division' and not self.price_include: return base_amount / (1 - self.amount / 100) - base_amount @api.multi @@ -734,58 +733,13 @@ class AccountTax(models.Model): 'analytic': boolean, }] } """ - - # 1) Flatten the taxes. - - def collect_taxes(self, all_taxes=None): - # Collect all the taxes recursively ordered by the sequence. - # Example: - # group | seq | sub-group | - # ------------|-----------| - # | 1 | | - # ------------|-----------| - # t | 2 | | seq | | - # | | | 4 | | - # | | | 5 | | - # | | | 6 | | - # | | | - # ------------|-----------| - # | 3 | | - # ------------|-----------| - # Result: 1-4-5-6-3 - if not all_taxes: - all_taxes = self.env['account.tax'] - for tax in self.sorted(key=lambda r: r.sequence): - if tax.amount_type == 'group': - all_taxes = collect_taxes(tax.children_tax_ids, all_taxes) - else: - all_taxes += tax - return all_taxes - - taxes = collect_taxes(self) - - # 2) Avoid dealing with taxes mixing price_include=False && include_base_amount=True - # with price_include=True - - base_excluded_flag = False # price_include=False && include_base_amount=True - included_flag = False # price_include=True - for tax in taxes: - if tax.price_include: - included_flag = True - elif tax.include_base_amount: - base_excluded_flag = True - if base_excluded_flag and included_flag: - raise UserError(_('Unable to mix any taxes being price included with taxes affecting the base amount but not included in price.')) - - # 3) Deal with the rounding methods - if len(self) == 0: company_id = self.env.user.company_id else: company_id = self[0].company_id if not currency: currency = company_id.currency_id - + taxes = [] # By default, for each tax, tax amount will first be computed # and rounded at the 'Account' decimal precision for each # PO/SO/invoice line and then these rounded amounts will be @@ -810,133 +764,50 @@ class AccountTax(models.Model): if not round_tax: prec += 5 - # 4) Iterate the taxes in the reversed sequence order to retrieve the initial base of the computation. - # tax | base | amount | - # /\ ---------------------------- - # || tax_1 | XXXX | | <- we are looking for that, it's the total_excluded - # || tax_2 | | | - # || tax_3 | | | - # || ... | .. | .. | - # ---------------------------- - - base = round(price_unit * quantity, prec) - - # Keep track of subsequent recomputed bases in order to avoid some rounding issues. - # For example, 399.99 computed with a tax 20% price_include leads to - # base = 399.99 / 1.2 = 333.32500000000005 - # tax_amount = base * 0.2 = 66.665 - # round(base) + round(tax_amount) = 333.33 + 66.67 = 400.0 (!= 399.99: WRONG) - # - # To fix such issues, base_gaps will contains amount between two bases. - # In our example, the gap between 333.32500000000005 and 399.99 is 66.66499999999996 - # - # Then, when processing the tax and because 66.665 - 66.66499999999996 is close to zero, - # the real gap is returned and so: - # tax_amount = 66.66499999999996 - # round(base) + round(tax_amount) = 333.33 + 66.66 = 399.99 (CORRECT) - base_gaps = [] - - def recompute_base(base_amount, fixed_amount, percent_amount): - # Recompute the new base amount based on included fixed/percent amount and the current base amount. - # Example: - # tax | amount | - # ------------------ - # tax_1 | 10% | - # tax_2 | 15 | - # tax_3 | 20% | - # ------------------ - # if base_amount = 145, the new base is computed as: - # (145 - 15) / (1.0 + ((10 + 20) / 100.0)) = 130 / 1.3 = 100 - if fixed_amount == 0.0 and percent_amount == 0.0: - return base_amount - new_base = (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0) - base_gaps.append(base_amount - new_base) - return new_base - - # For the computation of move lines, we could have a negative base value. - # In this case, compute all with positive values and negative them at the end. - if base < 0: - base = -base - sign = -1 + base_values = self.env.context.get('base_values') + if not base_values: + total_excluded = total_included = base = round(price_unit * quantity, prec) else: - sign = 1 + total_excluded, total_included, base = base_values - # Keep track of the accumulated included fixed/percent amount. - incl_fixed_amount = incl_percent_amount = 0 - for tax in reversed(taxes): - if tax.include_base_amount: - base = recompute_base(base, incl_fixed_amount, incl_percent_amount) - incl_fixed_amount = incl_percent_amount = 0 - if tax.price_include: - if tax.amount_type == 'fixed': - incl_fixed_amount += quantity * tax.amount - elif tax.amount_type == 'percent': - incl_percent_amount += tax.amount - # Start the computation of accumulated amounts at the total_excluded value. - total_excluded = total_included = base = recompute_base(base, incl_fixed_amount, incl_percent_amount) + # Sorting key is mandatory in this case. When no key is provided, sorted() will perform a + # search. However, the search method is overridden in account.tax in order to add a domain + # depending on the context. This domain might filter out some taxes from self, e.g. in the + # case of group taxes. + for tax in self.sorted(key=lambda r: r.sequence): + if tax.amount_type == 'group': + children = tax.children_tax_ids.with_context(base_values=(total_excluded, total_included, base)) + ret = children.compute_all(price_unit, currency, quantity, product, partner) + total_excluded = ret['total_excluded'] + base = ret['base'] if tax.include_base_amount else base + total_included = ret['total_included'] + tax_amount = total_included - total_excluded + taxes += ret['taxes'] + continue - # 5) Iterate the taxes in the sequence order to fill missing base/amount values. - # tax | base | amount | - # || ---------------------------- - # || tax_1 | OK | XXXX | - # || tax_2 | XXXX | XXXX | - # || tax_3 | XXXX | XXXX | - # \/ ... | .. | .. | - # ---------------------------- - - def compute_amount(tax): - # Compute the amount of the tax but don't deal with the price_include because it's already - # took into account on the base amount except for 'division' tax: - # (tax.amount_type == 'percent' && not tax.price_include) - # == (tax.amount_type == 'division' && tax.price_include) - # - # In case of price_included tax, subtract the amount to the corresponding - # gap between the current base and the next one. - amount = tax.with_context(force_price_include=False)._compute_amount( - base, price_unit, quantity, product, partner) - - if not tax.price_include or not base_gaps: - return amount - - # Compute the new gap after subtracting of the tax amount - new_gap = base_gaps[-1] - amount - - # If the newly computed gap is very close of zero, return the current gap to avoid - # rounding issues (see comments above base_gaps). - if is_zero(new_gap, prec): - return base_gaps.pop() - - # Update the current gap with the new one - base_gaps[-1] = new_gap - return amount - - taxes_vals = [] - for tax in taxes: - tax_amount = compute_amount(tax) + tax_amount = tax._compute_amount(base, price_unit, quantity, product, partner) if not round_tax: tax_amount = round(tax_amount, prec) else: tax_amount = currency.round(tax_amount) - # Suppose: - # seq | amount | incl | incl_base | base | amount - # ----------------------------------------------- - # 1 | 10 % | t | t | 100.0 | 10.0 - # ----------------------------------------------- - # ... the next computation must be done using 100.0 + 10.0 = 110.0 as base but - # the tax base of this tax will be 100.0. + if tax.price_include: + total_excluded -= tax_amount + base -= tax_amount + else: + total_included += tax_amount + + # Keep base amount used for the current tax tax_base = base + if tax.include_base_amount: base += tax_amount - # The total_included amount is computed as the sum of total_excluded with all tax_amount - total_included += tax_amount - - taxes_vals.append({ + taxes.append({ 'id': tax.id, 'name': tax.with_context(**{'lang': partner.lang} if partner else {}).name, - 'amount': sign * tax_amount, - 'base': round(sign * tax_base, prec), + 'amount': tax_amount, + 'base': tax_base, 'sequence': tax.sequence, 'account_id': tax.account_id.id, 'refund_account_id': tax.refund_account_id.id, @@ -944,10 +815,10 @@ class AccountTax(models.Model): }) return { - 'taxes': taxes_vals, - 'total_excluded': sign * (currency.round(total_excluded) if round_total else total_excluded), - 'total_included': sign * (currency.round(total_included) if round_total else total_included), - 'base': round(sign * base, prec), + 'taxes': sorted(taxes, key=lambda k: k['sequence']), + 'total_excluded': currency.round(total_excluded) if round_total else total_excluded, + 'total_included': currency.round(total_included) if round_total else total_included, + 'base': base, } @api.model diff --git a/addons/account/tests/test_tax.py b/addons/account/tests/test_tax.py index 806c0e5cda4..e3d67432339 100644 --- a/addons/account/tests/test_tax.py +++ b/addons/account/tests/test_tax.py @@ -1,7 +1,6 @@ -# -*- coding: utf-8 -*- - from odoo.addons.account.tests.account_test_users import AccountTestUsers - +from openerp.tests.common import TransactionCase +from openerp.tools import float_compare import time @@ -28,12 +27,6 @@ class TestTax(AccountTestUsers): 'amount': 10, 'sequence': 3, }) - self.percent_tax_bis = self.tax_model.create({ - 'name': "Percent tax bis", - 'amount_type': 'percent', - 'amount': 21, - 'sequence': 3, - }) self.division_tax = self.tax_model.create({ 'name': "Division tax", 'amount_type': 'division', @@ -74,125 +67,47 @@ class TestTax(AccountTestUsers): self.bank_account = self.bank_journal.default_debit_account_id self.expense_account = self.env['account.account'].search([('user_type_id.type', '=', 'payable')], limit=1) #Should be done by onchange later - def _check_compute_all_results(self, base, total_included, total_excluded, taxes, res): - self.assertAlmostEqual(res['base'], base) - self.assertAlmostEqual(res['total_included'], total_included) - self.assertAlmostEqual(res['total_excluded'], total_excluded) - for i in range(0, len(taxes)): - self.assertAlmostEqual(res['taxes'][i]['base'], taxes[i][0]) - self.assertAlmostEqual(res['taxes'][i]['amount'], taxes[i][1]) - def test_tax_group_of_group_tax(self): self.fixed_tax.include_base_amount = True + self.group_tax.include_base_amount = True + self.group_of_group_tax.include_base_amount = True res = self.group_of_group_tax.compute_all(200.0) - self._check_compute_all_results( - 220, # 'base' - 263, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 10.0), # | 1 | 10 | | t - (210.0, 21.0), # | 3 | 10% | | - (210.0, 10.0), # | 1 | 10 | | t - (220.0, 22.0), # | 3 | 10% | | - # --------------------------------------------------- - ], - res - ) + self.assertEquals(res['total_excluded'], 200.0) + # After calculation of first group + # base = 210 + # total_included = 231 + # Base of the first grouped is passed + # Base after the second group (220) is dropped. + # Base of the group of groups is passed out, + # so we obtain base as after first group + self.assertEquals(res['base'], 210.0) + self.assertEquals(res['total_included'], 263.0) def test_tax_group(self): res = self.group_tax.compute_all(200.0) - self._check_compute_all_results( - 200, # 'base' - 230, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 10.0), # | 1 | 10 | | - (200.0, 20.0), # | 3 | 10% | | - # --------------------------------------------------- - ], - res - ) + self.assertEquals(res['total_excluded'], 200.0) + self.assertEquals(res['total_included'], 230.0) + self.assertEquals(len(res['taxes']), 2) + self.assertEquals(res['taxes'][0]['amount'], 10.0) + self.assertEquals(res['taxes'][1]['amount'], 20.0) def test_tax_percent_division(self): self.division_tax.price_include = True self.division_tax.include_base_amount = True - res_division = self.division_tax.compute_all(200.0) - self._check_compute_all_results( - 220, # 'base' - 220, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 20.0), # | 4 | 10/ | t | t - # --------------------------------------------------- - ], - res_division - ) self.percent_tax.price_include = False self.percent_tax.include_base_amount = False + res_division = self.division_tax.compute_all(200.0) res_percent = self.percent_tax.compute_all(200.0) - self._check_compute_all_results( - 200, # 'base' - 220, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 20.0), # | 3 | 10% | | - # --------------------------------------------------- - ], - res_percent - ) + self.assertEquals(res_division['taxes'][0]['amount'], 20.0) + self.assertEquals(res_percent['taxes'][0]['amount'], 20.0) self.division_tax.price_include = False self.division_tax.include_base_amount = False - res_division = self.division_tax.compute_all(200.0) - self._check_compute_all_results( - 200, # 'base' - 222.22, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 22.22), # | 4 | 10/ | | - # --------------------------------------------------- - ], - res_division - ) self.percent_tax.price_include = True self.percent_tax.include_base_amount = True + res_division = self.division_tax.compute_all(200.0) res_percent = self.percent_tax.compute_all(200.0) - self._check_compute_all_results( - 200, # 'base' - 200, # 'total_included' - 181.82, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (181.82, 18.18), # | 3 | 10% | t | t - # --------------------------------------------------- - ], - res_percent - ) - self.percent_tax_bis.price_include = True - self.percent_tax_bis.include_base_amount = True - res_percent = self.percent_tax_bis.compute_all(7.0) - self._check_compute_all_results( - 7.0, # 'base' - 7.0, # 'total_included' - 5.79, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (5.79, 1.21), # | 3 | 21% | t | t - # --------------------------------------------------- - ], - res_percent - ) + self.assertEquals(res_division['taxes'][0]['amount'], 22.22) + self.assertEquals(res_percent['taxes'][0]['amount'], 18.18) def test_tax_sequence_normalized_set(self): self.division_tax.sequence = 1 @@ -200,103 +115,19 @@ class TestTax(AccountTestUsers): self.percent_tax.sequence = 3 taxes_set = (self.group_tax | self.division_tax) res = taxes_set.compute_all(200.0) - self._check_compute_all_results( - 200, # 'base' - 252.22, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 22.22), # | 1 | 10/ | | - (200.0, 10.0), # | 2 | 10 | | - (200.0, 20.0), # | 3 | 10% | | - # --------------------------------------------------- - ], - res - ) + self.assertEquals(res['taxes'][0]['amount'], 22.22) + self.assertEquals(res['taxes'][1]['amount'], 10.0) + self.assertEquals(res['taxes'][2]['amount'], 20.0) def test_tax_include_base_amount(self): self.fixed_tax.include_base_amount = True res = self.group_tax.compute_all(200.0) - self._check_compute_all_results( - 210, # 'base' - 231, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 10.0), # | 1 | 10 | | t - (210.0, 21.0), # | 3 | 10% | | - # --------------------------------------------------- - ], - res - ) - - self.fixed_tax.price_include = True - self.fixed_tax.include_base_amount = False - res = self.fixed_tax.compute_all(100.0, quantity=2.0) - self._check_compute_all_results( - 180, # 'base' - 200, # 'total_included' - 180, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (180.0, 20.0), # | 1 | 20 | | t - # --------------------------------------------------- - ], - res - ) - - def test_tax_include_base_amount_2(self): - self.percent_tax.price_include = True - self.percent_tax.amount = 21.0 - res = self.percent_tax.compute_all(7.0) - self._check_compute_all_results( - 5.79, # 'base' - 7.0, # 'total_included' - 5.79, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (5.79, 1.21), # | 3 | 10% | t | - # --------------------------------------------------- - ], - res - ) - - def test_tax_include_base_amount_3(self): - self.percent_tax.price_include = True - self.percent_tax.amount = 20.0 - res = self.percent_tax.compute_all(399.99) - self._check_compute_all_results( - 333.33, # 'base' - 399.99, # 'total_included' - 333.33, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (333.33, 66.66), # | 3 | 10% | t | - # --------------------------------------------------- - ], - res - ) + self.assertEquals(res['total_included'], 231.0) def test_tax_currency(self): self.division_tax.amount = 15.0 res = self.division_tax.compute_all(200.0, currency=self.env.ref('base.VEF')) - self._check_compute_all_results( - 200, # 'base' - 235.2941, # 'total_included' - 200, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (200.0, 35.2941), # | 4 | 15/ | | - # --------------------------------------------------- - ], - res - ) + self.assertAlmostEqual(res['total_included'], 235.2941) def test_tax_move_lines_creation(self): """ Test that creating a move.line with tax_ids generates the tax move lines and adjust line amount when a tax is price_include """ @@ -341,58 +172,3 @@ class TestTax(AccountTestUsers): aml_with_taxes = move.line_ids.filtered(lambda l: set(l.tax_ids.ids) == set([self.group_tax.id, self.fixed_tax_bis.id])) self.assertEquals(len(aml_with_taxes), 1) self.assertEquals(aml_with_taxes.credit, 190) - - def test_advanced_taxes_computation_0(self): - '''Test more advanced taxes computation (see issue 34471).''' - tax_1 = self.env['account.tax'].create({ - 'name': 'test_advanced_taxes_computation_0_1', - 'amount_type': 'percent', - 'amount': 10, - 'price_include': True, - 'include_base_amount': True, - 'sequence': 1, - }) - tax_2 = self.env['account.tax'].create({ - 'name': 'test_advanced_taxes_computation_0_2', - 'amount_type': 'percent', - 'amount': 10, - 'sequence': 2, - }) - tax_3 = self.env['account.tax'].create({ - 'name': 'test_advanced_taxes_computation_0_3', - 'amount_type': 'percent', - 'amount': 10, - 'price_include': True, - 'sequence': 3, - }) - tax_4 = self.env['account.tax'].create({ - 'name': 'test_advanced_taxes_computation_0_4', - 'amount_type': 'percent', - 'amount': 10, - 'sequence': 4, - }) - tax_5 = self.env['account.tax'].create({ - 'name': 'test_advanced_taxes_computation_0_5', - 'amount_type': 'percent', - 'amount': 10, - 'price_include': True, - 'sequence': 5, - }) - taxes = tax_1 + tax_2 + tax_3 + tax_4 + tax_5 - res = taxes.compute_all(132.0) - self._check_compute_all_results( - 110, # 'base' - 154, # 'total_included' - 100, # 'total_excluded' - [ - # base , amount | seq | amount | incl | incl_base - # --------------------------------------------------- - (100.0, 10.0), # | 1 | 10% | t | t - (110.0, 11.0), # | 2 | 10% | | - (110.0, 11.0), # | 3 | 10% | t | - (110.0, 11.0), # | 4 | 10% | | - (110.0, 11.0), # | 5 | 10% | t | - # --------------------------------------------------- - ], - res - ) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index b268e88571c..19400b2fe93 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -87,7 +87,7 @@ exports.PosModel = Backbone.Model.extend({ // We fetch the backend data on the server asynchronously. this is done only when the pos user interface is launched, // Any change on this data made on the server is thus not reflected on the point of sale until it is relaunched. - // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. + // when all the data has loaded, we compute some stuff, and declare the Pos ready to be used. this.ready = this.load_server_data().then(function(){ return self.after_load_server_data(); }); @@ -202,7 +202,7 @@ exports.PosModel = Backbone.Model.extend({ }, },{ model: 'account.tax', - fields: ['name','amount', 'price_include', 'include_base_amount', 'amount_type', 'children_tax_ids', 'sequence'], + fields: ['name','amount', 'price_include', 'include_base_amount', 'amount_type', 'children_tax_ids'], domain: null, loaded: function(self, taxes){ self.taxes = taxes; @@ -1400,159 +1400,74 @@ exports.Orderline = Backbone.Model.extend({ return tax; }, - /** - * Mirror JS method of: - * _compute_amount in addons/account/models/account.py - */ - _compute_all: function(tax, base_amount, quantity, price_include) { - if(price_include === undefined) - price_include = tax.price_include; + _compute_all: function(tax, base_amount, quantity) { if (tax.amount_type === 'fixed') { var sign_base_amount = base_amount >= 0 ? 1 : -1; return (Math.abs(tax.amount) * sign_base_amount) * quantity; } - if ((tax.amount_type === 'percent' && !price_include) || (tax.amount_type === 'division' && tax.price_include)){ + if ((tax.amount_type === 'percent' && !tax.price_include) || (tax.amount_type === 'division' && tax.price_include)){ return base_amount * tax.amount / 100; } - if (tax.amount_type === 'percent' && price_include){ + if (tax.amount_type === 'percent' && tax.price_include){ return base_amount - (base_amount / (1 + tax.amount / 100)); } - if (tax.amount_type === 'division' && !price_include) { + if (tax.amount_type === 'division' && !tax.price_include) { return base_amount / (1 - tax.amount / 100) - base_amount; } return false; }, - /** - * Mirror JS method of: - * compute_all in addons/account/models/account.py - * - * Read comments in the python side method for more details about each sub-methods. - */ compute_all: function(taxes, price_unit, quantity, currency_rounding, no_map_tax) { var self = this; - - // 1) Flatten the taxes. - - var _collect_taxes = function(taxes, all_taxes){ - taxes.sort(function (tax1, tax2) { - return tax1.sequence - tax2.sequence; - }); - _(taxes).each(function(tax){ - if(tax.amount_type === 'group') - all_taxes = _collect_taxes(tax.children_tax_ids, all_taxes); - else - all_taxes.push(tax); - }); - return all_taxes; - } - var collect_taxes = function(taxes){ - return _collect_taxes(taxes, []); - } - - taxes = collect_taxes(taxes); - - // 2) Avoid dealing with taxes mixing price_include=False && include_base_amount=True - // with price_include=True - - var base_excluded_flag = false; // price_include=False && include_base_amount=True - var included_flag = false; // price_include=True - _(taxes).each(function(tax){ - if(tax.price_include) - included_flag = true; - else if(tax.include_base_amount) - base_excluded_flag = true - if(base_excluded_flag && included_flag) - throw new Error('Unable to mix any taxes being price included with taxes affecting the base amount but not included in price.'); - }); - - // 3) Deal with the rounding methods - + var list_taxes = []; var currency_rounding_bak = currency_rounding; if (this.pos.company.tax_calculation_rounding_method == "round_globally"){ currency_rounding = currency_rounding * 0.00001; } - - // 4) Iterate the taxes in the reversed sequence order to retrieve the initial base of the computation. - - var base = round_pr(price_unit * quantity, currency_rounding); - - var base_gaps = []; - - var recompute_base = function(base_amount, fixed_amount, percent_amount){ - if(fixed_amount === 0.0 && percent_amount === 0.0) - return base_amount; - var new_base = (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0); - base_gaps.push(base_amount - new_base); - return new_base; - } - - var sign = 1; - if(base < 0){ - base = -base; - sign = -1; - } - - var incl_fixed_amount = 0.0; - var incl_percent_amount = 0.0; - _(taxes.reverse()).each(function(tax){ - if(tax.include_base_amount){ - base = recompute_base(base, incl_fixed_amount, incl_percent_amount); - incl_fixed_amount = 0.0; - incl_percent_amount = 0.0; + var total_excluded = round_pr(price_unit * quantity, currency_rounding); + var total_included = total_excluded; + var base = total_excluded; + _(taxes).each(function(tax) { + if (!no_map_tax){ + tax = self._map_tax_fiscal_position(tax); } - if(tax.price_include){ - if(tax.amount_type === 'fixed') - incl_fixed_amount += quantity * tax.amount; - else if(tax.amount_type === 'percent') - incl_percent_amount += tax.amount; + if (!tax){ + return; } - }); + if (tax.amount_type === 'group'){ + var ret = self.compute_all(tax.children_tax_ids, price_unit, quantity, currency_rounding); + total_excluded = ret.total_excluded; + base = ret.total_excluded; + total_included = ret.total_included; + list_taxes = list_taxes.concat(ret.taxes); + } + else { + var tax_amount = self._compute_all(tax, base, quantity); + tax_amount = round_pr(tax_amount, currency_rounding); - base = recompute_base(base, incl_fixed_amount, incl_percent_amount); - var total_excluded = base; - var total_included = base; - - // 5) Iterate the taxes in the sequence order to fill missing base/amount values. - - var compute_amount = function(tax){ - var amount = self._compute_all(tax, base, quantity, false); - - if(!tax.price_include || base_gaps.length == 0) - return amount; - - var new_gap = base_gaps[base_gaps.length - 1] - amount; - - if(round_pr(new_gap, currency_rounding) === 0.0) - return base_gaps.pop(); - - base_gaps[base_gaps.length - 1] = new_gap; - return amount; - }; - - var taxes_vals = []; - _(taxes.reverse()).each(function(tax){ - var tax_amount = compute_amount(tax); - tax_amount = round_pr(tax_amount, currency_rounding); - - var tax_base = base; - - if(tax.include_base_amount) - base += tax_amount; - - total_included += tax_amount; - - var tax_vals = { - id: tax.id, - amount: sign * tax_amount, - name: tax.name, - }; - - taxes_vals.push(tax_vals); + if (tax_amount){ + if (tax.price_include) { + total_excluded -= tax_amount; + base -= tax_amount; + } + else { + total_included += tax_amount; + } + if (tax.include_base_amount) { + base += tax_amount; + } + var data = { + id: tax.id, + amount: tax_amount, + name: tax.name, + }; + list_taxes.push(data); + } + } }); return { - taxes: taxes_vals, - total_excluded: sign * round_pr(total_excluded, currency_rounding_bak), - total_included: sign * round_pr(total_included, currency_rounding_bak) + taxes: list_taxes, + total_excluded: round_pr(total_excluded, currency_rounding_bak), + total_included: round_pr(total_included, currency_rounding_bak) }; }, get_all_prices: function(){ @@ -1570,7 +1485,6 @@ exports.Orderline = Backbone.Model.extend({ return t.id === el; })); }); - product_taxes = _.map(product_taxes, this._map_tax_fiscal_position.bind(this)); var all_taxes = this.compute_all(product_taxes, price_unit, this.get_quantity(), this.pos.currency.rounding); _(all_taxes.taxes).each(function(tax) {