From 31bf2fd446ca8bcb9fac3e51e146a10210b24376 Mon Sep 17 00:00:00 2001 From: Adrien Widart Date: Tue, 29 Dec 2020 15:07:36 +0000 Subject: [PATCH] [FIX] mrp: reordering rules for BoM product When running the scheduler, if one of the concerned product is a kit, it will always generate a RfQ for the components, even if there are already enough of them. To reproduce the error: (Need purchase) 1. Create 2 products P and P_compo - Both must be storable products - P_compo must have at least one vendor 2. Go on P-product page 3. Add a reordering rule (e.g., min: 5 and max: 10) 4. Add a BoM - Must be a Kit - Add P_compo to components 5. Inventory > Operations > Run Scheduler 6. Go to Purchase, find the generated RfQ 7. Confirm the order, Receive Products, Validate 8. Go on P-product's page - As you can see, you have enough P-products on hand 9. Repeat steps 5-6 => You should not find any RfQ since you already have enough P-products. When checking the P-product's page, you can see the correct quantity on hand because it uses the `_compute_quantities` method and this one has been overridden in the mrp-module so a kit will be correctly computed. However, when running the scheduler, it uses another method to get the current quantities: `_compute_quantities_dict`. Since this method has not been overridden in mrp-module, it will use the basic implementation and this one does not consider the possibility for a product to be a kit. This fix creates an override of `_compute_quantities_dict` so it can compute the correct quantities of products with 'phantom' as BoM type. Moreover, for the code to be simpler and clearer, thix fix also merges `_compute_quantities` and the new `_compute_quantities_dict`. OPW-2421841 closes odoo/odoo#64326 X-original-commit: 609e2f4427082431b56aa8f999d74ed9d39db653 Signed-off-by: Nicolas Lempereur (nle) Signed-off-by: Adrien Widart --- addons/mrp/models/product.py | 62 +++++++++++++----------------------- 1 file changed, 22 insertions(+), 40 deletions(-) diff --git a/addons/mrp/models/product.py b/addons/mrp/models/product.py index 74be5263df9..aca579e9272 100644 --- a/addons/mrp/models/product.py +++ b/addons/mrp/models/product.py @@ -56,34 +56,6 @@ class ProductTemplate(models.Model): } return action - def _compute_quantities(self): - """ When the product template is a kit, this override computes the fields : - - 'virtual_available' - - 'qty_available' - - 'incoming_qty' - - 'outgoing_qty' - """ - product_without_bom = self.browse([]) - for product_template in self: - if not self.env['mrp.bom']._bom_find(product_tmpl=product_template, bom_type='phantom'): - product_without_bom |= product_template - continue - virtual_available = 0 - qty_available = 0 - incoming_qty = 0 - outgoing_qty = 0 - for product in product_template.product_variant_ids: - if self.env['mrp.bom']._bom_find(product=product, bom_type='phantom'): - qty_available += product.qty_available - virtual_available += product.virtual_available - incoming_qty += product.incoming_qty - outgoing_qty += product.outgoing_qty - product_template.qty_available = qty_available - product_template.virtual_available = virtual_available - product_template.incoming_qty = incoming_qty - product_template.outgoing_qty = outgoing_qty - super(ProductTemplate, product_without_bom)._compute_quantities() - class ProductProduct(models.Model): _inherit = "product.product" @@ -141,19 +113,17 @@ class ProductProduct(models.Model): continue product.mrp_product_qty = float_round(mapped_data.get(product.id, 0), precision_rounding=product.uom_id.rounding) - def _compute_quantities(self): + def _compute_quantities_dict(self, lot_id, owner_id, package_id, from_date=False, to_date=False): """ When the product is a kit, this override computes the fields : - 'virtual_available' - 'qty_available' - 'incoming_qty' - 'outgoing_qty' - 'free_qty' + + This override is used to get the correct quantities of products + with 'phantom' as BoM type. """ - self.virtual_available = 0 - self.qty_available = 0 - self.incoming_qty = 0 - self.outgoing_qty = 0 - self.free_qty = 0 bom_kits = { product: bom for product in self @@ -161,7 +131,7 @@ class ProductProduct(models.Model): if bom } kits = self.filtered(lambda p: bom_kits.get(p)) - super(ProductProduct, self.filtered(lambda p: p not in bom_kits))._compute_quantities() + res = super(ProductProduct, self - kits)._compute_quantities_dict(lot_id, owner_id, package_id, from_date=from_date, to_date=to_date) for product in bom_kits: boms, bom_sub_lines = bom_kits[product].explode(product, 1) ratios_virtual_available = [] @@ -186,11 +156,23 @@ class ProductProduct(models.Model): ratios_outgoing_qty.append(component.outgoing_qty / qty_per_kit) ratios_free_qty.append(component.free_qty / qty_per_kit) if bom_sub_lines and ratios_virtual_available: # Guard against all cnsumable bom: at least one ratio should be present. - product.virtual_available = min(ratios_virtual_available) // 1 - product.qty_available = min(ratios_qty_available) // 1 - product.incoming_qty = min(ratios_incoming_qty) // 1 - product.outgoing_qty = min(ratios_outgoing_qty) // 1 - product.free_qty = min(ratios_free_qty) // 1 + res[product.id] = { + 'virtual_available': min(ratios_virtual_available) // 1, + 'qty_available': min(ratios_qty_available) // 1, + 'incoming_qty': min(ratios_incoming_qty) // 1, + 'outgoing_qty': min(ratios_outgoing_qty) // 1, + 'free_qty': min(ratios_free_qty) // 1, + } + else: + res[product.id] = { + 'virtual_available': 0, + 'qty_available': 0, + 'incoming_qty': 0, + 'outgoing_qty': 0, + 'free_qty': 0, + } + + return res def action_view_bom(self): action = self.env["ir.actions.actions"]._for_xml_id("mrp.product_open_bom")