[IMP] stock, mrp: make _split() return move values

This commit change the _split method to return new move values instead
of create it and confirm it.

This allow multi creation and multi confirmation when every move of the
recordset are split

Task : 2076873

closes odoo/odoo#58594

Signed-off-by: William Henrotin <Whenrow@users.noreply.github.com>
This commit is contained in:
fw-bot
2020-09-28 15:04:19 +00:00
committed by William Henrotin
parent 605c899b04
commit cd288bd1d9
3 changed files with 33 additions and 29 deletions
+20 -6
View File
@@ -1367,16 +1367,19 @@ class MrpProduction(models.Model):
'production_id': backorder_mo.id,
})
else:
new_moves_vals = []
for move in production.move_raw_ids | production.move_finished_ids:
if not move.additional:
qty_to_split = move.product_uom_qty - move.unit_factor * production.qty_producing
new_move = self.env['stock.move'].browse(move._split(qty_to_split))
move_vals = move._split(qty_to_split)
if not move_vals:
continue
if move.raw_material_production_id:
new_move.raw_material_production_id = backorder_mo.id
move_vals[0]['raw_material_production_id'] = backorder_mo.id
else:
new_move.production_id = backorder_mo.id
(move | new_move)._do_unreserve()
(move | new_move)._action_assign()
move_vals[0]['production_id'] = backorder_mo.id
new_moves_vals.append(move_vals[0])
new_moves = self.env['stock.move'].create(new_moves_vals)
backorders |= backorder_mo
for wo in backorder_mo.workorder_ids:
wo.qty_produced = 0
@@ -1395,8 +1398,14 @@ class MrpProduction(models.Model):
workorder.duration_expected = workorder.duration_expected * ratio
for workorder in backorder_mo.workorder_ids:
workorder.duration_expected = workorder.duration_expected * (1 - ratio)
# As we have split the moves before validating them, we need to 'remove' the excess reservation
if not close_mo:
self.move_raw_ids.filtered(lambda m: not m.additional)._do_unreserve()
self.move_raw_ids.filtered(lambda m: not m.additional)._action_assign()
# Confirm only productions with remaining components
backorders.filtered(lambda mo: mo.move_raw_ids).action_confirm()
backorders.filtered(lambda mo: mo.move_raw_ids).action_assign()
# Remove the serial move line without reserved quantity. Post inventory will assigned all the non done moves
# So those move lines are duplicated.
@@ -1440,7 +1449,12 @@ class MrpProduction(models.Model):
})
for production in self:
production.write({'date_finished': fields.Datetime.now(), 'product_qty': production.qty_produced, 'priority': '0'})
production.write({
'date_finished': fields.Datetime.now(),
'product_qty': production.qty_produced,
'priority': '0',
'is_locked': True,
})
for workorder in self.workorder_ids.filtered(lambda w: w.state not in ('done', 'cancel')):
workorder.duration_expected = workorder._get_duration_expected()
-7
View File
@@ -109,13 +109,6 @@ class StockMove(models.Model):
order_finished_lot_ids = fields.Many2many('stock.production.lot', string="Finished Lot/Serial Number", compute='_compute_order_finished_lot_ids')
should_consume_qty = fields.Float('Quantity To Consume', compute='_compute_should_consume_qty', digits='Product Unit of Measure')
def _unreserve_initial_demand(self, new_move):
# If you were already putting stock.move.lots on the next one in the work order, transfer those to the new move
self.filtered(lambda m: m.production_id or m.raw_material_production_id)\
.mapped('move_line_ids')\
.filtered(lambda ml: ml.qty_done == 0.0)\
.write({'move_id': new_move, 'product_uom_qty': 0})
@api.depends('raw_material_production_id.priority')
def _compute_priority(self):
super()._compute_priority()
+13 -16
View File
@@ -1442,9 +1442,6 @@ class StockMove(models.Model):
break
return extra_move | self
def _unreserve_initial_demand(self, new_move):
pass
def _action_done(self, cancel_backorder=False):
self.filtered(lambda move: move.state == 'draft')._action_confirm() # MRP allows scrapping draft moves
moves = self.exists().filtered(lambda x: x.state not in ('done', 'cancel'))
@@ -1466,6 +1463,7 @@ class StockMove(models.Model):
moves_todo._check_company()
# Split moves where necessary and move quants
backorder_moves_vals = []
for move in moves_todo:
# To know whether we need to create a backorder or not, round to the general product's
# decimal precision and not the product's UOM.
@@ -1473,10 +1471,12 @@ class StockMove(models.Model):
if float_compare(move.quantity_done, move.product_uom_qty, precision_digits=rounding) < 0:
# Need to do some kind of conversion here
qty_split = move.product_uom._compute_quantity(move.product_uom_qty - move.quantity_done, move.product_id.uom_id, rounding_method='HALF-UP')
new_move = move._split(qty_split)
move._unreserve_initial_demand(new_move)
if cancel_backorder:
self.env['stock.move'].browse(new_move).with_context(moves_todo=moves_todo)._action_cancel()
new_move_vals = move._split(qty_split)
backorder_moves_vals += new_move_vals
backorder_moves = self.env['stock.move'].create(backorder_moves_vals)
backorder_moves._action_confirm(merge=False)
if cancel_backorder:
backorder_moves.with_context(moves_todo=moves_todo)._action_cancel()
moves_todo.mapped('move_line_ids').sorted()._action_done()
# Check the consistency of the result packages; there should be an unique location across
# the contained quants.
@@ -1524,13 +1524,12 @@ class StockMove(models.Model):
return vals
def _split(self, qty, restrict_partner_id=False):
""" Splits qty from move move into a new move
""" Splits `self` quantity and return values for a new moves to be created afterwards
:param qty: float. quantity to split (given in product UoM)
:param restrict_partner_id: optional partner that can be given in order to force the new move to restrict its choice of quants to the ones belonging to this partner.
:param context: dictionay. can contains the special key 'source_location_id' in order to force the source location when copying the move
:returns: id of the backorder move created """
self = self.with_prefetch() # This makes the ORM only look for one record and not 300 at a time, which improves performance
:returns: list of dict. stock move values """
self.ensure_one()
if self.state in ('done', 'cancel'):
raise UserError(_('You cannot split a stock move that has been set to \'Done\'.'))
elif self.state == 'draft':
@@ -1538,7 +1537,7 @@ class StockMove(models.Model):
# case of phantom bom (with mrp module). And we don't want to deal with this complexity by copying the product that will explode.
raise UserError(_('You cannot split a draft move. It needs to be confirmed first.'))
if float_is_zero(qty, precision_rounding=self.product_id.uom_id.rounding) or self.product_qty <= qty:
return self.id
return []
decimal_precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
@@ -1558,17 +1557,15 @@ class StockMove(models.Model):
# TDE CLEANME: remove context key + add as parameter
if self.env.context.get('source_location_id'):
defaults['location_id'] = self.env.context['source_location_id']
new_move = self.copy(defaults)
new_move_vals = self.copy_data(defaults)
# FIXME: pim fix your crap
# Update the original `product_qty` of the move. Use the general product's decimal
# precision and not the move's UOM to handle case where the `quantity_done` is not
# compatible with the move's UOM.
new_product_qty = self.product_id.uom_id._compute_quantity(self.product_qty - qty, self.product_uom, round=False)
new_product_qty = float_round(new_product_qty, precision_digits=self.env['decimal.precision'].precision_get('Product Unit of Measure'))
self.with_context(do_not_unreserve=True).write({'product_uom_qty': new_product_qty})
new_move = new_move._action_confirm(merge=False)
return new_move.id
return new_move_vals
def _recompute_state(self):
for move in self: