diff --git a/addons/mrp/models/stock_orderpoint.py b/addons/mrp/models/stock_orderpoint.py index 4b1c1613376..e65e3481c15 100644 --- a/addons/mrp/models/stock_orderpoint.py +++ b/addons/mrp/models/stock_orderpoint.py @@ -75,6 +75,16 @@ class StockWarehouseOrderpoint(models.Model): res[orderpoint.id] = orderpoint.product_id.uom_id._compute_quantity(product_qty, orderpoint.product_uom, round=False) return res + def _get_qty_multiple_to_order(self): + """ Calculates the minimum quantity that can be ordered according to the qty and UoM of the BoM + """ + self.ensure_one() + qty_multiple_to_order = super()._get_qty_multiple_to_order() + if 'manufacture' in self.rule_ids.mapped('action'): + bom = self.env['mrp.bom']._bom_find(product=self.product_id, bom_type='normal') + return bom.product_uom_id._compute_quantity(bom.product_qty, self.product_uom) + return qty_multiple_to_order + def _set_default_route_id(self): route_id = self.env['stock.rule'].search([ ('action', '=', 'manufacture') diff --git a/addons/mrp/tests/test_bom.py b/addons/mrp/tests/test_bom.py index fc16d68a4c6..a9c41a1151d 100644 --- a/addons/mrp/tests/test_bom.py +++ b/addons/mrp/tests/test_bom.py @@ -835,3 +835,51 @@ class TestBoM(TestMrpCommon): line.product_uom_id = uom_unit line.product_qty = 5 bom_finished = bom_finished.save() + + def test_replenishment(self): + """ Tests the auto generation of manual orderpoints. + The multiple quantity of the orderpoint should be the + quantity of the BoM in the UoM of the product. + """ + + uom_kg = self.env.ref('uom.product_uom_kgm') + uom_gram = self.env.ref('uom.product_uom_gram') + + product_gram = self.env['product.product'].create({ + 'name': 'Product sold in grams', + 'type': 'product', + 'uom_id': uom_gram.id, + 'uom_po_id': uom_gram.id, + }) + # We create a BoM that manufactures 2kg of product + self.env['mrp.bom'].create({ + 'product_id': product_gram.id, + 'product_tmpl_id': product_gram.product_tmpl_id.id, + 'product_uom_id': uom_kg.id, + 'product_qty': 2.0, + 'type': 'normal', + }) + # We create a delivery order of 2300 grams + picking_form = Form(self.env['stock.picking']) + picking_form.picking_type_id = self.env.ref('stock.picking_type_out') + with picking_form.move_ids_without_package.new() as move: + move.product_id = product_gram + move.product_uom_qty = 2300.0 + customer_picking = picking_form.save() + customer_picking.action_confirm() + + # We check the created orderpoint without manufacturing route manufacturing + self.env['stock.warehouse.orderpoint']._get_orderpoint_action() + self.env['stock.warehouse.orderpoint']._get_orderpoint_action() + orderpoint = self.env['stock.warehouse.orderpoint'].search([('product_id', '=', product_gram.id)]) + self.assertEqual(orderpoint.qty_multiple, 0.0) + self.assertEqual(orderpoint.qty_to_order, 2300.0) + + # We select the manufacturing route and check the impact on the quantities + manufacturing_route_id = self.ref('mrp.route_warehouse0_manufacture') + manufacturing_route = self.env['stock.location.route'].search([('id', '=', manufacturing_route_id)]) + orderpoint_form = Form(orderpoint) + orderpoint_form.route_id = manufacturing_route + orderpoint_form.save() + self.assertEqual(orderpoint.qty_multiple, 2000.0) + self.assertEqual(orderpoint.qty_to_order, 4000.0) diff --git a/addons/stock/models/stock_orderpoint.py b/addons/stock/models/stock_orderpoint.py index b72c23d3a2a..63ac7abb1c2 100644 --- a/addons/stock/models/stock_orderpoint.py +++ b/addons/stock/models/stock_orderpoint.py @@ -200,6 +200,11 @@ class StockWarehouseOrderpoint(models.Model): ('company_id', '=', self.company_id.id) ], limit=1) + @api.onchange('route_id') + def _onchange_route_id(self): + if self.route_id: + self.qty_multiple = self._get_qty_multiple_to_order() + def write(self, vals): if 'company_id' in vals: for orderpoint in self: @@ -259,6 +264,12 @@ class StockWarehouseOrderpoint(models.Model): qty_to_order += orderpoint.qty_multiple - remainder orderpoint.qty_to_order = qty_to_order + def _get_qty_multiple_to_order(self): + """ Calculates the minimum quantity that can be ordered according to the PO UoM or BoM + """ + self.ensure_one() + return 0 + def _set_default_route_id(self): """ Write the `route_id` field on `self`. This method is intendend to be called on the orderpoints generated when openning the replenish report. @@ -358,8 +369,8 @@ class StockWarehouseOrderpoint(models.Model): orderpoints = self.env['stock.warehouse.orderpoint'].with_user(SUPERUSER_ID).create(orderpoint_values_list) for orderpoint in orderpoints: - orderpoint.route_id = orderpoint.product_id.route_ids[:1] - orderpoints.filtered(lambda o: not o.route_id)._set_default_route_id() + orderpoint.route_id = orderpoint.product_id.route_ids[:1] or orderpoint._set_default_route_id() + orderpoint.qty_multiple = orderpoint._get_qty_multiple_to_order() return action @api.model diff --git a/addons/stock/views/stock_orderpoint_views.xml b/addons/stock/views/stock_orderpoint_views.xml index 3aec2b665ec..913f1e170db 100644 --- a/addons/stock/views/stock_orderpoint_views.xml +++ b/addons/stock/views/stock_orderpoint_views.xml @@ -153,6 +153,8 @@ + +