[IMP] mrp: suggest lot on workorder

On a manufactoring order, 'checking availability' will make the reservation of
the components to consume. If those components are tracked by lot or serial numbers, the
reservation will also set a available lot/sn on the move lines and workorder lines.

The reservation already works on product produce wizard lines and workorder line
(form view). We need this commit as preliminary work to being able to suggest them
on the workorders tablet view too.

In case of requesting the reservation after planning the production (applying
reservation on already created workorder lines and checks), we deleted all lines
not yet consumed. They are then recreated with the reservation.

The user can obiously still use the lot/sn he wants by changing it onto the
workorder.

Task : 1891864

closes odoo/odoo#28020

Signed-off-by: Simon Lejeune (sle) <sle@openerp.com>
This commit is contained in:
William Henrotin
2019-04-04 11:32:42 +00:00
parent 7556938fc7
commit 2190568d9e
3 changed files with 61 additions and 9 deletions
+32 -7
View File
@@ -75,18 +75,24 @@ class MrpAbstractWorkorder(models.AbstractModel):
# Remove or lower quantity on exisiting workorder lines
if float_compare(qty_todo, 0.0, precision_rounding=rounding) < 0:
qty_todo = abs(qty_todo)
for workorder_line in move_workorder_lines.sorted(key=lambda wl: wl.qty_reserved):
# Try to decrease or remove lines that are not reserved and
# partialy reserved first. A different decrease strategy could
# be define in _unreserve_order method.
for workorder_line in move_workorder_lines.sorted(key=lambda wl: wl._unreserve_order()):
if float_compare(qty_todo, 0, precision_rounding=rounding) <= 0:
break
# If the quantity to consume on the line is lower than the
# quantity to remove, the line could be remove.
if float_compare(workorder_line.qty_to_consume, qty_todo, precision_rounding=rounding) <= 0:
# update qty_todo for next wo_line
qty_todo = float_round(qty_todo - workorder_line.qty_to_consume, precision_rounding=rounding)
if line_values['to_delete']:
line_values['to_delete'] |= workorder_line
else:
line_values['to_delete'] = workorder_line
# decrease the quantity on the line
else:
new_val = workorder_line.qty_to_consume - qty_todo
# avoid to write a negative reserved quantity
new_reserved = max(0, workorder_line.qty_reserved - qty_todo)
line_values['to_update'][workorder_line] = {
'qty_to_consume': new_val,
@@ -97,12 +103,17 @@ class MrpAbstractWorkorder(models.AbstractModel):
else:
# Search among wo lines which one could be updated
qty_reserved_wl = defaultdict(float)
# Try to update the line with the greater reservation first in
# order to promote bigger batch.
for workorder_line in move_workorder_lines.sorted(key=lambda wl: wl.qty_reserved, reverse=True):
rounding = workorder_line.product_uom_id.rounding
if float_compare(qty_todo, 0, precision_rounding=rounding) <= 0:
break
move_lines = workorder_line._get_move_lines()
qty_reserved_wl[workorder_line.lot_id] += workorder_line.qty_reserved
# The reserved quantity according to exisiting move line
# already produced (with qty_done set) and other production
# lines with the same lot that are currently on production.
qty_reserved_remaining = sum(move_lines.mapped('product_uom_qty')) - sum(move_lines.mapped('qty_done')) - qty_reserved_wl[workorder_line.lot_id]
if float_compare(qty_reserved_remaining, 0, precision_rounding=rounding) > 0:
qty_to_add = min(qty_reserved_remaining, qty_todo)
@@ -114,6 +125,13 @@ class MrpAbstractWorkorder(models.AbstractModel):
qty_todo -= qty_to_add
qty_reserved_wl[workorder_line.lot_id] += qty_to_add
# If a line exists without reservation and without lot. It
# means that previous operations could not find any reserved
# quantity and created a line without lot prefilled. In this
# case, the system will not find an existing move line with
# available reservation anymore and will increase this line
# instead of creating a new line without lot and reserved
# quantities.
if not workorder_line.qty_reserved and not workorder_line.lot_id and workorder_line.product_tracking != 'serial':
line_values['to_update'][workorder_line] = {
'qty_done': workorder_line.qty_to_consume + qty_todo,
@@ -130,9 +148,10 @@ class MrpAbstractWorkorder(models.AbstractModel):
@api.model
def _generate_lines_values(self, move, qty_to_consume):
""" Create workorder line. First generate line based on the reservation,
in order to match the reservation. If the quantity to consume is greater
than the reservation quantity then create line with the correct quantity
to consume but without lot or serial number.
in order to prefill reserved quantity, lot and serial number.
If the quantity to consume is greater than the reservation quantity then
create line with the correct quantity to consume but without lot or
serial number.
"""
lines = []
is_tracked = move.product_id.tracking != 'none'
@@ -227,8 +246,9 @@ class MrpAbstractWorkorder(models.AbstractModel):
)
def _update_raw_moves(self):
""" Once the production is done, the lots written on workorder lines
are saved on stock move lines"""
""" Once the production is done. Modify the workorder lines into
stock move line with the registered lot and quantity done.
"""
# Before writting produce quantities, we ensure they respect the bom strictness
self._strict_consumption_check()
vals_list = []
@@ -389,6 +409,11 @@ class MrpAbstractWorkorderLine(models.AbstractModel):
return vals_list
def _unreserve_order(self):
""" Unreserve line with lower reserved quantity first """
self.ensure_one()
return (self.qty_reserved,)
def _get_move_lines(self):
return self.move_id.move_line_ids.filtered(lambda ml:
ml.lot_id == self.lot_id and ml.product_id == self.product_id)
+2 -1
View File
@@ -592,6 +592,7 @@ class MrpProduction(models.Model):
def action_assign(self):
for production in self:
production.move_raw_ids._action_assign()
production.workorder_ids._refresh_wo_lines()
return True
@api.multi
@@ -729,7 +730,7 @@ class MrpProduction(models.Model):
moves_raw.mapped('move_line_ids').write({'workorder_id': workorder.id})
(moves_finished + moves_raw).write({'workorder_id': workorder.id})
workorder.generate_wo_lines()
workorder._generate_wo_lines()
return workorders
def _check_lots(self):
+27 -1
View File
@@ -220,7 +220,7 @@ class MrpWorkorder(models.Model):
raise UserError(_('The planned end date of the work order cannot be prior to the planned start date, please correct this to save the work order.'))
return super(MrpWorkorder, self).write(values)
def generate_wo_lines(self):
def _generate_wo_lines(self):
""" Generate workorder line """
self.ensure_one()
raw_moves = self.move_raw_ids.filtered(
@@ -247,6 +247,32 @@ class MrpWorkorder(models.Model):
for line, vals in line_values['to_update'].items():
line.write(vals)
def _refresh_wo_lines(self):
for workorder in self:
raw_moves = workorder.move_raw_ids.filtered(
lambda move: move.state not in ('done', 'cancel')
)
wl_to_unlink = self.env['mrp.workorder.line']
for move in raw_moves:
rounding = move.product_uom.rounding
qty_already_consumed = 0.0
workorder_lines = workorder.workorder_line_ids.filtered(lambda w: w.move_id == move)
for wl in workorder_lines:
if not wl.qty_done:
wl_to_unlink |= wl
continue
qty_already_consumed += wl.qty_done
qty_to_consume = move.product_uom._compute_quantity(
workorder.qty_producing * move.unit_factor,
move.product_id.uom_id,
round=False
)
wl_to_unlink.unlink()
if float_compare(qty_to_consume, qty_already_consumed, precision_rounding=rounding) > 0:
line_values = workorder._generate_lines_values(move, qty_to_consume - qty_already_consumed)
workorder.workorder_line_ids |= self.env['mrp.workorder.line'].create(line_values)
def _defaults_from_finished_workorder_line(self, reference_lot_lines):
for r_line in reference_lot_lines:
# see which lot we could suggest and its related qty_producing