[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) <nle@odoo.com> Signed-off-by: Adrien Widart <adwid@users.noreply.github.com>
This commit is contained in:
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user