[IMP] stock: add ask-always-never setting for backorder

In picking type, we introduce a new setting ask-always-never setting for
creating backorder. When a backorder is needed, the behaviour will be
different according to this setting:
 - ask: popup a wizard to ask if a backorder is needed
 - always: generate backorder automatically
 - never: no backorder

The main reasoning behind this change is that there are some scenarios
in which operators always create backorders (e.g. by validating the
transfer after scanning a pallet and setting quantities). Hence, always
asking if the user (operator) wants to create a backorder can be confusing.

task-2648993

Part-of: odoo/odoo#83462
This commit is contained in:
Jérôme Hellinckx
2022-06-23 11:15:47 +02:00
committed by yhu-odoo
parent c7c96d61e9
commit 047bfa7536
3 changed files with 81 additions and 6 deletions
+12 -5
View File
@@ -88,6 +88,13 @@ class PickingType(models.Model):
company_id = fields.Many2one(
'res.company', 'Company', required=True,
default=lambda s: s.env.company.id, index=True)
create_backorder = fields.Selection(
[('ask', 'Ask'), ('always', 'Always'), ('never', 'Never')],
'Create Backorder', required=True, default='ask',
help="When validating a transfer:\n"
" * Ask: users are asked to choose if they want to make a backorder for remaining products\n"
" * Always: a backorder is automatically created for the remaining products\n"
" * Never: remaining products are cancelled")
@api.model_create_multi
def create(self, vals_list):
@@ -1010,12 +1017,10 @@ class Picking(models.Model):
return res
# Call `_action_done`.
pickings_not_to_backorder = self.filtered(lambda p: p.picking_type_id.create_backorder == 'never')
if self.env.context.get('picking_ids_not_to_backorder'):
pickings_not_to_backorder = self.browse(self.env.context['picking_ids_not_to_backorder'])
pickings_to_backorder = self - pickings_not_to_backorder
else:
pickings_not_to_backorder = self.env['stock.picking']
pickings_to_backorder = self
pickings_not_to_backorder |= self.browse(self.env.context['picking_ids_not_to_backorder'])
pickings_to_backorder = self - pickings_not_to_backorder
pickings_not_to_backorder.with_context(cancel_backorder=True)._action_done()
pickings_to_backorder.with_context(cancel_backorder=False)._action_done()
@@ -1106,6 +1111,8 @@ class Picking(models.Model):
prec = self.env["decimal.precision"].precision_get("Product Unit of Measure")
backorder_pickings = self.browse()
for picking in self:
if picking.picking_type_id.create_backorder != 'ask':
continue
quantity_todo = {}
quantity_done = {}
for move in picking.move_ids.filtered(lambda m: m.state != "cancel"):
+68 -1
View File
@@ -1670,7 +1670,7 @@ class TestStockFlow(TestStockCommon):
backorder_wizard.process_cancel_backorder()
# Checking that no backorders were attached to the picking
self.assertFalse(picking.backorder_id)
self.assertFalse(picking.backorder_ids)
# Checking that the original move is still in the same picking
self.assertEqual(move_a.picking_id.id, picking.id)
@@ -1690,6 +1690,73 @@ class TestStockFlow(TestStockCommon):
# Checking that the canceled move is in the original picking
self.assertIn(move_canceled.id, picking.move_ids.mapped('id'))
def test_backorder_setting(self):
"""Create 3 pickings with each has different backorder settings on its
picking type. Check the backorder behavior for each picking.
"""
picking_type_ask = self.env['stock.picking.type'].browse(self.picking_type_in)
picking_type_ask.create_backorder = 'ask'
picking_type_always = picking_type_ask.copy({
'sequence_code': 'always',
'create_backorder': 'always',
})
picking_type_never = picking_type_ask.copy({
'sequence_code': 'never',
'create_backorder': 'never',
})
picking_ask = self.PickingObj.create({
'picking_type_id': picking_type_ask.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location})
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_ask.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location})
picking_always = self.PickingObj.create({
'picking_type_id': picking_type_always.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location})
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_always.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location})
picking_never = self.PickingObj.create({
'picking_type_id': picking_type_never.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location})
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_never.id,
'location_id': self.supplier_location,
'location_dest_id': self.stock_location})
pickings = picking_ask | picking_always | picking_never
pickings.action_confirm()
# Only 4 items are processed in each picking
pickings.move_ids.move_line_ids.qty_done = 4
res_dict = pickings.button_validate()
backorder_wizard = Form(self.env['stock.backorder.confirmation'].with_context(res_dict['context'])).save()
self.assertEqual(backorder_wizard.pick_ids, picking_ask, "Only ask backorder for picking with setting 'set'")
backorder_wizard.process_cancel_backorder()
self.assertEqual(pickings.mapped('state'), ['done', 'done', 'done'])
self.assertFalse(picking_ask.backorder_ids)
self.assertTrue(picking_always.backorder_ids, "Picking with setting 'always' should have backorder created")
self.assertFalse(picking_never.backorder_ids, "Picking with setting 'never' should have no backorder created")
def test_transit_multi_companies(self):
""" Ensure that inter company rules set the correct company on picking
and their moves.
@@ -56,6 +56,7 @@
<field name="code"/>
<field name="company_id" groups="base.group_multi_company" options="{'no_create': True}"/>
<field attrs='{"invisible": [("code", "not in", ["incoming", "outgoing", "internal"])]}' name="return_picking_type_id"/>
<field name="create_backorder" />
<field name="show_operations"/>
<field name="show_reserved" attrs="{'invisible': [('code', '!=', 'incoming')]}"/>
</group>