diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index f3d0ba9ffce..24fc4d79e5c 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -182,15 +182,14 @@ class StockMove(models.Model):
if self.product_id:
self.description_picking = self.product_id._get_description(self.picking_type_id)
- @api.depends('has_tracking', 'picking_type_id.use_create_lots', 'picking_type_id.use_existing_lots', 'picking_type_id.show_reserved', 'picking_type_id.show_operations')
+ @api.depends('has_tracking', 'picking_type_id.use_create_lots', 'picking_type_id.use_existing_lots', 'state')
def _compute_display_assign_serial(self):
for move in self:
move.display_assign_serial = (
move.has_tracking == 'serial' and
move.state in ('partially_available', 'assigned', 'confirmed') and
move.picking_type_id.use_create_lots and
- not move.picking_type_id.use_existing_lots and
- not move.picking_type_id.show_reserved
+ not move.picking_type_id.use_existing_lots
)
@api.depends('picking_id.is_locked')
@@ -641,23 +640,14 @@ class StockMove(models.Model):
suffix = splitted[1]
initial_number = int(initial_number)
- move_lines_commands = []
- location_dest = self.location_dest_id._get_putaway_strategy(self.product_id) or self.location_dest_id
+ lot_names = []
for i in range(0, next_serial_count):
- lot_name = '%s%s%s' % (
+ lot_names.append('%s%s%s' % (
prefix,
str(initial_number + i).zfill(padding),
suffix
- )
- move_lines_commands.append((0, 0, {
- 'lot_name': lot_name,
- 'qty_done': 1,
- 'product_id': self.product_id.id,
- 'product_uom_id': self.product_id.uom_id.id,
- 'location_id': self.location_id.id,
- 'location_dest_id': location_dest.id,
- 'picking_id': self.picking_id.id,
- }))
+ ))
+ move_lines_commands = self._generate_serial_move_line_commands(lot_names)
self.write({'move_line_ids': move_lines_commands})
return True
@@ -816,31 +806,34 @@ class StockMove(models.Model):
self.name = product.partner_ref
self.product_uom = product.uom_id.id
- @api.onchange('move_line_nosuggest_ids')
- def onchange_move_line_nosuggest_ids(self):
+ @api.onchange('move_line_ids', 'move_line_nosuggest_ids')
+ def onchange_move_line_ids(self):
+ if not self.picking_type_id.use_create_lots:
+ # This onchange manages the creation of multiple lot name. We don't
+ # need that if the picking type disallows the creation of new lots.
+ return
+
breaking_char = '\n'
- move_lines_to_create = []
- for move_line in self.move_line_nosuggest_ids:
+ if self.picking_type_id.show_reserved:
+ move_lines = self.move_line_ids
+ else:
+ move_lines = self.move_line_nosuggest_ids
+
+ for move_line in move_lines:
# Look if the `lot_name` contains multiple values.
if breaking_char in (move_line.lot_name or ''):
- splitted_lines = move_line.lot_name.split(breaking_char)
- splitted_lines = list(filter(lambda line: line, splitted_lines))
- move_line.lot_name = splitted_lines[0]
- # For each SN line, set move ine data...
- for line in splitted_lines[1:]:
- move_line_data = {
- 'lot_name': line,
- 'qty_done': 1,
- 'product_id': move_line.product_id.id,
- 'product_uom_id': move_line.product_id.uom_id.id,
- 'location_id': move_line.location_id.id,
- 'location_dest_id': move_line.location_dest_id.id,
- 'owner_id': move_line.owner_id or False,
- 'package_id': move_line.package_id or False,
- }
- move_lines_to_create += [(0, 0, move_line_data)]
- # ... then create these move lines.
- self.update({'move_line_nosuggest_ids': move_lines_to_create})
+ split_lines = move_line.lot_name.split(breaking_char)
+ split_lines = list(filter(None, split_lines))
+ move_line.lot_name = split_lines[0]
+ move_lines_commands = self._generate_serial_move_line_commands(
+ split_lines[1:],
+ origin_move_line=move_line,
+ )
+ if self.picking_type_id.show_reserved:
+ self.update({'move_line_ids': move_lines_commands})
+ else:
+ self.update({'move_line_nosuggest_ids': move_lines_commands})
+ break
@api.onchange('product_uom')
def onchange_product_uom(self):
@@ -904,6 +897,64 @@ class StockMove(models.Model):
def _assign_picking_post_process(self, new=False):
pass
+ def _generate_serial_move_line_commands(self, lot_names, origin_move_line=None):
+ """Return a list of commands to update the move lines (write on
+ existing ones or create new ones).
+ Called when user want to create and assign multiple serial numbers in
+ one time (using the button/wizard or copy-paste a list in the field).
+
+ :param lot_names: A list containing all serial number to assign.
+ :type lot_names: list
+ :param origin_move_line: A move line to duplicate the value from, default to None
+ :type origin_move_line: record of :class:`stock.move.line`
+ :return: A list of commands to create/update :class:`stock.move.line`
+ :rtype: list
+ """
+ self.ensure_one()
+
+ # Select the right move lines depending of the picking type configuration.
+ move_lines = self.env['stock.move.line']
+ if self.picking_type_id.show_reserved:
+ move_lines = self.move_line_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_name)
+ else:
+ move_lines = self.move_line_nosuggest_ids.filtered(lambda ml: not ml.lot_id and not ml.lot_name)
+
+ if origin_move_line:
+ location_dest = origin_move_line.location_dest_id
+ else:
+ location_dest = self.location_dest_id._get_putaway_strategy(self.product_id)
+ move_line_vals = {
+ 'location_dest_id': location_dest.id or self.location_dest_id.id,
+ 'location_id': self.location_id.id,
+ 'product_id': self.product_id.id,
+ 'product_uom_id': self.product_id.uom_id.id,
+ 'qty_done': 1,
+ }
+ if origin_move_line:
+ # `owner_id` and `package_id` are taken only in the case we create
+ # new move lines from an existing move line. Also, updates the
+ # `qty_done` because it could be usefull for products tracked by lot.
+ move_line_vals.update({
+ 'owner_id': origin_move_line.owner_id.id,
+ 'package_id': origin_move_line.package_id.id,
+ 'qty_done': origin_move_line.qty_done or 1,
+ })
+
+ move_lines_commands = []
+ for lot_name in lot_names:
+ # We write the lot name on an existing move line (if we have still one)...
+ if move_lines:
+ move_lines_commands.append((1, move_lines[0].id, {
+ 'lot_name': lot_name,
+ 'qty_done': 1,
+ }))
+ move_lines = move_lines[1:]
+ # ... or create a new move line with the serial name.
+ else:
+ move_line_cmd = dict(move_line_vals, lot_name=lot_name)
+ move_lines_commands.append((0, 0, move_line_cmd))
+ return move_lines_commands
+
def _get_new_picking_values(self):
""" return create values for new picking that will be linked with group
of moves in self.
diff --git a/addons/stock/tests/test_generate_serial_numbers.py b/addons/stock/tests/test_generate_serial_numbers.py
index ab534983696..1c3e79454f1 100644
--- a/addons/stock/tests/test_generate_serial_numbers.py
+++ b/addons/stock/tests/test_generate_serial_numbers.py
@@ -17,7 +17,7 @@ class StockGenerate(SavepointCase):
})
cls.uom_unit = cls.env.ref('uom.product_uom_unit')
- warehouse = cls.env['stock.warehouse'].create({
+ cls.warehouse = cls.env['stock.warehouse'].create({
'name': 'Base Warehouse',
'reception_steps': 'one_step',
'delivery_steps': 'ship_only',
@@ -25,11 +25,11 @@ class StockGenerate(SavepointCase):
})
cls.location = cls.env['stock.location'].create({
'name': 'Room A',
- 'location_id': warehouse.lot_stock_id.id,
+ 'location_id': cls.warehouse.lot_stock_id.id,
})
cls.location_dest = cls.env['stock.location'].create({
'name': 'Room B',
- 'location_id': warehouse.lot_stock_id.id,
+ 'location_id': cls.warehouse.lot_stock_id.id,
})
cls.Wizard = cls.env['stock.assign.serial']
@@ -275,7 +275,12 @@ class StockGenerate(SavepointCase):
has five new move lines with the right `lot_name`.
"""
nbre_of_lines = 10
+ picking_type = self.env['stock.picking.type'].search([
+ ('use_create_lots', '=', True),
+ ('warehouse_id', '=', self.warehouse.id)
+ ])
move = self.get_new_move(nbre_of_lines)
+ move.picking_type_id = picking_type
# We must begin with a move with 10 move lines.
self.assertEqual(len(move.move_line_ids), nbre_of_lines)
@@ -307,7 +312,12 @@ class StockGenerate(SavepointCase):
been correctly set.
"""
nbre_of_lines = 5
+ picking_type = self.env['stock.picking.type'].search([
+ ('use_create_lots', '=', True),
+ ('warehouse_id', '=', self.warehouse.id)
+ ])
move = self.get_new_move(nbre_of_lines)
+ move.picking_type_id = picking_type
# We must begin with a move with five move lines.
self.assertEqual(len(move.move_line_ids), nbre_of_lines)
diff --git a/addons/stock/views/stock_move_views.xml b/addons/stock/views/stock_move_views.xml
index 97bb1e2d8fe..4aa69439b00 100644
--- a/addons/stock/views/stock_move_views.xml
+++ b/addons/stock/views/stock_move_views.xml
@@ -228,7 +228,7 @@
-
+
@@ -257,7 +257,10 @@
'default_product_id': parent.product_id,
}"
/>
-
+
@@ -300,20 +303,6 @@
-
- stock.move.line.operations.tree
- stock.move.line
- 1000
- primary
-
-
-
- text
- Write your SN/LN one by one or copy paste a list.
-
-
-
-
stock.move.form
stock.move