[MERGE] forward port branch 9.0 up to 455b5aaff8

This commit is contained in:
Christophe Simonis
2017-11-03 11:42:39 +01:00
3 changed files with 114 additions and 20 deletions
+48 -8
View File
@@ -4,7 +4,7 @@ import time
import math
from odoo.osv import expression
from odoo.tools.float_utils import float_round as round
from odoo.tools.float_utils import float_round as round, float_is_zero as is_zero
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT
from odoo.exceptions import UserError, ValidationError
from odoo import api, fields, models, _
@@ -819,6 +819,23 @@ class AccountTax(models.Model):
# || ... | .. | .. |
# ----------------------------
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:
@@ -832,9 +849,9 @@ class AccountTax(models.Model):
# (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
return (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0)
base = round(price_unit * quantity, prec)
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.
@@ -864,15 +881,38 @@ class AccountTax(models.Model):
# || tax_1 | OK | XXXX |
# || tax_2 | XXXX | XXXX |
# || tax_3 | XXXX | XXXX |
# \/ ... | .. | .. |
# \/ ... | .. | .. |
# ----------------------------
taxes_vals = []
for tax in taxes:
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)
tax_amount = tax.with_context(force_price_include=False)._compute_amount(base, price_unit, quantity, product, partner)
#
# 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)
if not round_tax:
tax_amount = round(tax_amount, prec)
else:
+38 -4
View File
@@ -188,7 +188,7 @@ class TestTax(AccountTestUsers):
[
# base , amount | seq | amount | incl | incl_base
# ---------------------------------------------------
(5.79, 1.21), # | 3 | 21% | t | t
(5.79, 1.21), # | 3 | 21% | t | t
# ---------------------------------------------------
],
res_percent
@@ -232,6 +232,40 @@ class TestTax(AccountTestUsers):
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
)
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'))
@@ -338,10 +372,10 @@ class TestTax(AccountTestUsers):
# base , amount | seq | amount | incl | incl_base
# ---------------------------------------------------
(100.0, 10.0), # | 1 | 10% | t | t
(110.0, 11.0), # | 3 | 10% | |
(110.0, 11.0), # | 3 | 10% | t |
(110.0, 11.0), # | 3 | 10% | |
(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
+28 -8
View File
@@ -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();
});
@@ -1472,15 +1472,20 @@ exports.Orderline = Backbone.Model.extend({
currency_rounding = currency_rounding * 0.00001;
}
var recompute_base = function(base_amount, fixed_amount, percent_amount){
if(fixed_amount === 0.0 && percent_amount === 0.0)
return base_amount;
return (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0);
}
// 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;
@@ -1509,9 +1514,24 @@ exports.Orderline = Backbone.Model.extend({
// 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 tax_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 = self._compute_all(tax, base, quantity, false);
var tax_amount = compute_amount(tax);
tax_amount = round_pr(tax_amount, currency_rounding);
var tax_base = base;