From df7953f3031a2d3b0f2abf30a41df4955fb6b553 Mon Sep 17 00:00:00 2001 From: Arnold Moyaux Date: Fri, 4 Jan 2019 13:35:28 +0000 Subject: [PATCH 01/22] [FIX] stock: unreserve drop all the processed work Usecase to reproduce(or any other way to lower initial demand on move): - Build a MO with 5 products to do - Produce 3 - Update the quantity on the mo to 3 -> All the consumed quantity on move lines is dropped. It happens because lowering the initial demand on a move will call unlink on its move lines. (increase it will update the move's state and reassign if it comes from a supplier) Instead of dropping and reassign the move in order to change the reservation, it is possible to directly write the new reserved quantity on the move lines and that will update the reservation on qaunt directly. Also we don't want move lines without reserved quantity and quantity done thus we drop them. closes odoo/odoo#29941 --- addons/mrp/models/mrp_production.py | 5 ++- addons/mrp/models/stock_move.py | 18 +++++++++ addons/mrp/tests/test_order.py | 46 ++++++++++++++++++++++ addons/mrp/wizard/change_production_qty.py | 4 -- 4 files changed, 68 insertions(+), 5 deletions(-) diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 636d0abd8dc..608a0eace65 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -439,7 +439,10 @@ class MrpProduction(models.Model): move = self.move_raw_ids.filtered(lambda x: x.bom_line_id.id == bom_line.id and x.state not in ('done', 'cancel')) if move: if quantity > 0: - move[0].write({'product_uom_qty': quantity}) + move[0]._decrease_reserved_quanity(quantity) + move[0].with_context(do_not_unreserve=True).write({'product_uom_qty': quantity}) + move[0]._recompute_state() + move[0]._action_assign() move.unit_factor = quantity / move.raw_material_production_id.product_qty elif quantity < 0: # Do not remove 0 lines if move[0].quantity_done > 0: diff --git a/addons/mrp/models/stock_move.py b/addons/mrp/models/stock_move.py index 9db5a2b9d49..0821c2f8449 100644 --- a/addons/mrp/models/stock_move.py +++ b/addons/mrp/models/stock_move.py @@ -182,6 +182,24 @@ class StockMove(models.Model): self.sudo().unlink() return processed_moves + def _decrease_reserved_quanity(self, quantity): + """ Decrease the reservation on move lines but keeps the + all other data. + """ + move_line_to_unlink = self.env['stock.move.line'] + for move in self: + reserved_quantity = quantity + for move_line in self.move_line_ids: + if move_line.product_uom_qty > reserved_quantity: + move_line.product_uom_qty = reserved_quantity + else: + move_line.product_uom_qty = 0 + reserved_quantity -= move_line.product_uom_qty + if not move_line.product_uom_qty and not move_line.qty_done: + move_line_to_unlink |= move_line + move_line_to_unlink.unlink() + return True + def _prepare_phantom_move_values(self, bom_line, quantity): return { 'picking_id': self.picking_id.id if self.picking_id else False, diff --git a/addons/mrp/tests/test_order.py b/addons/mrp/tests/test_order.py index 656156a9cde..b49c39c226b 100644 --- a/addons/mrp/tests/test_order.py +++ b/addons/mrp/tests/test_order.py @@ -617,3 +617,49 @@ class TestMrpOrder(TestMrpCommon): mo.button_mark_done() self.assertEqual(mo.state, 'done', "Production order should be in done state.") + + def test_product_produce_5(self): + """ Build 5 final products with different consumed lots, + then edit the finished quantity and update the Manufacturing + order quantity. Then check if the produced quantity do not + change and it is possible to close the MO. + """ + self.stock_location = self.env.ref('stock.stock_location_stock') + mo, bom, p_final, p1, p2 = self.generate_mo(tracking_base_1='lot') + self.assertEqual(len(mo), 1, 'MO should have been created') + + lot_1 = self.env['stock.production.lot'].create({ + 'name': 'lot1', + 'product_id': p1.id, + }) + lot_2 = self.env['stock.production.lot'].create({ + 'name': 'lot2', + 'product_id': p1.id, + }) + + self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 10, lot_id=lot_1) + self.env['stock.quant']._update_available_quantity(p1, self.stock_location, 10, lot_id=lot_2) + + self.env['stock.quant']._update_available_quantity(p2, self.stock_location, 5) + mo.action_assign() + + produce_wizard = self.env['mrp.product.produce'].with_context({ + 'active_id': mo.id, + 'active_ids': [mo.id], + }).create({ + 'product_qty': 5.0, + }) + + for produce_line in produce_wizard.produce_line_ids: + produce_line.qty_done = produce_line.qty_to_consume + produce_wizard.do_produce() + + mo.move_finished_ids.move_line_ids.qty_done -= 1 + update_quantity_wizard = self.env['change.production.qty'].create({ + 'mo_id': mo.id, + 'product_qty': 4, + }) + update_quantity_wizard.change_prod_qty() + + self.assertEqual(mo.move_raw_ids.filtered(lambda m: m.product_id == p1).quantity_done, 20, 'Update the produce quantity should not impact already produced quantity.') + mo.button_mark_done() diff --git a/addons/mrp/wizard/change_production_qty.py b/addons/mrp/wizard/change_production_qty.py index 38a125c3524..cddc9101043 100644 --- a/addons/mrp/wizard/change_production_qty.py +++ b/addons/mrp/wizard/change_production_qty.py @@ -50,7 +50,6 @@ class ChangeProductionQty(models.TransientModel): qty_produced = production.product_id.uom_id._compute_quantity(sum(done_moves.mapped('product_qty')), production.product_uom_id) factor = production.product_uom_id._compute_quantity(production.product_qty - qty_produced, production.bom_id.product_uom_id) / production.bom_id.product_qty boms, lines = production.bom_id.explode(production.product_id, factor, picking_type=production.bom_id.picking_type_id) - done_quantities = {move: move.quantity_done for move in production.move_raw_ids} for line, line_data in lines: production._update_raw_move(line, line_data) operation_bom_qty = {} @@ -60,9 +59,6 @@ class ChangeProductionQty(models.TransientModel): self._update_product_to_produce(production, production.product_qty - qty_produced) moves = production.move_raw_ids.filtered(lambda x: x.state not in ('done', 'cancel')) moves._action_assign() - for move in production.move_raw_ids: - if move.quantity_done != done_quantities[move]: - move._set_quantity_done(done_quantities[move]) for wo in production.workorder_ids: operation = wo.operation_id if operation_bom_qty.get(operation.id): From c5a3b607e6ee5ad4cafc6e427490ff361b79c707 Mon Sep 17 00:00:00 2001 From: Jorge Pinna Puissant Date: Mon, 7 Jan 2019 13:13:47 +0000 Subject: [PATCH 02/22] [FIX] website_sale: empty error message opw-1918450 Before this commit, the error message wasn't shown when a field was empty. Now, the error message shows correctly. closes odoo/odoo#29993 --- addons/website_sale/controllers/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_sale/controllers/main.py b/addons/website_sale/controllers/main.py index 37ddd182ca5..401ad96f511 100644 --- a/addons/website_sale/controllers/main.py +++ b/addons/website_sale/controllers/main.py @@ -539,7 +539,7 @@ class WebsiteSale(http.Controller): except ValidationError: error["vat"] = 'error' - if [err for err in error.items() if err == 'missing']: + if [err for err in error.values() if err == 'missing']: error_message.append(_('Some required fields are empty.')) return error, error_message From eb11b465dc0cfed5159b9f794a2762693d87fe14 Mon Sep 17 00:00:00 2001 From: Andreas Perhab Date: Thu, 1 Nov 2018 10:15:55 +0000 Subject: [PATCH 03/22] [FIX] odoo: fix addons paths on windows with use of normcase This is necessary to get --dev=xml working on windows, because the xml import uses normcase in file_open and the case normalized filename is then used to find the addon path (which was not found before on windows) With this fix arch_fs is now correctly filled in windows and --dev=xml works. closes odoo/odoo#28331 --- odoo/modules/module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/odoo/modules/module.py b/odoo/modules/module.py index 15f4aae9a50..daf7fb7c440 100644 --- a/odoo/modules/module.py +++ b/odoo/modules/module.py @@ -128,17 +128,17 @@ def initialize_sys_path(): global ad_paths global hooked - dd = tools.config.addons_data_dir + dd = os.path.normcase(tools.config.addons_data_dir) if os.access(dd, os.R_OK) and dd not in ad_paths: ad_paths.append(dd) for ad in tools.config['addons_path'].split(','): - ad = os.path.abspath(tools.ustr(ad.strip())) + ad = os.path.normcase(os.path.abspath(tools.ustr(ad.strip()))) if ad not in ad_paths: ad_paths.append(ad) # add base module path - base_path = os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons')) + base_path = os.path.normcase(os.path.abspath(os.path.join(os.path.dirname(os.path.dirname(__file__)), 'addons'))) if base_path not in ad_paths and os.path.isdir(base_path): ad_paths.append(base_path) From e2bdb65321264322cd94ffa460f0856ca19b5f12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Aur=C3=A9lien=20Warnon?= Date: Tue, 8 Jan 2019 14:15:41 +0000 Subject: [PATCH 04/22] [FIX] website_slides: hide slide download button if not available Task #1923443 Purpose ======= The slides on the website showed the 'download' button regardless of the value of the 'download_security' field. If the value is 'none', meaning that the slides are never downloadable, the button should be hidden. closes #29904 closes odoo/odoo#30051 --- addons/website_slides/view/website_slides_embed.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_slides/view/website_slides_embed.xml b/addons/website_slides/view/website_slides_embed.xml index 72614803325..c7010c22e60 100644 --- a/addons/website_slides/view/website_slides_embed.xml +++ b/addons/website_slides/view/website_slides_embed.xml @@ -133,7 +133,7 @@
- + From 1b434ed8c3f1d7dfaf688f7fc634060147313638 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 11 Jan 2019 09:58:53 +0000 Subject: [PATCH 05/22] [FIX] models: make prefetching more specific in read() Commit a07a076c45fc4b83caa9ab30ca919b30016015a7 restricts the prefetching to `self` when accessing the fields to return. This is too restrictive, as it cancels prefetching of secondary records in computed fields. In this commit we limit the scope of the restriction to `self`'s model only; this fixes the original issue without impacting other models. closes odoo/odoo#30133 --- odoo/models.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/odoo/models.py b/odoo/models.py index 85419013c0e..af3453a8b38 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -3019,14 +3019,16 @@ class BaseModel(object): # retrieve results from records; this takes values from the cache and # computes remaining fields + self = self.with_prefetch(self._prefetch.copy()) data = {record: {'id': record.id} for record in self} missing = set() use_name_get = (load == '_classic_read') for name in (stored + inherited + computed): convert = self._fields[name].convert_to_read - # read every field with prefetching limited to self; this avoids + # restrict the prefetching of self's model to self; this avoids # computing fields on a larger recordset than self - for record in self.with_prefetch(): + self._prefetch[self._name] = set(self._ids) + for record in self: try: data[record][name] = convert(record[name], record, use_name_get) except MissingError: From e1767490e939233c050f796f4ae4f6db627d53c3 Mon Sep 17 00:00:00 2001 From: Denis Vermylen Date: Thu, 27 Dec 2018 16:40:39 +0000 Subject: [PATCH 06/22] [FIX] purchase: loading time on partners with many children When loading a partner form view, the ORM prefetches the computed fields purchase_order_count and supplier_invoice_count for all the related partners as well (parent, children, ..). Instead of reading the orders and invoices once per partner, we group them all together. opw 1919933 closes odoo/odoo#29788 --- addons/purchase/models/res_partner.py | 31 ++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/addons/purchase/models/res_partner.py b/addons/purchase/models/res_partner.py index 8983a4b07b3..df138af9655 100644 --- a/addons/purchase/models/res_partner.py +++ b/addons/purchase/models/res_partner.py @@ -11,11 +11,32 @@ class res_partner(models.Model): @api.multi def _purchase_invoice_count(self): - PurchaseOrder = self.env['purchase.order'] - Invoice = self.env['account.invoice'] - for partner in self: - partner.purchase_order_count = PurchaseOrder.search_count([('partner_id', 'child_of', partner.id)]) - partner.supplier_invoice_count = Invoice.search_count([('partner_id', 'child_of', partner.id), ('type', '=', 'in_invoice')]) + # retrieve all children partners and prefetch 'parent_id' on them + all_partners = self.search([('id', 'child_of', self.ids)]) + all_partners.read(['parent_id']) + + purchase_order_groups = self.env['purchase.order'].read_group( + domain=[('partner_id', 'in', all_partners.ids)], + fields=['partner_id'], groupby=['partner_id'] + ) + for group in purchase_order_groups: + partner = self.browse(group['partner_id'][0]) + while partner: + if partner in self: + partner.purchase_order_count += group['partner_id_count'] + partner = partner.parent_id + + supplier_invoice_groups = self.env['account.invoice'].read_group( + domain=[('partner_id', 'in', all_partners.ids), + ('type', '=', 'in_invoice')], + fields=['partner_id'], groupby=['partner_id'] + ) + for group in supplier_invoice_groups: + partner = self.browse(group['partner_id'][0]) + while partner: + if partner in self: + partner.supplier_invoice_count += group['partner_id_count'] + partner = partner.parent_id @api.model def _commercial_fields(self): From f8e953fdd6eb5d9ce3f6713f9cd06665ca20bb2a Mon Sep 17 00:00:00 2001 From: Nans Lefebvre Date: Fri, 11 Jan 2019 08:56:00 +0000 Subject: [PATCH 07/22] [FIX] base: set related crud_model_name on the correct field Create an automated action that creates a new record (of target crud_model_id). Try using a link_field_id: nothing appears to be selectable, even if some fields satisfy the intended conditions. The issue is that the crud_model_name field was related to crud_model_id.name, instead of the correct crud_model_id.model (note the apt naming). Note that in the onchange the value of crud_model_name was correctly set to crud_model_id.model, adding to the confusion. opw 1910671 closes odoo/odoo#30145 --- odoo/addons/base/ir/ir_actions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/odoo/addons/base/ir/ir_actions.py b/odoo/addons/base/ir/ir_actions.py index ee6eb782048..bec32692acc 100644 --- a/odoo/addons/base/ir/ir_actions.py +++ b/odoo/addons/base/ir/ir_actions.py @@ -383,7 +383,7 @@ class IrActionsServer(models.Model): # Create crud_model_id = fields.Many2one('ir.model', string='Create/Write Target Model', oldname='srcmodel_id', help="Model for record creation / update. Set this field only to specify a different model than the base model.") - crud_model_name = fields.Char(related='crud_model_id.name', readonly=True) + crud_model_name = fields.Char(related='crud_model_id.model', readonly=True) link_field_id = fields.Many2one('ir.model.fields', string='Link using field', help="Provide the field used to link the newly created record " "on the record on used by the server action.") From a7cbac305ed8645ce8ce67210d2ca5d723a891f0 Mon Sep 17 00:00:00 2001 From: Nans Lefebvre Date: Thu, 10 Jan 2019 13:53:05 +0000 Subject: [PATCH 08/22] [FIX] web: reset offset in the search more view dialog Start on the modal obtained by a "search more". The offset is never reset. So suppose you are on page 2, looking at record 81-160. Do a research that gives less than 80 records. The result of the search is nothing, since is has been done with a 80 offset. It should be reset to 0 when we do a new search. opw 1920826 closes odoo/odoo#30109 --- .../web/static/src/js/views/view_dialogs.js | 2 +- .../tests/fields/relational_fields_tests.js | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index 163131d47f3..99c56bf8dd1 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -264,7 +264,7 @@ var SelectCreateDialog = ViewDialog.extend({ event.stopPropagation(); // prevent this event from bubbling up to the view manager var d = event.data; var searchData = this._process_search_data(d.domains, d.contexts, d.groupbys); - this.list_controller.reload(searchData); + this.list_controller.reload(_.extend({offset: 0}, searchData)); }, get_controller_context: '_onGetControllerContext', }), diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 3991d499f3d..5d2ab325ef2 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -525,6 +525,61 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('search more pager is reset when doing a new search', function (assert) { + assert.expect(6); + for(var i = 10; i < 180; i++) { + this.data.partner.records.push({ + id: i, + display_name: "Partner " + i, + }); + } + + function stringToEvent ($element, string) { + for (var i = 0; i < string.length; i++) { + var keyAscii = string.charCodeAt(i); + $element.val($element.val()+string[i]); + $element.trigger($.Event('keyup', {which: keyAscii, keyCode:keyAscii})); + } + $element.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER, keyCode:$.ui.keyCode.ENTER})); + } + + this.data.partner.fields.datetime.searchable = true; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + archs: { + 'partner,false,list': '', + 'partner,false,search': '', + }, + res_id: 1, + }); + + form.$buttons.find('.o_form_button_edit').click(); + var $dropdown = form.$('.o_field_many2one input').autocomplete('widget'); + form.$('.o_field_many2one input').click(); + $dropdown.find('.o_m2o_dropdown_option:contains(Search)').mouseenter().click(); + $('.modal .o_pager_next').click(); + + assert.strictEqual($('.o_pager_limit').text(), "1173", "there should be 173 records"); + assert.strictEqual($('.o_pager_value').text(), "181-160", "should display the second page"); + assert.strictEqual($('tr.o_data_row').length, 80, "should display 80 record"); + + stringToEvent($('.modal .o_searchview_input'), 'first'); + + assert.strictEqual($('.o_pager_limit').text(), "11", "there should be 1 record"); + assert.strictEqual($('.o_pager_value').text(), "11-1", "should display the first page"); + assert.strictEqual($('tr.o_data_row').length, 1, "should display 1 record"); + form.destroy(); + }); + QUnit.test('onchanges on many2ones trigger when editing record in form view', function (assert) { assert.expect(9); From ec25b9d01b27b33339d64c4833563c4f1c987dc8 Mon Sep 17 00:00:00 2001 From: Laurent Smet Date: Thu, 10 Jan 2019 13:36:09 +0000 Subject: [PATCH 09/22] [FIX] account: Prevent crash when having custom coa - Create manually an account.payment after having created manually some journals (without installing a coa). - Install a module containing a coa and calling try_loading_for_current_company or depending of other module doing such things. => Crash because company.chart_template_id is not set. -opw: 1921711 closes odoo/odoo#30117 --- addons/account/models/chart_template.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/account/models/chart_template.py b/addons/account/models/chart_template.py index 2718f7e67ce..0742516c930 100644 --- a/addons/account/models/chart_template.py +++ b/addons/account/models/chart_template.py @@ -142,8 +142,9 @@ class AccountChartTemplate(models.Model): 'bank_account_code_prefix': self.bank_account_code_prefix, 'cash_account_code_prefix': self.cash_account_code_prefix, }) - wizard.onchange_chart_template_id() - wizard.execute() + if not wizard.existing_accounting(company): + wizard.onchange_chart_template_id() + wizard.execute() @api.multi def open_select_template_wizard(self): From 9916c30da8211274284ca58d73d3d1826c91ce06 Mon Sep 17 00:00:00 2001 From: Andreas Perhab Date: Fri, 11 Jan 2019 11:16:04 +0000 Subject: [PATCH 10/22] [FIX] web_planner: test should not assume url parameter order test_web_planner throws errors like this on some environments: AssertionError: '/web#action=135&view_type=list' != '/web#view_type=list&action=135' the urls in web_planner are created with a dict for the parameters to create the urls. dicts are unorderd up to python 3.5. (see https://www.python.org/dev/peps/pep-0468/) As the urls /web#action=135&view_type=list and /web#view_type=list&action=135 are equivalent we now test if the parameters are the equal in the url regardless of their order. closes odoo/odoo#30138 --- addons/web_planner/tests/test_web_planner.py | 30 +++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/addons/web_planner/tests/test_web_planner.py b/addons/web_planner/tests/test_web_planner.py index 55b291c094c..cf5fe5547e6 100644 --- a/addons/web_planner/tests/test_web_planner.py +++ b/addons/web_planner/tests/test_web_planner.py @@ -3,6 +3,7 @@ from odoo.tests.common import TransactionCase from odoo.addons.web_planner.models.web_planner import Planner +from werkzeug import urls class TestWebPlanner(TransactionCase): @@ -26,10 +27,9 @@ class TestWebPlanner(TransactionCase): } ) - self.assertEqual( - Planner.prepare_backend_url(self, "web_planner.test_00"), - "/web#view_type=list&action=%s" % action.id, - ) + backend_url = Planner.prepare_backend_url(self, "web_planner.test_00") + parameters = urls.url_decode(urls.url_parse(backend_url).fragment).to_dict(flat=False) + self.assertEqual(parameters, {'view_type': ['list'], 'action': [str(action.id)]}) def test_01(self): #bad choise of view_type @@ -50,10 +50,9 @@ class TestWebPlanner(TransactionCase): } ) - self.assertEqual( - Planner.prepare_backend_url(self, "web_planner.test_01", 'form'), - "/web#view_type=list&action=%s" % action.id, - ) + backend_url = Planner.prepare_backend_url(self, "web_planner.test_01", 'form') + parameters = urls.url_decode(urls.url_parse(backend_url).fragment).to_dict(flat=False) + self.assertEqual(parameters, {'view_type': ['list'], 'action': [str(action.id)]}) def test_02(self): #choose the second view_mode @@ -74,10 +73,9 @@ class TestWebPlanner(TransactionCase): } ) - self.assertEqual( - Planner.prepare_backend_url(self, "web_planner.test_02", 'form'), - "/web#view_type=form&action=%s" % action.id, - ) + backend_url = Planner.prepare_backend_url(self, "web_planner.test_02", 'form') + parameters = urls.url_decode(urls.url_parse(backend_url).fragment).to_dict(flat=False) + self.assertEqual(parameters, {'view_type': ['form'], 'action': [str(action.id)]}) def test_03(self): #bad xml_id @@ -98,8 +96,6 @@ class TestWebPlanner(TransactionCase): } ) - self.assertEqual( - Planner.prepare_backend_url(self, "web_planner.bad_xml_id", 'form'), - "/web#view_type=list&model=ir.module.module", - ) - + backend_url = Planner.prepare_backend_url(self, "web_planner.bad_xml_id", 'form') + parameters = urls.url_decode(urls.url_parse(backend_url).fragment).to_dict(flat=False) + self.assertEqual(parameters, {'view_type': ['list'], 'model': ['ir.module.module']}) From c99e1facf92ba419ae7c761acbe10bc1078ebdaa Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Tue, 15 Jan 2019 10:26:55 +0000 Subject: [PATCH 11/22] [FIX] purchase: in a x2m fields in tree must be in form too On the PO form view, change the order_lines inline view: - the tree becomes not editable - allowed modes are tree,form Add a new order line, the form view of order_lines opens up Modify some stuff, then save and new Before this commit, it crashed because some fields were expected in the tree view that were not defaulted in the form view by the JS framework. After this commit, this flow doesn't crash because we make sure every field in the tree view is present in the form view OPW 1917111 closes odoo/odoo#30219 --- addons/purchase/views/purchase_views.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/purchase/views/purchase_views.xml b/addons/purchase/views/purchase_views.xml index f215b2852ca..2dcff5b68b0 100644 --- a/addons/purchase/views/purchase_views.xml +++ b/addons/purchase/views/purchase_views.xml @@ -244,6 +244,7 @@ + @@ -253,6 +254,7 @@ + From dec9f7c4cec68863c2d4fa82235e3fd2b6def4d5 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 11 Jan 2019 14:36:13 +0000 Subject: [PATCH 12/22] [FIX] web: one2many list: quickly switch between pages Before this rev., a crash might occur when the user quickly switched twice between pages (e.g. go to page 2, then page 3) on a slow network. In the given example, when data of page 2 returned, the list was re-rendered. Unfortunately, the offset of that list' datapoint was already changed due to the switch to page 3, meaning that the view tried to render a page that wasn't loaded yet, leading to a crash if there were modifiers to evaluate, or to empty records being displayed. This rev. ensures that the view is rendered with the data of the page it expects. closes odoo/odoo#30150 --- .../src/js/views/field_manager_mixin.js | 6 ++- .../tests/fields/relational_fields_tests.js | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/views/field_manager_mixin.js b/addons/web/static/src/js/views/field_manager_mixin.js index 0bc1b5acfce..2b38ca4403d 100644 --- a/addons/web/static/src/js/views/field_manager_mixin.js +++ b/addons/web/static/src/js/views/field_manager_mixin.js @@ -130,8 +130,10 @@ var FieldManagerMixin = { if ('offset' in data) { params.offset = data.offset; } - this.model.reload(data.id, params).then(function (db_id) { - data.on_success(self.model.get(db_id)); + this.mutex.exec(function () { + return self.model.reload(data.id, params).then(function (db_id) { + data.on_success(self.model.get(db_id)); + }); }); }, /** diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 5d2ab325ef2..421cee8a4b1 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -10093,6 +10093,48 @@ QUnit.module('relational_fields', { form.destroy(); }); + QUnit.test('quickly switch between pages in one2many list', function (assert) { + assert.expect(2); + + this.data.partner.records[0].turtles = [1, 2, 3]; + + var readDefs = [$.when(), $.Deferred(), $.Deferred()]; + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '' + + '' + + '
', + mockRPC: function (route, args) { + var result = this._super.apply(this, arguments); + if (args.method === 'read') { + var recordID = args.args[0][0]; + return $.when(readDefs[recordID - 1]).then(_.constant(result)); + } + return result; + }, + res_id: 1, + }); + + form.$('.o_field_widget[name=turtles] .o_pager_next').click(); + form.$('.o_field_widget[name=turtles] .o_pager_next').click(); + + readDefs[1].resolve(); + + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_data_cell').text(), 'donatello'); + + readDefs[2].resolve(); + + assert.strictEqual(form.$('.o_field_widget[name=turtles] .o_data_cell').text(), 'raphael'); + + form.destroy(); + }); + QUnit.module('FieldMany2Many'); QUnit.test('many2many kanban: edition', function (assert) { From 6e3661d53f36324a6db9972ab615480d320e731b Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Thu, 1 Feb 2018 13:12:12 +0000 Subject: [PATCH 13/22] [FIX] payment_sips: allow to customize key_version Some sips provider don't use 2 as key_version. E.g. mercanet uses '1' as production key. Now we allow to override it in Ir Config Parameter for stable version. Todo: Need to make it customizable by end user into the configuration of acquirer. Courtesy of BEK for reporting opw-1916316 closes odoo/odoo#30185 --- addons/payment_sips/models/payment.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/payment_sips/models/payment.py b/addons/payment_sips/models/payment.py index 515bb08ee97..4b2122cec45 100644 --- a/addons/payment_sips/models/payment.py +++ b/addons/payment_sips/models/payment.py @@ -81,7 +81,7 @@ class AcquirerSips(models.Model): if self.environment == 'prod': # For production environment, key version 2 is required merchant_id = getattr(self, 'sips_merchant_id') - key_version = '2' + key_version = self.env['ir.config_parameter'].sudo().get_param('sips.key_version', '2') else: # Test key provided by Atos Wordline works only with version 1 merchant_id = '002001000000001' From 9c6d9697754d3e661794fa7be890ebccde6818ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Manuel=20V=C3=A1zquez=20Acosta?= Date: Mon, 24 Dec 2018 21:16:30 +0000 Subject: [PATCH 14/22] [CLA] Add Larisa to Merchise Autrement. --- doc/cla/corporate/merchise.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/cla/corporate/merchise.md b/doc/cla/corporate/merchise.md index e4cbd3c3f95..64d5b277ccd 100644 --- a/doc/cla/corporate/merchise.md +++ b/doc/cla/corporate/merchise.md @@ -21,3 +21,4 @@ Medardo Rodríguez med@merchise.org https://github.com/med-merchise Abel Firvida firvida@merchise.org Abel Firvida abel@merchise.org Mónica Díaz Pena monicadp@merchise.org https://github.com/mdpena +Larisa González larisa@merchise.org From 6edfadbdd2abf13aed42a9cd7392ca42edbbc52e Mon Sep 17 00:00:00 2001 From: larisa Date: Mon, 24 Dec 2018 20:17:47 +0000 Subject: [PATCH 15/22] [FIX] hr_payroll_account: correct account move lines creation The super call of action_payslip_done executes the method compute_sheet. Without this compute_sheet call, there is no salary lines and no accounting move is created. closes odoo/odoo#29737 --- addons/hr_payroll_account/models/hr_payroll_account.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/hr_payroll_account/models/hr_payroll_account.py b/addons/hr_payroll_account/models/hr_payroll_account.py index e76173d45a4..93d2ad307bf 100644 --- a/addons/hr_payroll_account/models/hr_payroll_account.py +++ b/addons/hr_payroll_account/models/hr_payroll_account.py @@ -53,6 +53,7 @@ class HrPayslip(models.Model): @api.multi def action_payslip_done(self): + res = super(HrPayslip, self).action_payslip_done() precision = self.env['decimal.precision'].precision_get('Payroll') for slip in self: @@ -138,7 +139,7 @@ class HrPayslip(models.Model): move = self.env['account.move'].create(move_dict) slip.write({'move_id': move.id, 'date': date}) move.post() - return super(HrPayslip, self).action_payslip_done() + return res class HrSalaryRule(models.Model): From 1e966452ff1c29b0e31048159780fc623e21a4b3 Mon Sep 17 00:00:00 2001 From: Nans Lefebvre Date: Tue, 15 Jan 2019 12:50:08 +0000 Subject: [PATCH 16/22] [FIX] l10n_be_invoice_bba: fix mail template The field reference_type has been renamed invoice_reference_type in v11. The selection value 'bba' for that field has been renamed 'structured'. We update the template to reflect these changes. Discovered thanks to a forward port of that field up to v12. closes odoo/odoo#30224 --- addons/l10n_be_invoice_bba/data/mail_template_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/l10n_be_invoice_bba/data/mail_template_data.xml b/addons/l10n_be_invoice_bba/data/mail_template_data.xml index b7110f157e7..ea2d688cdb9 100644 --- a/addons/l10n_be_invoice_bba/data/mail_template_data.xml +++ b/addons/l10n_be_invoice_bba/data/mail_template_data.xml @@ -33,7 +33,7 @@ from ${object.company_id.name}. % endif

-% if object.reference_type=='bba' and object.reference: +% if object.invoice_reference_type=='structured' and object.reference:

Please use the following communication for your payment : ${object.reference}

% endif From 8e87075724d0e9e4433d5bf5c3dfd3e62b48d67b Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Tue, 15 Jan 2019 14:28:01 +0000 Subject: [PATCH 17/22] [FIX] sale: inline form SO contains price_total When removing the editable attribute on the list view of th o2m towards order_line, when adding a line, the price_total of the line was neither computed nor sent to the onchange of the sale order now it is OPW 1914740 closes odoo/odoo#30234 --- addons/sale/views/sale_views.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml index dd8104d6938..ceaad5477ee 100644 --- a/addons/sale/views/sale_views.xml +++ b/addons/sale/views/sale_views.xml @@ -253,6 +253,7 @@ +