diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 45223d39d1..79cb61c797 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -1771,6 +1771,7 @@ class MrpProduction(models.Model): ('qty_done', '=', 1), ('state', '=', 'done'), ('location_dest_id.usage', '=', 'production'), + ('production_id', '!=', False), ]) if duplicates: # Maybe some move lines have been compensated by unbuild diff --git a/addons/mrp/tests/test_unbuild.py b/addons/mrp/tests/test_unbuild.py index c13606acdc..054c8fe7d8 100644 --- a/addons/mrp/tests/test_unbuild.py +++ b/addons/mrp/tests/test_unbuild.py @@ -737,3 +737,81 @@ class TestUnbuild(TestMrpCommon): {'product_id': p2.id, 'location_id': prod_location.id, 'location_dest_id': subloc02.id}, {'product_id': p1.id, 'location_id': prod_location.id, 'location_dest_id': subloc02.id}, ]) + + def test_use_unbuilt_sn_in_mo(self): + """ + use an unbuilt serial number in manufacturing order: + produce a tracked product, unbuild it and then use it as a component with the same SN in a mo. + """ + product_1 = self.env['product.product'].create({ + 'name': 'Product tracked by sn', + 'type': 'product', + 'tracking': 'serial', + }) + product_1_sn = self.env['stock.production.lot'].create({ + 'product_id': product_1.id, + 'company_id': self.env.company.id}) + component = self.env['product.product'].create({ + 'name': 'Product component', + 'type': 'product', + }) + bom_1 = self.env['mrp.bom'].create({ + 'product_id': product_1.id, + 'product_tmpl_id': product_1.product_tmpl_id.id, + 'product_uom_id': self.env.ref('uom.product_uom_unit').id, + 'product_qty': 1.0, + 'type': 'normal', + 'bom_line_ids': [ + (0, 0, {'product_id': component.id, 'product_qty': 1}), + ], + }) + product_2 = self.env['product.product'].create({ + 'name': 'finished Product', + 'type': 'product', + }) + self.env['mrp.bom'].create({ + 'product_id': product_2.id, + 'product_tmpl_id': product_2.product_tmpl_id.id, + 'product_uom_id': self.env.ref('uom.product_uom_unit').id, + 'product_qty': 1.0, + 'type': 'normal', + 'bom_line_ids': [ + (0, 0, {'product_id': product_1.id, 'product_qty': 1}), + ], + }) + # mo1 + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = product_1 + mo_form.bom_id = bom_1 + mo_form.product_qty = 1.0 + mo = mo_form.save() + mo.action_confirm() + + mo_form = Form(mo) + mo_form.qty_producing = 1.0 + mo_form.lot_producing_id = product_1_sn + mo = mo_form.save() + mo.button_mark_done() + self.assertEqual(mo.state, 'done', "Production order should be in done state.") + + #unbuild order + unbuild_form = Form(self.env['mrp.unbuild']) + unbuild_form.mo_id = mo + unbuild_form.lot_id = product_1_sn + unbuild_form.save().action_unbuild() + + #mo2 + mo_form = Form(self.env['mrp.production']) + mo_form.product_id = product_2 + mo2 = mo_form.save() + mo2.action_confirm() + details_operation_form = Form(mo2.move_raw_ids[0], view=self.env.ref('stock.view_stock_move_operations')) + with details_operation_form.move_line_ids.new() as ml: + ml.lot_id = product_1_sn + ml.qty_done = 1 + details_operation_form.save() + mo_form = Form(mo2) + mo_form.qty_producing = 1 + mo2 = mo_form.save() + mo2.button_mark_done() + self.assertEqual(mo2.state, 'done', "Production order should be in done state.") diff --git a/addons/payment/models/payment_acquirer.py b/addons/payment/models/payment_acquirer.py index eabe33e7b9..e6216b4add 100644 --- a/addons/payment/models/payment_acquirer.py +++ b/addons/payment/models/payment_acquirer.py @@ -679,7 +679,7 @@ class PaymentTransaction(models.Model): self.ensure_one() payment_vals = { - 'amount': self.amount, + 'amount': abs(self.amount), 'payment_type': 'inbound' if self.amount > 0 else 'outbound', 'currency_id': self.currency_id.id, 'partner_id': self.partner_id.commercial_partner_id.id, diff --git a/addons/purchase_stock/models/purchase.py b/addons/purchase_stock/models/purchase.py index 596d7bbf69..33e7a03fb4 100644 --- a/addons/purchase_stock/models/purchase.py +++ b/addons/purchase_stock/models/purchase.py @@ -490,6 +490,7 @@ class PurchaseOrderLine(models.Model): 'warehouse_id': self.order_id.picking_type_id.warehouse_id.id, 'product_uom_qty': product_uom_qty, 'product_uom': product_uom.id, + 'sequence': self.sequence, } @api.model diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index ba417bdd11..f131fc9bbb 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -548,6 +548,21 @@ options.Class.include({ const enableXmlIDs = xmlID.split(/\s*,\s*/); const disableXmlIDs = allXmlIDs.filter(xmlID => !enableXmlIDs.includes(xmlID)); + if (params.name === 'header_sidebar_opt') { + // When the user selects sidebar as header, make sure that the + // header position is regular. + // TODO we should avoid having that `if` in the generic option + // class (maybe simply use data-trigger but the header template + // option as no associated data-js to hack). To adapt in master. + await new Promise(resolve => { + this.trigger_up('action_demand', { + actionName: 'toggle_page_option', + params: [{name: 'header_overlay', value: false}], + onSuccess: () => resolve(), + }); + }); + } + return this._rpc({ route: '/website/theme_customize', params: { @@ -1940,6 +1955,25 @@ options.registry.TopMenuVisibility = VisibilityPageOptionUpdate.extend({ } return _super(...arguments); }, + /** + * @override + */ + _computeWidgetVisibility(widgetName, params) { + if (widgetName === 'header_visibility_opt') { + return this.$target[0].classList.contains('o_header_sidebar') ? '' : 'true'; + } + return this._super(...arguments); + }, + /** + * @override + */ + _renderCustomXML(uiFragment) { + // TODO in master: put this in the XML. + const weSelectEl = uiFragment.querySelector('we-select#option_header_visibility'); + if (weSelectEl) { + weSelectEl.dataset.name = 'header_visibility_opt'; + } + }, }); options.registry.topMenuColor = options.Class.extend({ diff --git a/addons/website_hr_recruitment/controllers/main.py b/addons/website_hr_recruitment/controllers/main.py index bb9b5870aa..d50e33c56f 100644 --- a/addons/website_hr_recruitment/controllers/main.py +++ b/addons/website_hr_recruitment/controllers/main.py @@ -45,8 +45,8 @@ class WebsiteHrRecruitment(http.Controller): # Filter job / office for country if country and not kwargs.get('all_countries'): - jobs = [j for j in jobs if j.address_id is None or j.address_id.country_id and j.address_id.country_id.id == country.id] - offices = set(j.address_id for j in jobs if j.address_id is None or j.address_id.country_id and j.address_id.country_id.id == country.id) + jobs = [j for j in jobs if not j.address_id or j.address_id.country_id.id == country.id] + offices = set(j.address_id for j in jobs if not j.address_id or j.address_id.country_id.id == country.id) else: offices = set(j.address_id for j in jobs if j.address_id)