# Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import Command from odoo.tests import Form, HttpCase, tagged @tagged('-at_install', 'post_install') class TestStockPickingTour(HttpCase): def setUp(self): self.receipt = self.env['stock.picking'].create({ 'picking_type_id': self.env.ref('stock.picking_type_in').id, 'location_id': self.env.ref('stock.stock_location_suppliers').id, 'location_dest_id': self.env.ref('stock.stock_location_stock').id, }) return super().setUp() def _get_picking_url(self, picking_id): return '/odoo/action-stock.action_picking_tree_incoming/%s' % (picking_id) def test_detailed_op_no_save_1(self): """validate a receipt with some move without any save except the last one""" product_lot = self.env['product.product'].create({ 'name': 'Product Lot', 'is_storable': True, 'tracking': 'lot', }) url = self._get_picking_url(self.receipt.id) self.start_tour(url, 'test_detailed_op_no_save_1', login='admin', timeout=60) self.assertEqual(self.receipt.state, 'done') self.assertEqual(self.receipt.move_ids.product_uom_qty, 0) self.assertEqual(self.receipt.move_ids.quantity, 4) lot = self.env['stock.lot'].search([ ('name', '=', 'lot1'), ('product_id', '=', product_lot.id), ]) self.assertEqual(len(lot), 1) self.assertEqual(lot.product_qty, 4) def test_generate_serial_1(self): """generate some serial numbers in the detailed operation modal""" product_serial = self.env['product.product'].create({ 'name': 'Product Serial', 'is_storable': True, 'tracking': 'serial', }) url = self._get_picking_url(self.receipt.id) self.start_tour(url, 'test_generate_serial_1', login='admin', timeout=60) self.assertEqual(self.receipt.state, 'done') self.assertEqual(self.receipt.move_ids.product_uom_qty, 0) self.assertEqual(self.receipt.move_ids.quantity, 5) self.assertEqual(len(self.receipt.move_ids.move_line_ids), 5) serial = self.env['stock.lot'].search([ ('name', 'ilike', 'serial_n_%'), ('product_id', '=', product_serial.id), ]) self.assertEqual(len(serial), 5) def test_generate_serial_2(self): """ Generate lot numbers in the detailed operation modal """ product_lot_1 = self.env['product.product'].create({ 'name': 'Product Lot 1', 'is_storable': True, 'tracking': 'lot', }) url = self._get_picking_url(self.receipt.id) self.start_tour(url, 'test_generate_serial_2', login='admin', timeout=60) self.assertEqual(self.receipt.state, 'done') self.assertEqual(self.receipt.move_ids.product_uom_qty, 100) self.assertEqual(self.receipt.move_ids.quantity, 100) self.assertEqual(len(self.receipt.move_ids.move_line_ids), 11) lots_batch_1 = self.env['stock.lot'].search([ ('name', 'ilike', 'lot_n_1_%'), ('product_id', '=', product_lot_1.id) ], order='name asc') lots_batch_2 = self.env['stock.lot'].search([ ('name', 'ilike', 'lot_n_2_%'), ('product_id', '=', product_lot_1.id) ], order='name asc') self.assertEqual(len(lots_batch_1), 7) self.assertEqual(lots_batch_1[-1].product_qty, 5) self.assertEqual(len(lots_batch_2), 4) self.assertEqual(lots_batch_2[-1].product_qty, 11) def test_inventory_adjustment_apply_all(self): """ Checks if the "Apply All" button works for all new entries, even if it was pressed immediately after entering the last entry's quantity (without clicking on anything else) """ self.env['product.product'].create([ {'name': 'Product 1', 'is_storable': True}, {'name': 'Product 2', 'is_storable': True}, ]) menu = self.env.ref('stock.menu_action_inventory_tree') url = '/odoo/stock.quant?menu_id=%s' % (menu['id']) # We need a bigger window, so the "Apply All" button is immediately visible self.browser_size = '1920,1080' self.start_tour(url, 'test_inventory_adjustment_apply_all', login='admin', timeout=60) def test_add_new_line(self): product_one, _ = self.env["product.product"].create([ { 'name': 'Product one', 'is_storable': True, 'tracking': 'serial', }, { 'name': 'Product two', 'is_storable': True, 'tracking': 'serial', } ]) self.receipt.write({ "move_ids": [Command.create({ "name": "test", "product_uom_qty": 1, "product_id": product_one.id, "location_id": self.receipt.location_id.id, "location_dest_id": self.receipt.location_dest_id.id, })] }) self.receipt.action_confirm() self.receipt.move_ids.move_line_ids.lot_name = "one" url = self._get_picking_url(self.receipt.id) self.start_tour(url, 'test_add_new_line', login='admin', timeout=60) names = self.receipt.move_ids.move_line_ids.mapped('lot_name') self.assertEqual(names, ["two", "one"]) def test_add_new_line_in_detailled_op(self): """ Check that the unsaved quantity/location changes of the detailed operations impact dynamically the creation of new move lines (considering the real avaible quantity rather than DB data's). """ warehouse = self.env.ref("stock.warehouse0") product_lot = self.env['product.product'].create({ 'name': 'Product Lot', 'is_storable': True, 'tracking': 'lot', }) lot_1, lot_2, lot_3 = self.env['stock.lot'].create([ {'name': 'LOT001', 'product_id': product_lot.id, 'company_id': warehouse.company_id.id}, {'name': 'LOT002', 'product_id': product_lot.id, 'company_id': warehouse.company_id.id}, {'name': 'LOT003', 'product_id': product_lot.id, 'company_id': warehouse.company_id.id}, ]) self.env['stock.quant']._update_available_quantity(product_lot, warehouse.lot_stock_id, quantity=10, lot_id=lot_1) self.env['stock.quant']._update_available_quantity(product_lot, warehouse.lot_stock_id, quantity=15, lot_id=lot_2) self.env['stock.quant']._update_available_quantity(product_lot, warehouse.lot_stock_id, quantity=10, lot_id=lot_3) partner = self.env['res.partner'].create({'name': 'Bob'}) delivery = self.picking_in = self.env['stock.picking'].create({ 'picking_type_id': warehouse.out_type_id.id, 'partner_id': partner.id, 'location_id': warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), 'move_ids': [Command.create({ 'name': product_lot.name, 'product_id': product_lot.id, 'location_id': warehouse.lot_stock_id.id, 'location_dest_id': self.ref('stock.stock_location_customers'), 'product_uom_qty': 20, })] }) delivery.action_confirm() self.assertRecordValues(delivery.move_line_ids, [ {'lot_id': lot_1.id, 'quantity': 10.0}, {'lot_id': lot_2.id, 'quantity': 10.0}, ]) url = self._get_picking_url(delivery.id) self.start_tour(url, 'test_add_new_line_in_detailled_op', login='admin', timeout=100) self.assertRecordValues(delivery.move_line_ids.sorted("quantity"), [ {'quantity': 2.0, 'lot_id': lot_1.id}, {'quantity': 3.0, 'lot_id': lot_1.id}, {'quantity': 15.0, 'lot_id': lot_2.id}, ]) def test_edit_existing_line(self): self.uom_unit = self.env.ref('uom.product_uom_unit') product_one = self.env['product.product'].create({ 'name': 'Product one', 'is_storable': True, 'tracking': 'serial', }) self.receipt.write({ "move_ids": [Command.create({ "name": "test", "product_id": product_one.id, "location_id": self.receipt.location_id.id, "location_dest_id": self.receipt.location_dest_id.id, "product_uom_qty": 1, })] }) self.receipt.action_confirm() self.receipt.move_ids.move_line_ids.lot_name = "one" url = self._get_picking_url(self.receipt.id) self.start_tour(url, 'test_edit_existing_line', login='admin', timeout=60) names = self.receipt.move_ids.move_line_ids.mapped('lot_name') self.assertEqual(names, ["one", "two"]) def test_edit_existing_lines_2(self): self.uom_unit = self.env.ref('uom.product_uom_unit') product_a, product_b = self.env["product.product"].create([ { 'name': 'Product a', 'is_storable': True, 'tracking': 'serial', }, { 'name': 'Product b', 'is_storable': True, 'tracking': 'serial', } ]) self.receipt.write({ "move_ids": [ Command.create({ "name": "Product a", "product_id": product_a.id, "location_id": self.receipt.location_id.id, "location_dest_id": self.receipt.location_dest_id.id, "product_uom_qty": 1, }), Command.create({ "name": "Product b", "product_id": product_b.id, "location_id": self.receipt.location_id.id, "location_dest_id": self.receipt.location_dest_id.id, "product_uom_qty": 1, }), ] }) self.receipt.action_confirm() url = self._get_picking_url(self.receipt.id) self.start_tour(url, 'test_edit_existing_lines_2', login='admin', timeout=60) names = self.receipt.move_ids.move_line_ids.mapped('lot_name') self.assertEqual(names, ["SNa001", "SNb001"]) def test_onchange_serial_lot_ids(self): """ Checks that onchange behaves correctly with respect to multiple unlinks """ product_serial = self.env['product.product'].create({ 'name': 'PSerial', 'is_storable': True, 'tracking': 'serial', 'categ_id': self.env.ref('product.product_category_all').id, }) lots = self.env['stock.lot'].create([{ 'name': 'SN01', 'product_id': product_serial.id, 'company_id': self.env.company.id, }, { 'name': 'SN02', 'product_id': product_serial.id, 'company_id': self.env.company.id, }, { 'name': 'SN03', 'product_id': product_serial.id, 'company_id': self.env.company.id, }]) stock_location = self.env.ref('stock.stock_location_stock') for lot in lots: self.env['stock.quant']._update_available_quantity(product_serial, stock_location, 1, lot_id=lot) picking = self.env['stock.picking'].create({ 'location_id': stock_location.id, 'location_dest_id': self.ref('stock.stock_location_customers'), 'picking_type_id': self.ref('stock.picking_type_out'), 'move_ids': [Command.create({ 'name': product_serial.name, 'product_id': product_serial.id, 'product_uom_qty': 3, 'product_uom': product_serial.uom_id.id, 'location_id': stock_location.id, 'location_dest_id': self.ref('stock.stock_location_customers'), })] }) picking.action_confirm() with Form(picking) as form: with form.move_ids_without_package.edit(0) as move_form: move_form.quantity = 3.0 move_form.lot_ids = lots url = self._get_picking_url(picking.id) self.start_tour(url, 'test_onchange_twice_lot_ids', login='admin') self.assertRecordValues(picking.move_ids, [{"quantity": 1, "lot_ids": lots[2].ids}])