[FIX] stock: use existing lots when importing lots
Steps to reproduce: 1. Install Inventory app. 2. Enable "Storage Locations" in Inventory settings. 3. Create a new product known as "X". Set it's tracking to "By Unique Serial Number". 4. In Inventory -> Configuration -> Operation Types -> Receipts, set "Use Existing Ones" to true. 5. Create a new Receipt, add a new line of product X. 6. In the "Details" page of the X line, click "Import Serials/Lots". 7. Enter at least two lines of two serial numbers and import. Bug: Notice that the "Lot/Serial Number" column on each row are blank. This is happening because the view used for stock move lines here will display either the lot_id field of a sml, or the lot_name field, based on varying conditions. In this case, setting "Use Existing Ones" to true will cause the lot_id field to be displayed (and the lot_name field to be invisible). When importing lots, only the lot_name is set with the user's input but the lot_id is left blank. So because lot_id is empty, the column is empty. But this works as expected when "Use Existing Ones" is false because it displays the correctly set lot_name. When the receipt is validated, the lot is created and lot_id for each of the smls is set, so now it displays properly. Fix: when "Use Existing Ones" is set first check if lot exists and set it in that case, otherwise create and set a new lot at that moment opw-3498812 closes odoo/odoo#140400 X-original-commit: c5a35a1c2a7e16adbd3f2b1e37509003bcfac957 Signed-off-by: William Henrotin (whe) <whe@odoo.com> Signed-off-by: Walid Hanniche (waha) <waha@odoo.com>
This commit is contained in:
@@ -10,12 +10,6 @@ from odoo.tools.misc import get_lang
|
||||
|
||||
class TestStockLot(StockGenerateCommon):
|
||||
|
||||
def _import_lots(self, lots, move):
|
||||
location_id = move.location_id
|
||||
move_lines_vals = move.split_lots(lots)
|
||||
move_lines_commands = move._generate_serial_move_line_commands(move_lines_vals, location_dest_id=location_id)
|
||||
move.update({'move_line_ids': move_lines_commands})
|
||||
|
||||
def test_set_multiple_lot_name_with_expiration_date_01(self):
|
||||
""" In a move line's `lot_name` field, pastes a list of lots and expiration dates.
|
||||
Checks the values are correctly interpreted and the expiration dates are correctly created
|
||||
|
||||
@@ -850,16 +850,31 @@ Please change the quantity done or the rounding precision of your unit of measur
|
||||
options = options or self._get_formating_options(lot_text_parts[1:])
|
||||
for extra_string in lot_text_parts[1:]:
|
||||
field_data = self._convert_string_into_field_data(extra_string, options)
|
||||
if field_data == "ignore":
|
||||
# Got an unusable data for this move, updates only the lot_name part.
|
||||
move_line_vals.update(lot_name=lot_text_parts[0])
|
||||
elif field_data:
|
||||
move_line_vals.update(**field_data, lot_name=lot_text_parts[0])
|
||||
if field_data:
|
||||
lot_text = lot_text_parts[0]
|
||||
if field_data == "ignore":
|
||||
# Got an unusable data for this move, updates only the lot_name part.
|
||||
move_line_vals.update(lot_name=lot_text)
|
||||
else:
|
||||
move_line_vals.update(**field_data, lot_name=lot_text)
|
||||
else:
|
||||
# At least this part of the string is erronous and can't be converted,
|
||||
# don't try to guess and simply use the full string as the lot name.
|
||||
move_line_vals['lot_name'] = lot_text
|
||||
break
|
||||
if self.picking_type_id.use_existing_lots:
|
||||
lot_id = self.env['stock.lot'].search([
|
||||
('product_id', '=', self.product_id.id),
|
||||
('name', '=', lot_text),
|
||||
('company_id', '=', self.company_id.id),
|
||||
])
|
||||
if not lot_id:
|
||||
lot_id = self.env['stock.lot'].create({
|
||||
'product_id': self.product_id.id,
|
||||
'name': lot_text,
|
||||
'company_id': self.company_id.id,
|
||||
})
|
||||
move_line_vals['lot_id'] = lot_id.id
|
||||
move_lines_vals.append(move_line_vals)
|
||||
return move_lines_vals
|
||||
|
||||
|
||||
@@ -35,6 +35,13 @@ class StockGenerateCommon(TransactionCase):
|
||||
|
||||
cls.Wizard = cls.env['stock.assign.serial']
|
||||
|
||||
|
||||
def _import_lots(self, lots, move):
|
||||
location_id = move.location_id
|
||||
move_lines_vals = move.split_lots(lots)
|
||||
move_lines_commands = move._generate_serial_move_line_commands(move_lines_vals, location_dest_id=location_id)
|
||||
move.update({'move_line_ids': move_lines_commands})
|
||||
|
||||
def get_new_move(self, nbre_of_lines=0, product=False):
|
||||
product = product or self.product_serial
|
||||
move_lines_vals = [Command.create({
|
||||
@@ -369,3 +376,36 @@ class StockGenerateCommon(TransactionCase):
|
||||
{'quantity': 1, 'lot_name': '003', 'location_dest_id': sub_loc_03.id},
|
||||
{'quantity': 1, 'lot_name': '004', 'location_dest_id': sub_loc_04.id},
|
||||
])
|
||||
|
||||
def test_import_lots(self):
|
||||
product_lot = self.env['product.product'].create({
|
||||
'name': 'Tracked by Lots',
|
||||
'type': 'product',
|
||||
'tracking': 'lot',
|
||||
})
|
||||
lot_id = self.env['stock.lot'].create({
|
||||
'product_id': product_lot.id,
|
||||
'name': 'abc',
|
||||
})
|
||||
self.warehouse.in_type_id.use_existing_lots = True
|
||||
receipt_picking = self.env['stock.picking'].create({
|
||||
'picking_type_id': self.warehouse.in_type_id.id,
|
||||
'location_id': self.env.ref('stock.stock_location_suppliers').id,
|
||||
'location_dest_id': self.warehouse.lot_stock_id.id,
|
||||
'state': 'draft',
|
||||
})
|
||||
move = self.env['stock.move'].create({
|
||||
'name': product_lot.name,
|
||||
'product_id': product_lot.id,
|
||||
'product_uom': product_lot.uom_id.id,
|
||||
'product_uom_qty': 5.0,
|
||||
'picking_id': receipt_picking.id,
|
||||
'location_id': receipt_picking.location_id.id,
|
||||
'location_dest_id': receipt_picking.location_dest_id.id,
|
||||
})
|
||||
self._import_lots("abc;4\ndef", move)
|
||||
self.assertIn(lot_id, move.move_line_ids.lot_id)
|
||||
self.assertRecordValues(move.move_line_ids, [
|
||||
{'quantity': 4, 'lot_name': 'abc'},
|
||||
{'quantity': 1, 'lot_name': 'def'},
|
||||
])
|
||||
|
||||
Reference in New Issue
Block a user