From 5d0d80afa5bce8780305173c93e291466a133f7f Mon Sep 17 00:00:00 2001 From: Stefan Rijnhart Date: Tue, 31 Oct 2017 12:20:54 +0100 Subject: [PATCH 01/11] [FIX] l10n_br: wrong & pointless field redefinition Fields `amount_type` on `account.tax` and `account.tax.template` are already defined in the `account` module. Redefining them here (with the same parameters) breaks every other module that would have used `selection_add=` on those fields. Actually, it is the case in `account_tax_python`, and thus, all the localizations/customizations that depend on it were broken by this one. (issue spotted by 11.0-nightly) Closes #19812 #20596 --- addons/l10n_br/models/account.py | 10 ---------- 1 file changed, 10 deletions(-) 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') From b9b6deff8ede3236806f21355133e61f76714a89 Mon Sep 17 00:00:00 2001 From: Richard Mathot Date: Tue, 31 Oct 2017 13:21:16 +0100 Subject: [PATCH 02/11] [FIX] l10n_br: wrong & pointless field redefinition Fields `amount_type` on `account.tax` and `account.tax.template` are already defined in the `account` module. Redefining them here (with the same parameters) breaks every other module that would have used `selection_add=` on those fields. Actually, it is the case in `account_tax_python`, and thus, all the localizations/customizations that depend on it were broken by this one. ~ Old API backport of 5d0d80afa5bce8780305173c93e291466a133f7f ~ --- addons/l10n_br/account.py | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/addons/l10n_br/account.py b/addons/l10n_br/account.py index e9f3111f742..8a5f532e995 100644 --- a/addons/l10n_br/account.py +++ b/addons/l10n_br/account.py @@ -23,11 +23,6 @@ class account_tax_template(osv.osv): 'amount_mva': fields.float('MVA Percent', required=True, digits=0, help="Um percentual decimal em % entre 0-1."), - '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) } _defaults = TAX_DEFAULTS @@ -44,10 +39,5 @@ class account_tax(osv.osv): 'amount_mva': fields.float('MVA Percent', required=True, digits=0, help="Um percentual decimal em % entre 0-1."), - '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) } _defaults = TAX_DEFAULTS From 75b7f6278367ecf929196f54545231645b6eb73d Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Thu, 2 Nov 2017 11:47:04 +0100 Subject: [PATCH 03/11] [FIX] maintenance: default supplier - Go to Maintenance Requests, Create - Go to 'Equipment' field, 'Create and edit' - Go to 'Product Information' tab - Click on 'Vendor' field, 'Create and edit' 'Is a Vendor' field is False opw-779200 --- addons/maintenance/views/maintenance_views.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/maintenance/views/maintenance_views.xml b/addons/maintenance/views/maintenance_views.xml index aec69d80479..6b3f190d04a 100644 --- a/addons/maintenance/views/maintenance_views.xml +++ b/addons/maintenance/views/maintenance_views.xml @@ -307,7 +307,7 @@ - + From e134dde867844b4eea6cfba9447968cb311a1d85 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 2 Nov 2017 13:14:59 +0100 Subject: [PATCH 04/11] [FIX] base: make partner.company_type properly writeable In 7eab8e26d3d46c53f4be924d6a34e80a66e74960 res.partner was converted to the new API, at that point company_type was changed from a stored field manually synchronised with is_company (through create/write overrides) into a proper computed field. However to make it "editable" it was simply marked as "readonly=False", which means even though UI-wise it looks editable editing it does not actually do anything (things work in the partners form because there's also an onchange which updates is_company on the fly). Fix by implementing an inverse function and actually do this correctly. Fixes #20623 --- odoo/addons/base/res/res_partner.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/odoo/addons/base/res/res_partner.py b/odoo/addons/base/res/res_partner.py index 85e8d719ac8..8e91a0b3ebd 100644 --- a/odoo/addons/base/res/res_partner.py +++ b/odoo/addons/base/res/res_partner.py @@ -201,7 +201,7 @@ class Partner(models.Model, FormatAddress): # 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, FormatAddress): 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') From 455b5aaff82bdc286de05205a6bc39a87f5c36a2 Mon Sep 17 00:00:00 2001 From: Laurent Smet Date: Thu, 2 Nov 2017 14:02:04 +0100 Subject: [PATCH 05/11] [FIX] account, point_of_sale: fix rounding issues with taxes computation (#20573) Suppose a tax of 20% with price_included = True and apply this tax on 399.99: - 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) -opw: 777925 --- addons/account/models/account.py | 56 +++++++++++++++++--- addons/account/tests/test_tax.py | 42 +++++++++++++-- addons/point_of_sale/static/src/js/models.js | 36 ++++++++++--- 3 files changed, 114 insertions(+), 20 deletions(-) diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 02ff42a09f3..2f9b3ee8bce 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -4,7 +4,7 @@ import time import math from openerp.osv import expression -from openerp.tools.float_utils import float_round as round +from openerp.tools.float_utils import float_round as round, float_is_zero as is_zero from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT from openerp.exceptions import UserError, ValidationError from openerp import api, fields, models, _ @@ -745,6 +745,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: @@ -758,9 +775,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. @@ -790,15 +807,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: diff --git a/addons/account/tests/test_tax.py b/addons/account/tests/test_tax.py index df12bb1dd3a..f688e5ba9be 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,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 diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 5321e0ee13a..1df0dd7e3a0 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(); }); @@ -1404,15 +1404,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; @@ -1441,9 +1446,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; From 50a6f50df1b46ffb160e9244b4c3a3d2efdf5b36 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Thu, 2 Nov 2017 13:22:46 +0100 Subject: [PATCH 06/11] [FIX] mrp: production chained to a done or cancelled move Do not try to run `action_assign` on the next move if it is done or cancelled. The issue is that `action_assign` will first unlink the existing pack operations before creating new ones, and the system forbids to unlink these ones. To reproduce this issue: 1. Create a product, routes manufacturing and MTO 2. Create SO with that product 3. Go to DO and force assign then cancel the delivery 4. Go to Manufacturing order created and produce opw 778897 --- addons/mrp/models/stock_move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index dba5d5cd0d3..b53685eb9fc 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -293,7 +293,7 @@ class StockMove(models.Model): self.env['stock.quant'].quants_move(quants, move, move.location_dest_id, lot_id = movelot.lot_id.id) moves_to_unreserve |= move # Next move in production order - if move.move_dest_id: + if move.move_dest_id and move.move_dest_id.state not in ('done', 'cancel'): move.move_dest_id.action_assign() moves_to_unreserve.quants_unreserve() moves_todo.write({'state': 'done', 'date': fields.Datetime.now()}) From be7927d629b765c4f3eeb3ada11806ba75d909ca Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 2 Nov 2017 16:33:01 +0100 Subject: [PATCH 07/11] [FIX] payment_buckaroo: remove bad link The link does not work anymore. Even when it was, it did not provide any information as can be seen in https://web.archive.org/web/20160608025556/http://www.buckaroo-payments.com:80/business/customers/online-payment/ Fixes #16987 --- addons/payment_buckaroo/views/payment_views.xml | 1 - 1 file changed, 1 deletion(-) 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 @@ - How to configure your Buckaroo account? From 0e7f3be6b6b5c2f43c9920feeb0cd426b04c2844 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Fri, 3 Nov 2017 08:57:27 +0100 Subject: [PATCH 08/11] [FIX] stock: incorrect decorator --- addons/stock/models/stock_inventory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/models/stock_inventory.py b/addons/stock/models/stock_inventory.py index 996dfec0b5d..c6bfc0549dd 100644 --- a/addons/stock/models/stock_inventory.py +++ b/addons/stock/models/stock_inventory.py @@ -373,7 +373,7 @@ class InventoryLine(models.Model): self._compute_theoretical_qty() self.product_qty = self.theoretical_qty - @api.model + @api.multi def write(self, values): values.pop('product_name', False) res = super(InventoryLine, self).write(values) From f993ca8ce730e5be3a5cf4067d4ca66592f927f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miquel=20Ra=C3=AFch?= Date: Fri, 3 Nov 2017 10:02:43 +0100 Subject: [PATCH 09/11] [FIX] point_of_sale: adapt test for multicompany Only products without company can have taxes of different companies Closes #20256 Fixes #20250 --- addons/point_of_sale/tests/common.py | 1 + 1 file changed, 1 insertion(+) 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])]}) From 95747685f7e5e5bf74102526d51496132a658c57 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Thu, 2 Nov 2017 18:16:36 +0100 Subject: [PATCH 10/11] [FIX] stock: group by in Packages Location and Company are non-stored fields. Therefore, grouping by these fields leads to a traceback. opw-779564 --- addons/stock/views/stock_quant_views.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/stock/views/stock_quant_views.xml b/addons/stock/views/stock_quant_views.xml index 72577b4033a..10f26fdca93 100644 --- a/addons/stock/views/stock_quant_views.xml +++ b/addons/stock/views/stock_quant_views.xml @@ -159,9 +159,8 @@ - + - From 15310c4d662f84a74be822fccd10c00e77368c9c Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Fri, 3 Nov 2017 09:26:51 +0100 Subject: [PATCH 11/11] [FIX] web_editor: upload without optimization In Enterprise: - Go in Settings > Technical > Mail > Templates - Edit a template - In the editor, upload an image - Choose "Upload image without optimization" You are sent back to the app switcher. opw-778918 --- addons/web_editor/static/src/xml/editor.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/web_editor/static/src/xml/editor.xml b/addons/web_editor/static/src/xml/editor.xml index 2ad47703700..944a9be607f 100644 --- a/addons/web_editor/static/src/xml/editor.xml +++ b/addons/web_editor/static/src/xml/editor.xml @@ -100,7 +100,7 @@