summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2017-11-07 11:45:42 +0100
committerChristophe Simonis <chs@odoo.com>2017-11-07 11:45:42 +0100
commit45958ab3129b25debf821179f36cda4d4c81e6f7 (patch)
tree7b76e0efaa7a130defdd6c0e7b7fa6e51928eb51
parent8e298f69cdac2a32821b83efbca70f7c4099e3f0 (diff)
parent5f9c3f51a8be5e9c399228d5589d86d7bec94bc8 (diff)
[MERGE] forward port branch saas-16 up to 5f9c3f51a8b
-rw-r--r--addons/account/models/account.py57
-rw-r--r--addons/account/tests/test_tax.py58
-rw-r--r--addons/auth_signup/controllers/main.py4
-rw-r--r--addons/l10n_br/models/account.py10
-rw-r--r--addons/maintenance/views/maintenance_views.xml2
-rw-r--r--addons/payment_buckaroo/views/payment_views.xml1
-rw-r--r--addons/point_of_sale/static/src/js/models.js40
-rw-r--r--addons/point_of_sale/tests/common.py1
-rw-r--r--addons/sale_expense/models/__init__.py1
-rw-r--r--addons/sale_expense/models/product_template.py13
-rw-r--r--addons/sale_stock/static/src/js/tour.js4
-rw-r--r--addons/stock/views/stock_quant_views.xml3
-rw-r--r--addons/web_editor/static/src/xml/editor.xml2
-rw-r--r--odoo/addons/base/res/res_partner.py6
14 files changed, 161 insertions, 41 deletions
diff --git a/addons/account/models/account.py b/addons/account/models/account.py
index 5422197159b..078c504eb43 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
+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, _
@@ -985,6 +985,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:
@@ -998,9 +1015,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.
@@ -1018,7 +1035,7 @@ class AccountTax(models.Model):
incl_fixed_amount = incl_percent_amount = 0
if self._context.get('force_price_include', tax.price_include):
if tax.amount_type == 'fixed':
- incl_fixed_amount += tax.amount
+ 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.
@@ -1030,10 +1047,10 @@ 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)
@@ -1041,7 +1058,29 @@ class AccountTax(models.Model):
# N.B: don't use the with_context if force_price_include already False in context
if 'force_price_include' not in self._context or self._context['force_price_include']:
tax = tax.with_context(force_price_include=False)
- tax_amount = tax._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._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:
diff --git a/addons/account/tests/test_tax.py b/addons/account/tests/test_tax.py
index b39d36f7bf3..806c0e5cda4 100644
--- a/addons/account/tests/test_tax.py
+++ b/addons/account/tests/test_tax.py
@@ -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,56 @@ class TestTax(AccountTestUsers):
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
+ )
+
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 +388,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
diff --git a/addons/auth_signup/controllers/main.py b/addons/auth_signup/controllers/main.py
index 17501e53d05..9fc6ee22a40 100644
--- a/addons/auth_signup/controllers/main.py
+++ b/addons/auth_signup/controllers/main.py
@@ -54,7 +54,9 @@ class AuthSignupHome(Home):
_logger.error("%s", e)
qcontext['error'] = _("Could not create a new account.")
- return request.render('auth_signup.signup', qcontext)
+ response = request.render('auth_signup.signup', qcontext)
+ response.headers['X-Frame-Options'] = 'DENY'
+ return response
@http.route('/web/reset_password', type='http', auth='public', website=True, sitemap=False)
def web_auth_reset_password(self, *args, **kw):
diff --git a/addons/l10n_br/models/account.py b/addons/l10n_br/models/account.py
index b88cd4465e0..e7ce0dc75e5 100644
--- a/addons/l10n_br/models/account.py
+++ b/addons/l10n_br/models/account.py
@@ -14,11 +14,6 @@ class AccountTaxTemplate(models.Model):
help="Um percentual decimal em % entre 0-1.", default=0)
amount_mva = fields.Float(string='MVA Percent', digits=0, required=True,
help="Um percentual decimal em % entre 0-1.", default=0)
- amount_type = fields.Selection([('group', 'Group of Taxes'),
- ('fixed', 'Fixed'),
- ('percent', 'Percentage of Price'),
- ('division', 'Percentage of Price Tax Included')],
- string='Tax Computation', required=True, default='percent')
class AccountTax(models.Model):
@@ -31,8 +26,3 @@ class AccountTax(models.Model):
help="Um percentual decimal em % entre 0-1.", default=0)
amount_mva = fields.Float(string='MVA Percent', digits=0, required=True,
help="Um percentual decimal em % entre 0-1.", default=0)
- amount_type = fields.Selection([('group', 'Group of Taxes'),
- ('fixed', 'Fixed'),
- ('percent', 'Percentage of Price'),
- ('division', 'Percentage of Price Tax Included')],
- string='Tax Computation', required=True, default='percent')
diff --git a/addons/maintenance/views/maintenance_views.xml b/addons/maintenance/views/maintenance_views.xml
index f35d0e43525..a41fb3da972 100644
--- a/addons/maintenance/views/maintenance_views.xml
+++ b/addons/maintenance/views/maintenance_views.xml
@@ -339,7 +339,7 @@
<page string="Product Information">
<group>
<group>
- <field name="partner_id"/>
+ <field name="partner_id" context="{'default_supplier': True, 'default_customer': False}"/>
<field name="partner_ref"/>
<field name="model"/>
<field name="serial_no"/>
diff --git a/addons/payment_buckaroo/views/payment_views.xml b/addons/payment_buckaroo/views/payment_views.xml
index db49146c410..11ef4873cff 100644
--- a/addons/payment_buckaroo/views/payment_views.xml
+++ b/addons/payment_buckaroo/views/payment_views.xml
@@ -11,7 +11,6 @@
<group attrs="{'invisible': [('provider', '!=', 'buckaroo')]}">
<field name="brq_websitekey"/>
<field name="brq_secretkey"/>
- <a colspan="2" href="http://www.buckaroo-payments.com/business/customers/online-payment/" target="_blank">How to configure your Buckaroo account?</a>
</group>
</xpath>
</field>
diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js
index bddecdfa270..35fd4f52a74 100644
--- a/addons/point_of_sale/static/src/js/models.js
+++ b/addons/point_of_sale/static/src/js/models.js
@@ -868,10 +868,10 @@ exports.PosModel = Backbone.Model.extend({
// generate the pdf and download it
self.chrome.do_action('point_of_sale.pos_invoice_report',{additional_context:{
active_ids:order_server_id,
- }});
-
- invoiced.resolve();
- done.resolve();
+ }}).done(function () {
+ invoiced.resolve();
+ done.resolve();
+ });
});
return done;
@@ -1718,15 +1718,20 @@ exports.Orderline = Backbone.Model.extend({
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;
- return (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0);
+ var new_base = (base_amount - fixed_amount) / (1.0 + percent_amount / 100.0);
+ base_gaps.push(base_amount - new_base);
+ return new_base;
}
- // 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 sign = 1;
if(base < 0){
base = -base;
@@ -1743,7 +1748,7 @@ exports.Orderline = Backbone.Model.extend({
}
if(tax.price_include){
if(tax.amount_type === 'fixed')
- incl_fixed_amount += tax.amount;
+ incl_fixed_amount += quantity * tax.amount;
else if(tax.amount_type === 'percent')
incl_percent_amount += tax.amount;
}
@@ -1755,9 +1760,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 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;
diff --git a/addons/point_of_sale/tests/common.py b/addons/point_of_sale/tests/common.py
index 4c5597979c6..1352e7e2e9f 100644
--- a/addons/point_of_sale/tests/common.py
+++ b/addons/point_of_sale/tests/common.py
@@ -59,6 +59,7 @@ class TestPointOfSaleCommon(common.TransactionCase):
'company_id': self.ref('stock.res_company_1')
})
+ self.product4.company_id = False
# I assign those 5 percent taxes on the PCSC349 product as a sale taxes
self.product4.write(
{'taxes_id': [(6, 0, [account_tax_05_incl.id, account_tax_05_incl_chicago.id])]})
diff --git a/addons/sale_expense/models/__init__.py b/addons/sale_expense/models/__init__.py
index dcf2d3dc85d..b5979a2dd23 100644
--- a/addons/sale_expense/models/__init__.py
+++ b/addons/sale_expense/models/__init__.py
@@ -3,3 +3,4 @@
from . import analytic
from . import hr_expense
+from . import product_template
diff --git a/addons/sale_expense/models/product_template.py b/addons/sale_expense/models/product_template.py
new file mode 100644
index 00000000000..5e6299f2548
--- /dev/null
+++ b/addons/sale_expense/models/product_template.py
@@ -0,0 +1,13 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, models
+
+
+class ProductTemplate(models.Model):
+ _inherit = "product.template"
+
+ @api.onchange('can_be_expensed')
+ def _onchange_can_be_expensed(self):
+ if not self.can_be_expensed:
+ self.expense_policy = 'no'
diff --git a/addons/sale_stock/static/src/js/tour.js b/addons/sale_stock/static/src/js/tour.js
index cb10a00c5ef..690150677de 100644
--- a/addons/sale_stock/static/src/js/tour.js
+++ b/addons/sale_stock/static/src/js/tour.js
@@ -20,7 +20,9 @@ odoo.define("sale_stock.tour", function (require) {
auto: true,
in_modal: false,
run: function (actions) {
- actions.auto(".modal-footer .btn-primary");
+ if ($('.modal-footer .btn-primary').length){
+ actions.auto(".modal-footer .btn-primary");
+ }
},
});
});
diff --git a/addons/stock/views/stock_quant_views.xml b/addons/stock/views/stock_quant_views.xml
index b88346da4e8..30c380a0d0e 100644
--- a/addons/stock/views/stock_quant_views.xml
+++ b/addons/stock/views/stock_quant_views.xml
@@ -151,9 +151,8 @@
<field name="location_id"/>
<field name="packaging_id"/>
<group expand='0' string='Group by...'>
- <filter string='Location' domain="[]" context="{'group_by' : 'location_id'}"/>
+ <!-- FORWARD-PORT UP TO v11 -->
<filter string='Packaging' domain="[]" context="{'group_by' : 'packaging_id'}"/>
- <filter string='Company' domain="[]" context="{'group_by' : 'company_id'}" groups="base.group_multi_company"/>
</group>
</search>
</field>
diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml
index 104c9b63049..e8e6374305e 100644
--- a/addons/web_editor/static/src/xml/editor.xml
+++ b/addons/web_editor/static/src/xml/editor.xml
@@ -98,7 +98,7 @@
</button>
<ul class="dropdown-menu" role="menu">
<li>
- <a class="js_disable_optimization" href="#">Upload image without optimization</a>
+ <a class="js_disable_optimization">Upload image without optimization</a>
</li>
</ul>
</div>
diff --git a/odoo/addons/base/res/res_partner.py b/odoo/addons/base/res/res_partner.py
index 4fc963e589d..4862689ea64 100644
--- a/odoo/addons/base/res/res_partner.py
+++ b/odoo/addons/base/res/res_partner.py
@@ -200,7 +200,7 @@ class Partner(models.Model):
# company_type is only an interface field, do not use it in business logic
company_type = fields.Selection(string='Company Type',
selection=[('person', 'Individual'), ('company', 'Company')],
- compute='_compute_company_type', readonly=False)
+ compute='_compute_company_type', inverse='_write_company_type')
company_id = fields.Many2one('res.company', 'Company', index=True, default=_default_company)
color = fields.Integer(string='Color Index', default=0)
user_ids = fields.One2many('res.users', 'partner_id', string='Users', auto_join=True)
@@ -372,6 +372,10 @@ class Partner(models.Model):
for partner in self:
partner.company_type = 'company' if partner.is_company else 'person'
+ def _write_company_type(self):
+ for partner in self:
+ partner.is_company = partner.company_type == 'company'
+
@api.onchange('company_type')
def onchange_company_type(self):
self.is_company = (self.company_type == 'company')