[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:
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user