From 9777a2b4d64c0d1cbf5144c6c9865bf3a44f7e07 Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Mon, 18 Jan 2021 14:37:07 +0000 Subject: [PATCH] [FIX] mrp: kits and PO based on reordering rules If a product is a kit and has a storable component, if this component is already ordered (with sufficient quantity), when running the scheduler, the ordered quantity of this component will still be increased. To reproduce the error: 1. Create two products P_kit and P_compo - Both are storable products - P_compo must have at least one vendor 2. Create a BoM for P_kit: - Must be a kit - Add P_compo to components 3. Set a reordering rule for P_kit 4. Inventory > Operations > Run Scheduler 5. Go to Purchase and find the generated RfQ - The ordered quantity is correct 6. Repeat 4-5 Error: This time, the ordered quantity is incorrect. While you don't have any P_kit on hand, each time the scheduler is run, the ordered quantity will increase. A similar issue has already been fixed: #63891. The original commit targets the version 13, but the fix has also been applied to v14 (#64277). However, here the case is slightly different: the user run the scheduler twice while the first RfQ is not yet confirmed and received. In such a case, the scheduler must also check the "quantity in progress". In version 14, it seems nothing computes this quantity for the kits. OPW-2421841 closes odoo/odoo#65100 X-original-commit: f53ab55000c32fd67c567f01a9533298936daaac Signed-off-by: Adrien Widart --- addons/mrp/models/stock_orderpoint.py | 33 +++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/addons/mrp/models/stock_orderpoint.py b/addons/mrp/models/stock_orderpoint.py index ac2bc0df7a2..4b1c1613376 100644 --- a/addons/mrp/models/stock_orderpoint.py +++ b/addons/mrp/models/stock_orderpoint.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import _, api, fields, models +from odoo.tools.float_utils import float_is_zero class StockWarehouseOrderpoint(models.Model): @@ -42,6 +43,38 @@ class StockWarehouseOrderpoint(models.Model): for orderpoint in self: orderpoint.show_bom = orderpoint.route_id.id in manufacture_route + def _quantity_in_progress(self): + bom_kit_orderpoints = { + orderpoint: bom + for orderpoint in self + for bom in self.env['mrp.bom']._bom_find(product=orderpoint.product_id, bom_type='phantom') + if bom + } + res = super(StockWarehouseOrderpoint, self.filtered(lambda p: p not in bom_kit_orderpoints))._quantity_in_progress() + for orderpoint in bom_kit_orderpoints: + dummy, bom_sub_lines = bom_kit_orderpoints[orderpoint].explode(orderpoint.product_id, 1) + ratios_qty_available = [] + # total = qty_available + in_progress + ratios_total = [] + for bom_line, bom_line_data in bom_sub_lines: + component = bom_line.product_id + if component.type != 'product' or float_is_zero(bom_line_data['qty'], precision_rounding=bom_line.product_uom_id.rounding): + continue + uom_qty_per_kit = bom_line_data['qty'] / bom_line_data['original_qty'] + qty_per_kit = bom_line.product_uom_id._compute_quantity(uom_qty_per_kit, bom_line.product_id.uom_id, raise_if_failure=False) + if not qty_per_kit: + continue + qty_by_product_location, dummy = component._get_quantity_in_progress(orderpoint.location_id.ids) + qty_in_progress = qty_by_product_location.get((component.id, orderpoint.location_id.id), 0.0) + qty_available = component.qty_available / qty_per_kit + ratios_qty_available.append(qty_available) + ratios_total.append(qty_available + (qty_in_progress / qty_per_kit)) + # For a kit, the quantity in progress is : + # (the quantity if we have received all in-progress components) - (the quantity using only available components) + product_qty = min(ratios_total or [0]) - min(ratios_qty_available or [0]) + res[orderpoint.id] = orderpoint.product_id.uom_id._compute_quantity(product_qty, orderpoint.product_uom, round=False) + return res + def _set_default_route_id(self): route_id = self.env['stock.rule'].search([ ('action', '=', 'manufacture')