Files
odoo_source/addons/mrp/models/stock_orderpoint.py
T
Adrien Widart 9777a2b4d6 [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 <adwid@users.noreply.github.com>
2021-01-27 09:57:57 +00:00

91 lines
4.5 KiB
Python

# -*- coding: utf-8 -*-
# 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):
_inherit = 'stock.warehouse.orderpoint'
show_bom = fields.Boolean('Show BoM column', compute='_compute_show_bom')
bom_id = fields.Many2one(
'mrp.bom', string='Bill of Materials', check_company=True,
domain="[('type', '=', 'normal'), '&', '|', ('company_id', '=', company_id), ('company_id', '=', False), '|', ('product_id', '=', product_id), '&', ('product_id', '=', False), ('product_tmpl_id', '=', product_tmpl_id)]")
def _get_replenishment_order_notification(self):
self.ensure_one()
production = self.env['mrp.production'].search([
('orderpoint_id', 'in', self.ids)
], order='create_date desc', limit=1)
if production:
action = self.env.ref('mrp.action_mrp_production_form')
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
'params': {
'title': _('The following replenishment order has been generated'),
'message': '%s',
'links': [{
'label': production.name,
'url': f'#action={action.id}&id={production.id}&model=mrp.production'
}],
'sticky': False,
}
}
return super()._get_replenishment_order_notification()
@api.depends('route_id')
def _compute_show_bom(self):
manufacture_route = []
for res in self.env['stock.rule'].search_read([('action', '=', 'manufacture')], ['route_id']):
manufacture_route.append(res['route_id'][0])
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')
]).route_id
orderpoint_wh_bom = self.filtered(lambda o: o.product_id.bom_ids)
if route_id and orderpoint_wh_bom:
orderpoint_wh_bom.route_id = route_id[0].id
return super()._set_default_route_id()
def _prepare_procurement_values(self, date=False, group=False):
values = super()._prepare_procurement_values(date=date, group=group)
values['bom_id'] = self.bom_id
return values