diff options
| -rw-r--r-- | addons/account/models/account.py | 57 | ||||
| -rw-r--r-- | addons/account/tests/test_tax.py | 58 | ||||
| -rw-r--r-- | addons/auth_signup/controllers/main.py | 4 | ||||
| -rw-r--r-- | addons/l10n_br/models/account.py | 10 | ||||
| -rw-r--r-- | addons/maintenance/views/maintenance_views.xml | 2 | ||||
| -rw-r--r-- | addons/payment_buckaroo/views/payment_views.xml | 1 | ||||
| -rw-r--r-- | addons/point_of_sale/static/src/js/models.js | 40 | ||||
| -rw-r--r-- | addons/point_of_sale/tests/common.py | 1 | ||||
| -rw-r--r-- | addons/sale_expense/models/__init__.py | 1 | ||||
| -rw-r--r-- | addons/sale_expense/models/product_template.py | 13 | ||||
| -rw-r--r-- | addons/sale_stock/static/src/js/tour.js | 4 | ||||
| -rw-r--r-- | addons/stock/views/stock_quant_views.xml | 3 | ||||
| -rw-r--r-- | addons/web_editor/static/src/xml/editor.xml | 2 | ||||
| -rw-r--r-- | odoo/addons/base/res/res_partner.py | 6 |
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') |
