[IMP] stock: backorders created only when needed
Description of the issue/feature this PR addresses: Creation of backorders only when needed Current behavior before PR: - When the option "No Backorder" was selected, a backorder was created then cancelled. Desired behavior after PR is merged: - When the option "No Backorder" was selected, no backorders are created. ( Task ID : 46981 ) -- I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr closes odoo/odoo#27624
This commit is contained in:
@@ -82,8 +82,8 @@ class StockMove(models.Model):
|
||||
super(StockMove, self)._clean_merged()
|
||||
self.write({'created_purchase_line_id': False})
|
||||
|
||||
def _action_done(self):
|
||||
res = super(StockMove, self)._action_done()
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
res = super(StockMove, self)._action_done(cancel_backorder=cancel_backorder)
|
||||
self.mapped('purchase_line_id').sudo()._update_received_qty()
|
||||
return res
|
||||
|
||||
|
||||
@@ -180,7 +180,7 @@ class Inventory(models.Model):
|
||||
else:
|
||||
self._action_done()
|
||||
|
||||
def _action_done(self):
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
negative = next((line for line in self.mapped('line_ids') if line.product_qty < 0 and line.product_qty != line.theoretical_qty), False)
|
||||
if negative:
|
||||
raise UserError(_('You cannot set a negative product quantity in an inventory line:\n\t%s - qty: %s') % (negative.product_id.name, negative.product_qty))
|
||||
|
||||
@@ -1084,7 +1084,7 @@ class StockMove(models.Model):
|
||||
def _unreserve_initial_demand(self, new_move):
|
||||
pass
|
||||
|
||||
def _action_done(self):
|
||||
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'))
|
||||
moves_todo = self.env['stock.move']
|
||||
@@ -1124,6 +1124,8 @@ class StockMove(models.Model):
|
||||
except UserError:
|
||||
pass
|
||||
move._unreserve_initial_demand(new_move)
|
||||
if cancel_backorder:
|
||||
self.env['stock.move'].browse(new_move)._action_cancel()
|
||||
moves_todo.mapped('move_line_ids')._action_done()
|
||||
# Check the consistency of the result packages; there should be an unique location across
|
||||
# the contained quants.
|
||||
@@ -1140,7 +1142,7 @@ class StockMove(models.Model):
|
||||
if all(move_todo.scrapped for move_todo in moves_todo):
|
||||
return moves_todo
|
||||
|
||||
if picking:
|
||||
if picking and not cancel_backorder:
|
||||
picking._create_backorder()
|
||||
return moves_todo
|
||||
|
||||
|
||||
@@ -351,7 +351,7 @@ class StockMoveLine(models.Model):
|
||||
moves._recompute_state()
|
||||
return res
|
||||
|
||||
def _action_done(self):
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
""" This method is called during a move's `action_done`. It'll actually move a quant from
|
||||
the source location to the destination location, and unreserve if needed in the source
|
||||
location.
|
||||
|
||||
@@ -622,7 +622,7 @@ class Picking(models.Model):
|
||||
new_move._action_confirm()
|
||||
todo_moves |= new_move
|
||||
#'qty_done': ops.qty_done})
|
||||
todo_moves._action_done()
|
||||
todo_moves._action_done(cancel_backorder=self.env.context.get('cancel_backorder'))
|
||||
self.write({'date_done': fields.Datetime.now()})
|
||||
return True
|
||||
|
||||
@@ -806,10 +806,11 @@ class Picking(models.Model):
|
||||
precision_rounding=move.product_uom.rounding) == 1
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def _create_backorder(self, backorder_moves=[]):
|
||||
""" Move all non-done lines into a new backorder picking.
|
||||
"""
|
||||
def _create_backorder(self):
|
||||
|
||||
""" This method is called when the user chose to create a backorder. It will create a new
|
||||
picking, the backorder, and move the stock.moves that are not `done` or `cancel` into it.
|
||||
"""
|
||||
backorders = self.env['stock.picking']
|
||||
for picking in self:
|
||||
moves_to_backorder = picking.move_lines.filtered(lambda x: x.state not in ('done', 'cancel'))
|
||||
|
||||
@@ -1791,3 +1791,55 @@ class TestStockFlow(TestStockCommon):
|
||||
self.assertEquals(move_mto_alone.state, "draft")
|
||||
self.assertEquals(move_with_ancestors.state, "waiting")
|
||||
self.assertEquals(other_move.state, "confirmed")
|
||||
|
||||
def test_80_partial_picking_without_backorder(self):
|
||||
""" This test will create a picking with an initial demand for a product
|
||||
then process a lesser quantity than the expected quantity to be processed.
|
||||
When the wizard ask for a backorder, the 'NO BACKORDER' option will be selected
|
||||
and no backorder should be created afterwards
|
||||
"""
|
||||
|
||||
picking = self.PickingObj.create({
|
||||
'partner_id': self.partner_delta_id,
|
||||
'picking_type_id': self.picking_type_in,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location})
|
||||
move_a = self.MoveObj.create({
|
||||
'name': self.productA.name,
|
||||
'product_id': self.productA.id,
|
||||
'product_uom_qty': 10,
|
||||
'product_uom': self.productA.uom_id.id,
|
||||
'picking_id': picking.id,
|
||||
'location_id': self.supplier_location,
|
||||
'location_dest_id': self.stock_location})
|
||||
|
||||
picking.action_confirm()
|
||||
|
||||
# Only 4 items are processed
|
||||
move_a.move_line_ids.qty_done = 4
|
||||
backorder_wizard = self.env['stock.backorder.confirmation'].create({'pick_ids': [(4, picking.id)]})
|
||||
backorder_wizard.process_cancel_backorder()
|
||||
|
||||
# Checking that no backorders were attached to the picking
|
||||
self.assertFalse(picking.backorder_id)
|
||||
|
||||
# Checking that the original move is still in the same picking
|
||||
self.assertEquals(move_a.picking_id.id, picking.id)
|
||||
|
||||
move_lines = picking.move_lines
|
||||
move_done = move_lines.browse(move_a.id)
|
||||
move_canceled = move_lines - move_done
|
||||
|
||||
# Checking that the original move was set to done
|
||||
self.assertEquals(move_done.product_uom_qty, 4)
|
||||
self.assertEquals(move_done.state, 'done')
|
||||
|
||||
# Checking that the new move created was canceled
|
||||
self.assertEquals(move_canceled.product_uom_qty, 6)
|
||||
self.assertEquals(move_canceled.state, 'cancel')
|
||||
|
||||
# Checking that the canceled move is in the original picking
|
||||
self.assertIn(move_canceled.id, picking.move_lines.mapped('id'))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,12 +20,7 @@ class StockBackorderConfirmation(models.TransientModel):
|
||||
if float_compare(move.product_uom_qty, move.quantity_done, move.product_uom.rounding) > 0:
|
||||
moves_to_log[move] = (move.quantity_done, move.product_uom_qty)
|
||||
pick_id._log_less_quantities_than_expected(moves_to_log)
|
||||
self.pick_ids.action_done()
|
||||
if cancel_backorder:
|
||||
for pick_id in self.pick_ids:
|
||||
backorder_pick = self.env['stock.picking'].search([('backorder_id', '=', pick_id.id)])
|
||||
backorder_pick.action_cancel()
|
||||
pick_id.message_post(body=_("Back order <em>%s</em> <b>cancelled</b>.") % (backorder_pick.name))
|
||||
self.pick_ids.with_context({'cancel_backorder': cancel_backorder}).action_done()
|
||||
|
||||
def process(self):
|
||||
self._process()
|
||||
|
||||
@@ -348,9 +348,9 @@ class StockMove(models.Model):
|
||||
'price_unit': price_unit if self._is_dropshipped() else -price_unit,
|
||||
})
|
||||
|
||||
def _action_done(self):
|
||||
def _action_done(self, cancel_backorder=False):
|
||||
self.product_price_update_before_done()
|
||||
res = super(StockMove, self)._action_done()
|
||||
res = super(StockMove, self)._action_done(cancel_backorder=cancel_backorder)
|
||||
for move in res:
|
||||
# Apply restrictions on the stock move to be able to make
|
||||
# consistent accounting entries.
|
||||
|
||||
Reference in New Issue
Block a user