[REF] stock: planned transfer: additional moves

Before this patch, move added in a planned transfer once it is ready are
directly marked as assigned and the reservation is disabled on them.

People found it hard to understand why the check availability button did
not appear, plus the push rules were not applied.

Now we chose to use the autoconfirm mechanism on the added move and we
don't reserve them, so the check availability button reappear.

task-2081844
This commit is contained in:
Simon Lejeune
2019-11-13 15:40:40 +00:00
parent 314e3eed7a
commit 4d653af6e9
3 changed files with 128 additions and 63 deletions
+7 -5
View File
@@ -399,8 +399,9 @@ class StockMove(models.Model):
@api.model
def default_get(self, fields_list):
# We override the default_get to make stock moves created after the picking was confirmed
# directly as available. This allows to create extra move lines in
# the fp view.
# directly as available in immediate transfer mode. This allows to create extra move lines
# in the fp view. In planned transfer, the stock move are marked as `additional` and will be
# auto-confirmed.
defaults = super(StockMove, self).default_get(fields_list)
if self.env.context.get('default_picking_id'):
picking_id = self.env['stock.picking'].browse(self.env.context['default_picking_id'])
@@ -408,10 +409,11 @@ class StockMove(models.Model):
defaults['state'] = 'done'
defaults['product_uom_qty'] = 0.0
defaults['additional'] = True
elif picking_id.state not in ['draft', 'confirmed']:
defaults['state'] = 'assigned'
elif picking_id.state not in ['cancel', 'draft', 'done']:
if picking_id.immediate_transfer:
defaults['state'] = 'assigned'
defaults['product_uom_qty'] = 0.0
defaults['additional'] = True
defaults['additional'] = True # to trigger `_autoconfirm_picking`
return defaults
def name_get(self):
+13 -14
View File
@@ -473,6 +473,7 @@ class Picking(models.Model):
for picking in self:
picking.has_packages = picking.move_line_ids.filtered(lambda ml: ml.result_package_id)
@api.depends('immediate_transfer', 'is_locked', 'state')
def _compute_show_check_availability(self):
""" According to `picking.show_check_availability`, the "check availability" button will be
displayed in the form view of a picking.
@@ -589,18 +590,7 @@ class Picking(models.Model):
if after_vals:
self.mapped('move_lines').filtered(lambda move: not move.scrapped).write(after_vals)
if vals.get('move_lines'):
# Do not run autoconfirm if any of the moves has an initial demand. If an initial demand
# is present in any of the moves, it means the picking was created through the "planned
# transfer" mechanism.
pickings_to_not_autoconfirm = self.env['stock.picking']
for picking in self:
if picking.state != 'draft':
continue
for move in picking.move_lines:
if not float_is_zero(move.product_uom_qty, precision_rounding=move.product_uom.rounding):
pickings_to_not_autoconfirm |= picking
break
(self - pickings_to_not_autoconfirm)._autoconfirm_picking()
self._autoconfirm_picking()
return res
def unlink(self):
@@ -906,8 +896,17 @@ class Picking(models.Model):
return immediate_pickings
def _autoconfirm_picking(self):
for picking in self.filtered(lambda picking: picking.immediate_transfer and picking.state not in ('done', 'cancel') and (picking.move_lines or picking.package_level_ids)):
picking.action_confirm()
""" Automatically run `action_confirm` on `self` if the picking is an immediate transfer or
if the picking is a planned transfer and one of its move was added after the initial
call to `action_confirm`. Note that `action_confirm` will only work on draft moves.
"""
for picking in self:
if picking.state in ('done', 'cancel'):
continue
if not picking.move_lines and not picking.package_level_ids:
continue
if picking.immediate_transfer or any(move.additional for move in picking.move_lines):
picking.action_confirm()
def _create_backorder(self):
""" This method is called when the user chose to create a backorder. It will create a new
+108 -44
View File
@@ -1348,50 +1348,6 @@ class TestSinglePicking(TestStockCommon):
self.assertEqual(move_lines[0].lot_id.id, serial1.id)
self.assertEqual(move_lines[1].lot_id.id, serial2.id)
def test_add_move_when_picking_is_available_1(self):
""" Check that any move added in a picking once it's assigned is directly considered as
assigned and bypass the reservation.
"""
delivery_order = self.env['stock.picking'].create({
'location_id': self.pack_location,
'location_dest_id': self.customer_location,
'picking_type_id': self.picking_type_out,
})
self.MoveObj.create({
'name': self.productA.name,
'product_id': self.productA.id,
'product_uom_qty': 2,
'product_uom': self.productA.uom_id.id,
'picking_id': delivery_order.id,
'location_id': self.pack_location,
'location_dest_id': self.customer_location,
})
# make some stock
pack_location = self.env['stock.location'].browse(self.pack_location)
self.env['stock.quant']._update_available_quantity(self.productA, pack_location, 2)
# assign
delivery_order.action_confirm()
delivery_order.action_assign()
self.assertEqual(delivery_order.state, 'assigned')
# add a move
move2 = self.MoveObj\
.with_context(default_picking_id=delivery_order.id)\
.create({
'name': self.productA.name,
'product_id': self.productB.id,
'product_uom_qty': 1,
'product_uom': self.productA.uom_id.id,
'picking_id': delivery_order.id,
'location_id': self.pack_location,
'location_dest_id': self.customer_location,
})
self.assertEqual(move2.state, 'assigned')
self.assertEqual(delivery_order.state, 'assigned')
def test_use_create_lot_use_existing_lot_1(self):
""" Check the behavior of a picking when `use_create_lot` and `use_existing_lot` are
set to False and there's a move for a tracked product.
@@ -1824,12 +1780,120 @@ class TestSinglePicking(TestStockCommon):
move.product_id = self.productA
move.product_uom_qty = 10
picking = picking.save()
self.assertEqual(picking.immediate_transfer, False)
self.assertEqual(picking.state, 'draft')
picking = Form(picking)
picking.move_ids_without_package.remove(0)
picking = picking.save()
self.assertEqual(len(picking.move_ids_without_package), 0)
def test_additional_move_1(self):
""" On a planned trasfer, add a stock move when the picking is already ready. Check that
the check availability button appears and work.
"""
# Make some stock for productA and productB.
receipt = self.env['stock.picking'].create({
'location_id': self.supplier_location,
'location_dest_id': self.stock_location,
'picking_type_id': self.picking_type_in,
})
move_1 = 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': receipt.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location,
})
move_2 = self.MoveObj.create({
'name': self.productB.name,
'product_id': self.productB.id,
'product_uom_qty': 10,
'product_uom': self.productB.uom_id.id,
'picking_id': receipt.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location,
})
receipt.action_confirm()
move_1.quantity_done = 10
move_2.quantity_done = 10
receipt.button_validate()
self.assertEqual(self.productA.qty_available, 10)
self.assertEqual(self.productB.qty_available, 10)
# Create a delivery for 1 productA, reserve, check the picking is ready
delivery_order = self.env['stock.picking'].create({
'location_id': self.stock_location,
'location_dest_id': self.customer_location,
'picking_type_id': self.picking_type_out,
'move_type': 'one',
})
move_3 = 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': delivery_order.id,
'location_id': self.stock_location,
'location_dest_id': self.customer_location,
})
delivery_order.action_confirm()
delivery_order.action_assign()
self.assertEqual(delivery_order.state, 'assigned')
# Add a unit of productB, the check_availability button should appear.
delivery_order = Form(delivery_order)
with delivery_order.move_ids_without_package.new() as move:
move.product_id = self.productB
move.product_uom_qty = 10
delivery_order = delivery_order.save()
# The autocoform ran, the picking shoud be confirmed and reservable.
self.assertEqual(delivery_order.state, 'confirmed')
self.assertEqual(delivery_order.show_mark_as_todo, False)
self.assertEqual(delivery_order.show_check_availability, True)
delivery_order.action_assign()
self.assertEqual(delivery_order.state, 'assigned')
self.assertEqual(delivery_order.show_check_availability, False)
self.assertEqual(delivery_order.show_mark_as_todo, False)
stock_location = self.env['stock.location'].browse(self.stock_location)
self.assertEqual(self.env['stock.quant']._gather(self.productA, stock_location).reserved_quantity, 10.0)
self.assertEqual(self.env['stock.quant']._gather(self.productB, stock_location).reserved_quantity, 10.0)
def test_additional_move_2(self):
""" On an immediate trasfer, add a stock move when the picking is already ready. Check that
the check availability button doest not appear.
"""
# Create a delivery for 1 productA, check the picking is ready
delivery_order = self.env['stock.picking'].create({
'location_id': self.stock_location,
'location_dest_id': self.customer_location,
'picking_type_id': self.picking_type_out,
'immediate_transfer': True,
'move_ids_without_package': [(0, 0, {
'name': self.productA.name,
'product_id': self.productA.id,
'product_uom': self.productA.uom_id.id,
'location_id': self.stock_location,
'location_dest_id': self.customer_location,
})],
})
self.assertEqual(delivery_order.state, 'assigned')
# Add a unit of productB, the check_availability button should not appear.
delivery_order = Form(delivery_order)
with delivery_order.move_ids_without_package.new() as move:
move.product_id = self.productB
delivery_order = delivery_order.save()
self.assertEqual(delivery_order.state, 'assigned')
self.assertEqual(delivery_order.show_check_availability, False)
self.assertEqual(delivery_order.show_mark_as_todo, False)
def test_owner_1(self):
"""Make a receipt, set an owner and validate"""
owner1 = self.env['res.partner'].create({'name': 'owner'})