# Part of Odoo. See LICENSE file for full copyright and licensing details. from unittest.mock import patch import odoo from odoo.tests import Form, tagged from odoo.addons.stock_barcode.tests.test_barcode_client_action import TestBarcodeClientAction @tagged('post_install', '-at_install') class TestPickingBarcodeClientAction(TestBarcodeClientAction): def test_internal_picking_from_scratch(self): """ Opens an empty internal picking and creates following move through the form view: - move 2 `self.product1` from shelf1 to shelf2 - move 1 `self.product2` from shelf1 to shelf3 - move 1 `self.product2` from shelf1 to shelf2 Then creates a fourth move by scanning product1 (from shelf1 to shelf3). Counts the number of picking's write. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.picking_type_internal.restrict_scan_dest_location = 'mandatory' self.picking_type_internal.restrict_scan_source_location = 'mandatory' internal_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_internal.id, 'immediate_transfer': True, }) url = self._get_client_action_url(internal_picking.id) # Mock the calls to write and run the phantomjs script. picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write picking_button_validate_orig = odoo.addons.stock.models.stock_picking.Picking.button_validate product1 = self.product1 stock_location = self.stock_location shelf1 = self.shelf1 shelf3 = self.shelf3 assertEqual = self.assertEqual self1 = self self1.stop_count_write = False def picking_write_mock(self, vals): if self1.stop_count_write: # Stops to count before `stock.picking` `button_validate` was called because # that method and its overrides can call an unpredictable amount of write. return picking_write_orig(self, vals) self1.call_count += 1 if self1.call_count == 1: # Open the edit form view for a line added by scanning its product. cmd = vals['move_line_ids'][0] write_vals = cmd[2] assertEqual(cmd[0], 0) assertEqual(cmd[1], 0) assertEqual(write_vals['product_id'], product1.id) assertEqual(write_vals['picking_id'], internal_picking.id) assertEqual(write_vals['location_id'], shelf1.id) assertEqual(write_vals['location_dest_id'], stock_location.id) assertEqual(write_vals['qty_done'], 1) elif self1.call_count == 2: # Write before the validate. cmd = vals['move_line_ids'][0] write_vals = cmd[2] assertEqual(cmd[0], 1) assertEqual(write_vals['location_dest_id'], shelf3.id) return picking_write_orig(self, vals) def picking_button_validate_mock(self): self1.stop_count_write = True # Stops to count write once validate is called. return picking_button_validate_orig(self) with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock): with patch('odoo.addons.stock.models.stock_picking.Picking.button_validate', new=picking_button_validate_mock): self.start_tour(url, 'test_internal_picking_from_scratch', login='admin', timeout=180) self.assertEqual(self.call_count, 2) self.assertEqual(len(internal_picking.move_line_ids), 4) prod1_ml = internal_picking.move_line_ids.filtered(lambda ml: ml.product_id.id == self.product1.id) prod2_ml = internal_picking.move_line_ids.filtered(lambda ml: ml.product_id.id == self.product2.id) self.assertEqual(prod1_ml[0].qty_done, 2) self.assertEqual(prod1_ml[0].location_id, self.shelf1) self.assertEqual(prod1_ml[0].location_dest_id, self.shelf2) self.assertEqual(prod1_ml[1].qty_done, 1) self.assertEqual(prod1_ml[1].location_id, self.shelf1) self.assertEqual(prod1_ml[1].location_dest_id, self.shelf3) self.assertEqual(prod2_ml[0].qty_done, 1) self.assertEqual(prod2_ml[0].location_id, self.shelf1) self.assertEqual(prod2_ml[0].location_dest_id, self.shelf2) self.assertEqual(prod2_ml[1].qty_done, 1) self.assertEqual(prod2_ml[1].location_id, self.shelf1) self.assertEqual(prod2_ml[1].location_dest_id, self.shelf3) def test_internal_picking_reserved_1(self): """ Open a reserved internal picking - move 1 `self.product1` and 1 `self.product2` from shelf1 to shelf2 - move 1 `self.product1` from shelf3 to shelf4. Before doing the reservation, move 1 `self.product1` from shelf3 to shelf2 """ self.clean_access_rights() self.picking_type_internal.restrict_scan_dest_location = 'mandatory' self.picking_type_internal.restrict_scan_source_location = 'mandatory' grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) internal_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_internal.id, }) odoo.addons.stock.models.stock_picking.Picking.write url = self._get_client_action_url(internal_picking.id) # prepare the picking self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 1) self.env['stock.quant']._update_available_quantity(self.product2, self.shelf1, 1) self.env['stock.quant']._update_available_quantity(self.product2, self.shelf3, 1) move1 = self.env['stock.move'].create({ 'name': 'test_internal_picking_reserved_1_1', 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 1, 'picking_id': internal_picking.id, }) move2 = self.env['stock.move'].create({ 'name': 'test_internal_picking_reserved_1_2', 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product2.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': internal_picking.id, }) internal_picking.action_confirm() internal_picking.action_assign() move1.move_line_ids.location_dest_id = self.shelf2.id for ml in move2.move_line_ids: if ml.location_id.id == self.shelf1.id: ml.location_dest_id = self.shelf2.id else: ml.location_dest_id = self.shelf4.id self.start_tour(url, 'test_internal_picking_reserved_1', login='admin', timeout=180) def test_receipt_from_scratch_with_lots_1(self): self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, 'immediate_transfer': True, }) url = self._get_client_action_url(receipt_picking.id) self.start_tour(url, 'test_receipt_from_scratch_with_lots_1', login='admin', timeout=180) self.assertEqual(receipt_picking.move_line_ids.mapped('lot_name'), ['lot1', 'lot2']) def test_receipt_from_scratch_with_lots_2(self): self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, 'immediate_transfer': True, }) url = self._get_client_action_url(receipt_picking.id) self.start_tour(url, 'test_receipt_from_scratch_with_lots_2', login='admin', timeout=180) self.assertEqual(receipt_picking.move_line_ids.mapped('lot_name'), ['lot1', 'lot2']) self.assertEqual(receipt_picking.move_line_ids.mapped('qty_done'), [2, 2]) def test_receipt_from_scratch_with_lots_3(self): """ Scans a non tracked product, then scans a tracked by lots product, then scans a production lot twice and checks the tracked product quantity was rightly increased. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, 'immediate_transfer': True, }) url = self._get_client_action_url(receipt_picking.id) self.start_tour(url, 'test_receipt_from_scratch_with_lots_3', login='admin', timeout=180) move_lines = receipt_picking.move_line_ids self.assertEqual(move_lines[0].product_id.id, self.product1.id) self.assertEqual(move_lines[0].qty_done, 1.0) self.assertEqual(move_lines[1].product_id.id, self.productlot1.id) self.assertEqual(move_lines[1].qty_done, 2.0) self.assertEqual(move_lines[1].lot_name, 'lot1') def test_receipt_from_scratch_with_lots_4(self): """ With picking type options "use_create_lots" and "use existing lots" disabled, scan a tracked product 3 times and checks the tracked product quantity was rightly increased without the need to enter serial/lot number. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) self.picking_type_in.use_create_lots = False self.picking_type_in.use_existing_lots = False receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, 'immediate_transfer': True, }) url = self._get_client_action_url(receipt_picking.id) self.start_tour(url, 'test_receipt_from_scratch_with_lots_4', login='admin', timeout=180) move_lines = receipt_picking.move_line_ids self.assertEqual(move_lines[0].product_id.id, self.productserial1.id) self.assertEqual(move_lines[0].qty_done, 3.0) def test_receipt_with_sn_1(self): """ With picking type options "use_create_lots" and "use_existing_lots" enabled, scan a tracked product and enter a serial number already registered (but not used) in the system. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) self.picking_type_in.use_create_lots = True self.picking_type_in.use_existing_lots = True snObj = self.env['stock.lot'] snObj.create({'name': 'sn1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, }) self.env['stock.move'].create({ 'name': 'test_receipt_1', 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.productserial1.id, 'product_uom': self.productserial1.uom_id.id, 'product_uom_qty': 1, 'picking_id': receipt_picking.id, 'picking_type_id': self.picking_type_in.id, }) url = self._get_client_action_url(receipt_picking.id) self.start_tour(url, 'test_receipt_with_sn_1', login='admin', timeout=180) move_lines = receipt_picking.move_line_ids self.assertEqual(move_lines[0].product_id.id, self.productserial1.id) self.assertEqual(move_lines[0].lot_id.name, 'sn1') self.assertEqual(move_lines[0].qty_done, 1.0) def test_receipt_reserved_1(self): """ Open a receipt. Move four units of `self.product1` and four units of unit of `self.product2` into shelf1. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, }) picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write url = self._get_client_action_url(receipt_picking.id) move1 = self.env['stock.move'].create({ 'name': 'test_receipt_reserved_1_1', 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 4, 'picking_id': receipt_picking.id, }) move2 = self.env['stock.move'].create({ 'name': 'test_receipt_reserved_1_2', 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product2.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 4, 'picking_id': receipt_picking.id, }) receipt_picking.action_confirm() receipt_picking.action_assign() # Mock the calls to write and run the phantomjs script. assertEqual = self.assertEqual ml1 = move1.move_line_ids ml2 = move2.move_line_ids shelf1 = self.shelf1 self1 = self def picking_write_mock(self, vals): self1.call_count += 1 if self1.call_count == 1: assertEqual(len(vals['move_line_ids']), 2) assertEqual(vals['move_line_ids'][0][:2], [1, ml2.id]) assertEqual(vals['move_line_ids'][1][:2], [1, ml1.id]) return picking_write_orig(self, vals) with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock): self.start_tour(url, 'test_receipt_reserved_1', login='admin', timeout=180) self.assertEqual(self.call_count, 1) self.assertEqual(receipt_picking.move_line_ids[0].location_dest_id.id, shelf1.id) self.assertEqual(receipt_picking.move_line_ids[1].location_dest_id.id, shelf1.id) def test_receipt_product_not_consecutively(self): """ Check that there is no new line created when scanning the same product several times but not consecutively.""" self.clean_access_rights() receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, }) url = self._get_client_action_url(receipt_picking.id) self.start_tour(url, 'test_receipt_product_not_consecutively', login='admin', timeout=180) self.assertEqual(len(receipt_picking.move_line_ids), 2) self.assertEqual(receipt_picking.move_line_ids.product_id.mapped('id'), [self.product1.id, self.product2.id]) self.assertEqual(receipt_picking.move_line_ids.mapped('qty_done'), [2, 1]) def test_delivery_lot_with_package(self): """ Have a delivery for a product tracked by SN, scan a non-reserved SN and checks the new created line has the right SN's package & owner. """ self.clean_access_rights() grp_lot = self.env.ref('stock.group_production_lot') grp_owner = self.env.ref('stock.group_tracking_owner') grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_owner.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) # Creates 4 serial numbers and adds 2 qty. for the reservation. snObj = self.env['stock.lot'] sn1 = snObj.create({'name': 'sn1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) sn2 = snObj.create({'name': 'sn2', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) sn3 = snObj.create({'name': 'sn3', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) sn4 = snObj.create({'name': 'sn4', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) package1 = self.env['stock.quant.package'].create({'name': 'pack_sn_1'}) package2 = self.env['stock.quant.package'].create({'name': 'pack_sn_2'}) partner = self.env['res.partner'].create({'name': 'Particulier'}) self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.productserial1.id, 'inventory_quantity': 1, 'lot_id': sn1.id, 'location_id': self.stock_location.id, 'package_id': package1.id, }).action_apply_inventory() self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.productserial1.id, 'inventory_quantity': 1, 'lot_id': sn2.id, 'location_id': self.stock_location.id, 'package_id': package1.id, }).action_apply_inventory() # Creates and confirms the delivery. delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) self.env['stock.move'].create({ 'name': self.productserial1.name, 'product_id': self.productserial1.id, 'product_uom_qty': 2, 'product_uom': self.productserial1.uom_id.id, 'picking_id': delivery_picking.id, 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, }) delivery_picking.action_confirm() delivery_picking.action_assign() # Add 2 more qty. after the reservation. self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.productserial1.id, 'inventory_quantity': 1, 'lot_id': sn3.id, 'location_id': self.stock_location.id, 'package_id': package2.id, }).action_apply_inventory() self.env['stock.quant'].with_context(inventory_mode=True).create({ 'product_id': self.productserial1.id, 'inventory_quantity': 1, 'lot_id': sn4.id, 'location_id': self.stock_location.id, 'package_id': package2.id, 'owner_id': partner.id, }).action_apply_inventory() # Runs the tour. url = self._get_client_action_url(delivery_picking.id) self.start_tour(url, 'test_delivery_lot_with_package', login='admin', timeout=180) # Checks move lines values after delivery was completed. self.assertEqual(delivery_picking.state, "done") move_line_1 = delivery_picking.move_line_ids[0] move_line_2 = delivery_picking.move_line_ids[1] self.assertEqual(move_line_1.lot_id, sn3) self.assertEqual(move_line_1.package_id, package2) self.assertEqual(move_line_1.owner_id.id, False) self.assertEqual(move_line_2.lot_id, sn4) self.assertEqual(move_line_2.package_id, package2) self.assertEqual(move_line_2.owner_id, partner) def test_delivery_reserved_1(self): self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, 'note': "A Test Note", }) picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write url = self._get_client_action_url(delivery_picking.id) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_1_1', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 4, 'picking_id': delivery_picking.id, }) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_1_2', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.product2.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 4, 'picking_id': delivery_picking.id, }) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4) self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 4) delivery_picking.action_confirm() delivery_picking.action_assign() self1 = self # Mock the calls to write and run the phantomjs script. def picking_write_mock(self, vals): self1.call_count += 1 return picking_write_orig(self, vals) with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock): self.start_tour(url, 'test_delivery_reserved_1', login='admin', timeout=180) self.assertEqual(self.call_count, 1) def test_delivery_reserved_2(self): self.clean_access_rights() delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write url = self._get_client_action_url(delivery_picking.id) pg_1 = self.env['procurement.group'].create({'name': 'ProcurementGroup1'}) pg_2 = self.env['procurement.group'].create({'name': 'ProcurementGroup2'}) partner_1 = self.env['res.partner'].create({'name': 'Parter1'}) partner_2 = self.env['res.partner'].create({'name': 'Partner2'}) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_2_1', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': delivery_picking.id, 'group_id': pg_1.id, 'restrict_partner_id': partner_1.id }) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_2_2', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': delivery_picking.id, 'group_id': pg_2.id, 'restrict_partner_id': partner_2.id }) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4) self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 4) delivery_picking.action_confirm() delivery_picking.action_assign() self.assertEqual(len(delivery_picking.move_ids), 2) self1 = self def picking_write_mock(self, vals): self1.call_count += 1 return picking_write_orig(self, vals) with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock): self.start_tour(url, 'test_delivery_reserved_2', login='admin', timeout=180) self.assertEqual(self.call_count, 0) def test_delivery_reserved_3(self): self.clean_access_rights() delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) picking_write_orig = odoo.addons.stock.models.stock_picking.Picking.write url = self._get_client_action_url(delivery_picking.id) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_2_1', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 1, 'picking_id': delivery_picking.id, }) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2) delivery_picking.action_confirm() delivery_picking.action_assign() self1 = self def picking_write_mock(self, vals): self1.call_count += 1 return picking_write_orig(self, vals) with patch('odoo.addons.stock.models.stock_picking.Picking.write', new=picking_write_mock): self.start_tour(url, 'test_delivery_reserved_3', login='admin', timeout=180) self.assertEqual(self.call_count, 0) def test_delivery_from_scratch_1(self): """ Scan unreserved lots on a delivery order. """ self.clean_access_rights() grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) # Adds lot1 and lot2 for productlot1 lotObj = self.env['stock.lot'] lotObj.create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) lotObj.create({'name': 'lot2', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) # Creates an empty picking. delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, 'immediate_transfer': True, }) url = self._get_client_action_url(delivery_picking.id) self.start_tour(url, 'test_delivery_from_scratch_with_lots_1', login='admin', timeout=180) lines = delivery_picking.move_line_ids self.assertEqual(lines[0].lot_id.name, 'lot1') self.assertEqual(lines[1].lot_id.name, 'lot2') self.assertEqual(lines[0].qty_done, 2) self.assertEqual(lines[1].qty_done, 2) def test_delivery_from_scratch_with_common_lots_name(self): """ Suppose: - two tracked-by-lot products - these products share one lot name - an extra product tracked by serial number This test ensures that a user can scan the tracked products in a picking that does not expect them and updates/creates the right line depending of the scanned lot """ self.clean_access_rights() group_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, group_lot.id, 0)]}) (self.product1 + self.product2).tracking = 'lot' lot01, lot02, sn = self.env['stock.lot'].create([{ 'name': lot_name, 'product_id': product.id, 'company_id': self.env.company.id, } for (lot_name, product) in [("LOT01", self.product1), ("LOT01", self.product2), ("SUPERSN", self.productserial1)]]) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2, lot_id=lot01) self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 3, lot_id=lot02) self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn) picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True)) picking_form.picking_type_id = self.picking_type_out delivery = picking_form.save() url = self._get_client_action_url(delivery.id) self.start_tour(url, 'test_delivery_from_scratch_with_common_lots_name', login='admin', timeout=180) self.assertRecordValues(delivery.move_line_ids, [ # pylint: disable=C0326 {'product_id': self.product1.id, 'lot_id': lot01.id, 'qty_done': 2}, {'product_id': self.product2.id, 'lot_id': lot02.id, 'qty_done': 3}, {'product_id': self.productserial1.id, 'lot_id': sn.id, 'qty_done': 1}, ]) def test_delivery_reserved_lots_1(self): self.clean_access_rights() grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) url = self._get_client_action_url(delivery_picking.id) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_lots_1', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.productlot1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 3, 'picking_id': delivery_picking.id, }) # Add lot1 et lot2 sur productlot1 lotObj = self.env['stock.lot'] lot1 = lotObj.create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) lot2 = lotObj.create({'name': 'lot2', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 1, lot_id=lot1) self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 2, lot_id=lot2) delivery_picking.action_confirm() delivery_picking.action_assign() self.assertEqual(delivery_picking.move_ids.state, 'assigned') self.assertEqual(len(delivery_picking.move_ids.move_line_ids), 2) self.start_tour(url, 'test_delivery_reserved_lots_1', login='admin', timeout=180) self.env.invalidate_all() lines = delivery_picking.move_line_ids self.assertEqual(lines[0].lot_id.name, 'lot1') self.assertEqual(lines[1].lot_id.name, 'lot2') self.assertEqual(lines[0].qty_done, 1) self.assertEqual(lines[1].qty_done, 2) def test_delivery_different_products_with_same_lot_name(self): self.clean_access_rights() grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) self.productlot2 = self.env['product.product'].create({ 'name': 'productlot2', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': 'productlot2', 'tracking': 'lot', }) delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) url = self._get_client_action_url(delivery_picking.id) self.env['stock.move'].create({ 'name': 'test_delivery_different_products_with_same_lot_name_1', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.productlot1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': delivery_picking.id, }) self.env['stock.move'].create({ 'name': 'test_delivery_different_products_with_same_lot_name_2', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.productlot2.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': delivery_picking.id, }) # Create 2 lots with the same name for productlot1 and productlot2 lot1 = self.env['stock.lot'].create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) lot2 = self.env['stock.lot'].create({'name': 'lot1', 'product_id': self.productlot2.id, 'company_id': self.env.company.id}) self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 2, lot_id=lot1) self.env['stock.quant']._update_available_quantity(self.productlot2, self.stock_location, 2, lot_id=lot2) delivery_picking.action_confirm() delivery_picking.action_assign() self.assertEqual(len(delivery_picking.move_ids), 2) self.start_tour(url, 'test_delivery_different_products_with_same_lot_name', login='admin', timeout=180) self.env.invalidate_all() lines = delivery_picking.move_line_ids self.assertEqual(lines[0].lot_id.name, 'lot1') self.assertEqual(lines[0].product_id.name, 'productlot1') self.assertEqual(lines[0].qty_done, 2) self.assertEqual(lines[1].lot_id.name, 'lot1') self.assertEqual(lines[1].product_id.name, 'productlot2') self.assertEqual(lines[1].qty_done, 2) def test_delivery_from_scratch_sn_1(self): """ Scan unreserved serial number on a delivery order. """ self.clean_access_rights() grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) # Add 4 serial numbers productserial1 snObj = self.env['stock.lot'] sn1 = snObj.create({'name': 'sn1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) sn2 = snObj.create({'name': 'sn2', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) sn3 = snObj.create({'name': 'sn3', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) sn4 = snObj.create({'name': 'sn4', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn1) self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn2) self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn3) self.env['stock.quant']._update_available_quantity(self.productserial1, self.stock_location, 1, lot_id=sn4) # empty picking delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_lots_1', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.productserial1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 4, 'picking_id': delivery_picking.id, }) delivery_picking.action_confirm() delivery_picking.action_assign() url = self._get_client_action_url(delivery_picking.id) self.start_tour(url, 'test_delivery_reserved_with_sn_1', login='admin', timeout=180) # TODO: the framework should call invalidate_cache every time a test cursor is asked or # given back self.env.invalidate_all() lines = delivery_picking.move_line_ids self.assertEqual(lines.mapped('lot_id.name'), ['sn1', 'sn2', 'sn3', 'sn4']) self.assertEqual(lines.mapped('qty_done'), [1, 1, 1, 1]) def test_delivery_using_buttons(self): """ Creates a delivery with 3 lines, then: - Completes first line with "+1" button; - Completes second line with "Add reserved quantities" button; - Completes last line with "+1" button and scanning barcode. Checks also written quantity on buttons is correctly updated and only "+1" button is displayed on new line created by user. """ self.clean_access_rights() # Creates a new product. product3 = self.env['product.product'].create({ 'name': 'product3', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': 'product3', }) # Creates some quants. self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2) self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 3) self.env['stock.quant']._update_available_quantity(product3, self.stock_location, 4) # Create the delivery transfer. delivery_form = Form(self.env['stock.picking']) delivery_form.picking_type_id = self.picking_type_out with delivery_form.move_ids_without_package.new() as move: move.product_id = self.product1 move.product_uom_qty = 2 with delivery_form.move_ids_without_package.new() as move: move.product_id = self.product2 move.product_uom_qty = 3 with delivery_form.move_ids_without_package.new() as move: move.product_id = product3 move.product_uom_qty = 4 delivery_picking = delivery_form.save() delivery_picking.action_confirm() delivery_picking.action_assign() url = self._get_client_action_url(delivery_picking.id) self.start_tour(url, 'test_delivery_using_buttons', login='admin', timeout=180) self.assertEqual(len(delivery_picking.move_line_ids), 4) self.assertEqual(delivery_picking.move_line_ids.mapped('qty_done'), [2, 3, 4, 2]) def test_receipt_reserved_lots_multiloc_1(self): self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) receipts_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, 'user_id': False, }) url = self._get_client_action_url(receipts_picking.id) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_lots_1', 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.productlot1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 4, 'picking_id': receipts_picking.id, }) # Creates lot1 and lot2 for productlot1. lotObj = self.env['stock.lot'] lotObj.create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) lotObj.create({'name': 'lot2', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) receipts_picking.action_confirm() receipts_picking.action_assign() self.assertEqual(receipts_picking.user_id.id, False) self.start_tour(url, 'test_receipt_reserved_lots_multiloc_1', login='admin', timeout=180) self.assertEqual(receipts_picking.user_id.id, self.env.user.id) self.env.invalidate_all() lines = receipts_picking.move_line_ids self.assertEqual(len(lines), 2) self.assertEqual(lines.mapped('qty_done'), [2, 2]) self.assertEqual(lines.mapped('location_id.name'), ['Vendors']) self.assertEqual(lines[0].location_dest_id.name, 'Section 1') self.assertEqual(lines[0].lot_name, 'lot2') self.assertEqual(lines[1].location_dest_id.name, 'Section 2') self.assertEqual(lines[1].lot_name, 'lot1') def test_pack_multiple_scan(self): """ Make a reception of two products, put them in pack and validate. Then make a delivery, scan the package two times (check the warning) and validate. Finally, check that the package is in the customer location. """ self.clean_access_rights() grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu') url = "/web#action=" + str(action_id.id) # set sequence packages to 1000 to find it easily in the tour sequence = self.env['ir.sequence'].search([( 'code', '=', 'stock.quant.package', )], limit=1) sequence.write({'number_next_actual': 1000}) self.start_tour(url, 'test_pack_multiple_scan', login='admin', timeout=180) # Check the new package is well delivered package = self.env['stock.quant.package'].search([ ('name', '=', 'PACK0001000') ]) self.assertEqual(package.location_id, self.customer_location) def test_pack_common_content_scan(self): """ Simulate a picking where 2 packages have the same products inside. It should display one barcode line for each package and not a common barcode line for both packages. """ self.clean_access_rights() grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu') url = "/web#action=" + str(action_id.id) # Create a pack and 2 quants in this pack pack1 = self.env['stock.quant.package'].create({ 'name': 'PACK1', }) pack2 = self.env['stock.quant.package'].create({ 'name': 'PACK2', }) self.env['stock.quant']._update_available_quantity( product_id=self.product1, location_id=self.stock_location, quantity=5, package_id=pack1, ) self.env['stock.quant']._update_available_quantity( product_id=self.product2, location_id=self.stock_location, quantity=1, package_id=pack1, ) self.env['stock.quant']._update_available_quantity( product_id=self.product1, location_id=self.stock_location, quantity=5, package_id=pack2, ) self.env['stock.quant']._update_available_quantity( product_id=self.product2, location_id=self.stock_location, quantity=1, package_id=pack2, ) self.start_tour(url, 'test_pack_common_content_scan', login='admin', timeout=180) def test_pack_multiple_location(self): """ Create a package in Shelf 1 and makes an internal transfer to move it to Shelf 2. """ self.clean_access_rights() grp_pack = self.env.ref('stock.group_tracking_lot') grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.picking_type_internal.active = True self.picking_type_internal.show_entire_packs = True self.picking_type_internal.restrict_scan_dest_location = 'mandatory' self.picking_type_internal.restrict_scan_source_location = 'mandatory' action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu') url = "/web#action=" + str(action_id.id) # Create a pack and 2 quants in this pack pack1 = self.env['stock.quant.package'].create({ 'name': 'PACK0000666', }) self.env['stock.quant']._update_available_quantity( product_id=self.product1, location_id=self.shelf1, quantity=5, package_id=pack1, ) self.env['stock.quant']._update_available_quantity( product_id=self.product2, location_id=self.shelf1, quantity=5, package_id=pack1, ) self.start_tour(url, 'test_pack_multiple_location', login='admin', timeout=180) # Check the new package is well transfered self.assertEqual(pack1.location_id, self.shelf2) def test_pack_multiple_location_02(self): """ Creates an internal transfer and reserves a package. Then this test will scan the location source, the package (already in the barcode view) and the location destination. """ self.clean_access_rights() grp_pack = self.env.ref('stock.group_tracking_lot') grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) # Creates a package with 1 quant in it. pack1 = self.env['stock.quant.package'].create({ 'name': 'PACK0002020', }) self.env['stock.quant']._update_available_quantity( product_id=self.product1, location_id=self.shelf1, quantity=5, package_id=pack1, ) # Creates an internal transfer for this package. internal_picking = self.env['stock.picking'].create({ 'location_id': self.shelf1.id, 'location_dest_id': self.shelf2.id, 'picking_type_id': self.picking_type_internal.id, }) url = self._get_client_action_url(internal_picking.id) self.env['stock.move'].create({ 'name': 'test_delivery_reserved_2_1', 'location_id': self.shelf1.id, 'location_dest_id': self.shelf2.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 5, 'picking_id': internal_picking.id, }) internal_picking.action_confirm() internal_picking.action_assign() self.start_tour(url, 'test_pack_multiple_location_02', login='admin', timeout=180) # Checks the new package is well transfered. self.assertEqual(pack1.location_id, self.shelf2) def test_put_in_pack_from_multiple_pages(self): """ In an internal picking where prod1 and prod2 are reserved in shelf1 and shelf2, processing all these products and then hitting put in pack should move them all in the new pack. """ self.clean_access_rights() # Adapts the setting to scan only the source location. self.picking_type_internal.restrict_scan_dest_location = 'no' self.picking_type_internal.restrict_scan_source_location = 'mandatory' grp_pack = self.env.ref('stock.group_tracking_lot') grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 1) self.env['stock.quant']._update_available_quantity(self.product2, self.shelf1, 1) self.env['stock.quant']._update_available_quantity(self.product1, self.shelf2, 1) self.env['stock.quant']._update_available_quantity(self.product2, self.shelf2, 1) internal_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_internal.id, }) self.env['stock.move'].create({ 'name': 'test_put_in_pack_from_multiple_pages', 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': internal_picking.id, }) self.env['stock.move'].create({ 'name': 'test_put_in_pack_from_multiple_pages', 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product2.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': internal_picking.id, }) url = self._get_client_action_url(internal_picking.id) internal_picking.action_confirm() internal_picking.action_assign() self.start_tour(url, 'test_put_in_pack_from_multiple_pages', login='admin', timeout=180) pack = self.env['stock.quant.package'].search([])[-1] self.assertEqual(len(pack.quant_ids), 2) self.assertEqual(sum(pack.quant_ids.mapped('quantity')), 4) def test_reload_flow(self): self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu') url = "/web#action=" + str(action_id.id) self.start_tour(url, 'test_reload_flow', login='admin', timeout=180) move_line1 = self.env['stock.move.line'].search_count([ ('product_id', '=', self.product1.id), ('location_dest_id', '=', self.shelf1.id), ('location_id', '=', self.supplier_location.id), ('qty_done', '=', 2), ]) move_line2 = self.env['stock.move.line'].search_count([ ('product_id', '=', self.product2.id), ('location_dest_id', '=', self.shelf1.id), ('location_id', '=', self.supplier_location.id), ('qty_done', '=', 1), ]) self.assertEqual(move_line1, 1) self.assertEqual(move_line2, 1) def test_duplicate_serial_number(self): """ Simulate a receipt and a delivery with a product tracked by serial number. It will try to break the ClientAction by using twice the same serial number. """ self.clean_access_rights() grp_lot = self.env.ref('stock.group_production_lot') grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu') url = "/web#action=" + str(action_id.id) self.start_tour(url, 'test_receipt_duplicate_serial_number', login='admin', timeout=180) self.start_tour(url, 'test_delivery_duplicate_serial_number', login='admin', timeout=180) def test_bypass_source_scan(self): """ Scan a lot, package, product without source location scan. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_pack = self.env.ref('stock.group_tracking_lot') grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_lot.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) # For the purpose of this test, disable the source scan (mandatory for a deliery otherwise). self.picking_type_out.restrict_scan_source_location = 'no' lot1 = self.env['stock.lot'].create({'name': 'lot1', 'product_id': self.productlot1.id, 'company_id': self.env.company.id}) lot2 = self.env['stock.lot'].create({'name': 'serial1', 'product_id': self.productserial1.id, 'company_id': self.env.company.id}) pack1 = self.env['stock.quant.package'].create({ 'name': 'THEPACK', }) self.env['stock.quant']._update_available_quantity(self.productlot1, self.shelf1, 2, lot_id=lot1) self.env['stock.quant']._update_available_quantity(self.productserial1, self.shelf2, 1, lot_id=lot2) self.env['stock.quant']._update_available_quantity(self.product1, self.shelf2, 4, package_id=pack1) delivery_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) url = self._get_client_action_url(delivery_picking.id) self.env['stock.move'].create({ 'name': 'test_bypass_source_scan_1_1', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.productserial1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 1, 'picking_id': delivery_picking.id, }) self.env['stock.move'].create({ 'name': 'test_bypass_source_scan_1_2', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.productlot1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': delivery_picking.id, }) self.env['stock.move'].create({ 'name': 'test_bypass_source_scan_1_3', 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 4, 'picking_id': delivery_picking.id, }) delivery_picking.action_confirm() delivery_picking.action_assign() self.start_tour(url, 'test_bypass_source_scan', login='admin', timeout=180) def test_put_in_pack_from_different_location(self): """ Scans two different products from two different locations, then put them in pack and scans a destination location. Checks the package is in the right location. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.picking_type_internal.active = True self.picking_type_internal.restrict_scan_source_location = 'no' self.picking_type_internal.restrict_scan_dest_location = 'optional' self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 1) self.env['stock.quant']._update_available_quantity(self.product2, self.shelf3, 1) internal_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_internal.id, }) self.env['stock.move'].create({ 'name': 'test_put_in_pack_from_different_location', 'location_id': self.shelf1.id, 'location_dest_id': self.shelf2.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 1, 'picking_id': internal_picking.id, }) self.env['stock.move'].create({ 'name': 'test_put_in_pack_from_different_location2', 'location_id': self.shelf3.id, 'location_dest_id': self.shelf2.id, 'product_id': self.product2.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 1, 'picking_id': internal_picking.id, }) url = self._get_client_action_url(internal_picking.id) internal_picking.action_confirm() internal_picking.action_assign() self.start_tour(url, 'test_put_in_pack_from_different_location', login='admin', timeout=180) pack = self.env['stock.quant.package'].search([])[-1] self.assertEqual(len(pack.quant_ids), 2) self.assertEqual(pack.location_id, self.shelf2) def test_put_in_pack_before_dest(self): self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.picking_type_internal.active = True self.picking_type_internal.restrict_scan_dest_location = 'mandatory' self.picking_type_internal.restrict_scan_source_location = 'mandatory' self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 1) self.env['stock.quant']._update_available_quantity(self.product2, self.shelf3, 1) internal_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_internal.id, }) self.env['stock.move'].create({ 'name': 'test_put_in_pack_before_dest', 'location_id': self.shelf1.id, 'location_dest_id': self.shelf2.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 1, 'picking_id': internal_picking.id, }) self.env['stock.move'].create({ 'name': 'test_put_in_pack_before_dest', 'location_id': self.shelf3.id, 'location_dest_id': self.shelf4.id, 'product_id': self.product2.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 1, 'picking_id': internal_picking.id, }) url = self._get_client_action_url(internal_picking.id) internal_picking.action_confirm() internal_picking.action_assign() self.start_tour(url, 'test_put_in_pack_before_dest', login='admin', timeout=180) pack = self.env['stock.quant.package'].search([])[-1] self.assertEqual(len(pack.quant_ids), 2) self.assertEqual(pack.location_id, self.shelf2) def test_put_in_pack_scan_package(self): """ Put in pack a product line, then scan the newly created package to assign it to another lines. """ self.clean_access_rights() grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0)]}) grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 1) self.env['stock.quant']._update_available_quantity(self.product1, self.shelf2, 1) self.env['stock.quant']._update_available_quantity(self.product2, self.shelf1, 1) # Resets package sequence to be sure we'll have the attended packages name. seq = self.env['ir.sequence'].search([('code', '=', 'stock.quant.package')]) seq.number_next_actual = 1 # Creates a delivery with three move lines: two from Section 1 and one from Section 2. delivery_form = Form(self.env['stock.picking']) delivery_form.picking_type_id = self.picking_type_out with delivery_form.move_ids_without_package.new() as move: move.product_id = self.product1 move.product_uom_qty = 2 with delivery_form.move_ids_without_package.new() as move: move.product_id = self.product2 move.product_uom_qty = 1 delivery = delivery_form.save() delivery.action_confirm() delivery.action_assign() url = self._get_client_action_url(delivery.id) self.start_tour(url, 'test_put_in_pack_scan_package', login='admin', timeout=180) self.assertEqual(delivery.state, 'done') self.assertEqual(len(delivery.move_line_ids), 3) for move_line in delivery.move_line_ids: self.assertEqual(move_line.result_package_id.name, 'PACK0000001') def test_highlight_packs(self): self.clean_access_rights() grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) pack1 = self.env['stock.quant.package'].create({ 'name': 'PACK001', }) pack2 = self.env['stock.quant.package'].create({ 'name': 'PACK002', }) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4, package_id=pack1) self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 4, package_id=pack1) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2, package_id=pack2) self.env['stock.quant']._update_available_quantity(self.product2, self.stock_location, 2, package_id=pack2) out_picking = self.env['stock.picking'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) self.picking_type_out.show_entire_packs = True self.env['stock.package_level'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'package_id': pack1.id, 'is_done': False, 'picking_id': out_picking.id, 'company_id': self.env.company.id, }) url = self._get_client_action_url(out_picking.id) out_picking.action_confirm() out_picking.action_assign() self.start_tour(url, 'test_highlight_packs', login='admin', timeout=180) def test_picking_owner_scan_package(self): grp_owner = self.env.ref('stock.group_tracking_owner') grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.env.user.write({'groups_id': [(4, grp_owner.id, 0)]}) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 7, package_id=self.package, owner_id=self.owner) action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu') url = "/web#action=" + str(action_id.id) self.start_tour(url, 'test_picking_owner_scan_package', login='admin', timeout=180) move_line = self.env['stock.move.line'].search([('product_id', '=', self.product1.id)], limit=1) self.assertTrue(move_line) line_owner = move_line.owner_id self.assertEqual(line_owner.id, self.owner.id) def test_picking_type_mandatory_scan_settings(self): ''' Makes some operations with different scan's settings.''' self.clean_access_rights() # Enables packages and multi-locations. grp_multi_loc = self.env.ref('stock.group_stock_multi_locations') grp_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, grp_multi_loc.id, 0), (4, grp_lot.id, 0)]}) # Creates a product without barcode to check it can always be processed regardless the config. product_without_barcode = self.env['product.product'].create({ 'name': 'Barcodeless Product', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, }) # Adds products' quantities. self.env['stock.quant']._update_available_quantity(self.product1, self.shelf1, 8) self.env['stock.quant']._update_available_quantity(product_without_barcode, self.shelf1, 8) # First config: products must be scanned, empty picking can't be immediatly validated, # locations can't be scanned, no put in pack. self.picking_type_internal.barcode_validation_after_dest_location = False self.picking_type_internal.barcode_validation_all_product_packed = False self.picking_type_internal.barcode_validation_full = False self.picking_type_internal.restrict_scan_product = True self.picking_type_internal.restrict_put_in_pack = 'optional' self.picking_type_internal.restrict_scan_source_location = 'no' self.picking_type_internal.restrict_scan_dest_location = 'no' # Creates an internal transfer, from WH/Stock/Shelf 1 to WH/Stock. picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_internal with picking_form.move_ids_without_package.new() as move: move.product_id = self.product1 move.product_uom_qty = 4 with picking_form.move_ids_without_package.new() as move: move.product_id = product_without_barcode move.product_uom_qty = 4 picking_internal_1 = picking_form.save() picking_internal_1.action_confirm() picking_internal_1.action_assign() url = self._get_client_action_url(picking_internal_1.id) self.start_tour(url, 'test_picking_type_mandatory_scan_settings_pick_int_1', login='admin', timeout=180) self.assertEqual(picking_internal_1.state, 'done') # Second picking: change the config (same than before but locations MUST be scanned). self.picking_type_internal.restrict_scan_source_location = 'mandatory' self.picking_type_internal.restrict_scan_dest_location = 'mandatory' # Creates an internal transfer, from WH/Stock/Shelf 1 to WH/Stock. picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_internal with picking_form.move_ids_without_package.new() as move: move.product_id = self.product1 move.product_uom_qty = 4 with picking_form.move_ids_without_package.new() as move: move.product_id = product_without_barcode move.product_uom_qty = 4 picking_internal_2 = picking_form.save() picking_internal_2.action_confirm() picking_internal_2.action_assign() url = self._get_client_action_url(picking_internal_2.id) self.start_tour(url, 'test_picking_type_mandatory_scan_settings_pick_int_2', login='admin', timeout=180) self.assertEqual(picking_internal_2.state, 'done') def test_picking_type_mandatory_scan_complete_flux(self): """ From the receipt to the delivery, make a complete flux with each picking types having their own barcode's settings: - Starts by receive multiple products (some of them are tracked); - Stores each product in a different location; - Makes a picking operation; - Then makes a packing operation and put all products in pack; - And finally, does the delivery. """ def create_picking(picking_type): picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = picking_type with picking_form.move_ids_without_package.new() as move: move.product_id = self.product1 move.product_uom_qty = 2 with picking_form.move_ids_without_package.new() as move: move.product_id = self.product2 move.product_uom_qty = 1 with picking_form.move_ids_without_package.new() as move: move.product_id = product_without_barcode move.product_uom_qty = 1 with picking_form.move_ids_without_package.new() as move: move.product_id = self.productserial1 move.product_uom_qty = 3 with picking_form.move_ids_without_package.new() as move: move.product_id = self.productlot1 move.product_uom_qty = 6 return picking_form.save() self.clean_access_rights() # Creates a product without barcode to check it can always be processed regardless the config. product_without_barcode = self.env['product.product'].create({ 'name': 'Barcodeless Product', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, }) # Enables packages, multi-locations and multiple steps routes. self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_production_lot').id, 0)]}) self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_tracking_lot').id, 0)]}) self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_stock_multi_locations').id, 0)]}) self.env.user.write({'groups_id': [(4, self.env.ref('stock.group_adv_location').id, 0)]}) warehouse = self.env.ref('stock.warehouse0') warehouse.reception_steps = 'two_steps' warehouse.delivery_steps = 'pick_pack_ship' # Creates two cluster packs. self.env['stock.quant.package'].create({ 'name': 'cluster-pack-01', 'package_use': 'reusable', }) self.env['stock.quant.package'].create({ 'name': 'cluster-pack-02', 'package_use': 'reusable', }) # Resets package sequence to be sure we'll have the attended packages name. seq = self.env['ir.sequence'].search([('code', '=', 'stock.quant.package')]) seq.number_next_actual = 1 # Configures the picking type's scan settings. # Receipt: no put in pack, can not be directly validate. self.picking_type_in.barcode_validation_full = False self.picking_type_in.restrict_put_in_pack = 'no' # Storage (internal transfer): no put in pack, scan dest. after each product. self.picking_type_internal.barcode_validation_full = False self.picking_type_internal.restrict_put_in_pack = 'no' self.picking_type_internal.restrict_scan_dest_location = 'mandatory' # Pick: source mandatory, lots reserved only. warehouse.pick_type_id.barcode_validation_full = False warehouse.pick_type_id.restrict_scan_source_location = 'mandatory' warehouse.pick_type_id.restrict_put_in_pack = 'mandatory' # Will use cluster packs. warehouse.pick_type_id.restrict_scan_tracking_number = 'mandatory' warehouse.pick_type_id.restrict_scan_dest_location = 'no' # Pack: pack after group, all products have to be packed to be validate. warehouse.pack_type_id.restrict_put_in_pack = 'optional' warehouse.pack_type_id.restrict_scan_tracking_number = 'mandatory' warehouse.pack_type_id.barcode_validation_all_product_packed = True warehouse.pick_type_id.restrict_scan_dest_location = 'no' # Delivery: pack after group, all products have to be packed to be validate. self.picking_type_out.restrict_put_in_pack = 'optional' self.picking_type_out.restrict_scan_tracking_number = 'mandatory' self.picking_type_out.barcode_validation_all_product_packed = True self.picking_type_out.show_entire_packs = True # Creates and assigns the receipt. picking_receipt = create_picking(self.picking_type_in) picking_receipt.action_confirm() picking_receipt.action_assign() # Get the storage operation (automatically created by the receipt). picking_internal = picking_receipt.move_ids.move_dest_ids.picking_id # Creates the pick, pack, ship. picking_pick = create_picking(warehouse.pick_type_id) picking_pack = create_picking(warehouse.pack_type_id) picking_delivery = create_picking(self.picking_type_out) picking_pack.location_dest_id = picking_delivery.location_id # Process each picking one by one. url = self._get_client_action_url(picking_receipt.id) self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_receipt', login='admin', timeout=180) self.assertEqual(picking_receipt.state, 'done') url = self._get_client_action_url(picking_internal.id) self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_internal', login='admin', timeout=180) self.assertEqual(picking_internal.state, 'done') picking_pick.action_confirm() picking_pick.action_assign() url = self._get_client_action_url(picking_pick.id) self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_pick', login='admin', timeout=180) self.assertEqual(picking_pick.state, 'done') picking_pack.action_confirm() picking_pack.action_assign() for move_line in picking_pack.move_line_ids: # TODO: shouldn't have to do that, reusable packages shouldn't be set in `result_package_id` for the next move. move_line.result_package_id = False url = self._get_client_action_url(picking_pack.id) self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_pack', login='admin', timeout=180) self.assertEqual(picking_pack.state, 'done') picking_delivery.action_confirm() picking_delivery.action_assign() url = self._get_client_action_url(picking_delivery.id) self.start_tour(url, 'test_picking_type_mandatory_scan_complete_flux_delivery', login='admin', timeout=180) self.assertEqual(picking_delivery.state, 'done') def test_receipt_delete_button(self): """ Scan products that not part of a receipt. Check that products not part of original receipt can be deleted, but the products that are part of the original receipt cannot be deleted. """ self.clean_access_rights() receipt_picking = self.env['stock.picking'].create({ 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'picking_type_id': self.picking_type_in.id, }) self.env['stock.move'].create({ 'name': 'test_receipt_1', 'location_id': self.supplier_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product1.id, 'product_uom': self.product1.uom_id.id, 'product_uom_qty': 1, 'picking_id': receipt_picking.id, 'picking_type_id': self.picking_type_in.id, }) # extra product to test that deleting works self.env['product.product'].create({ 'name': 'product3', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': 'product3', }) url = self._get_client_action_url(receipt_picking.id) receipt_picking.action_confirm() self.start_tour(url, 'test_receipt_delete_button', login='admin', timeout=180) self.assertEqual(len(receipt_picking.move_line_ids), 2, "2 lines expected: product1 + product2") def test_show_entire_package(self): """ Enables 'Move Entire Packages' for delivery and then creates two deliveries: - One where we use package level; - One where we use move without package. Then, checks it's the right type of line who is shown in the Barcode App.""" self.clean_access_rights() grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) self.picking_type_out.show_entire_packs = True package1 = self.env['stock.quant.package'].create({'name': 'package001'}) package2 = self.env['stock.quant.package'].create({'name': 'package002'}) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4, package_id=package1) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 4, package_id=package2) delivery_with_package_level = self.env['stock.picking'].create({ 'name': "Delivery with Package Level", 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) self.env['stock.package_level'].create({ 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'package_id': package1.id, 'is_done': False, 'picking_id': delivery_with_package_level.id, 'company_id': self.env.company.id, }) delivery_with_package_level.action_confirm() delivery_with_package_level.action_assign() delivery_with_move = self.env['stock.picking'].create({ 'name': "Delivery with Stock Move", 'location_id': self.stock_location.id, 'location_dest_id': self.customer_location.id, 'picking_type_id': self.picking_type_out.id, }) self.env['stock.move'].create({ 'name': 'test_show_entire_package', 'location_id': self.stock_location.id, 'location_dest_id': self.stock_location.id, 'product_id': self.product1.id, 'product_uom': self.uom_unit.id, 'product_uom_qty': 2, 'picking_id': delivery_with_move.id, }) delivery_with_move.action_confirm() delivery_with_move.action_assign() action = self.env["ir.actions.actions"]._for_xml_id("stock_barcode.stock_barcode_action_main_menu") url = '/web#action=%s' % action['id'] delivery_with_package_level.action_confirm() delivery_with_package_level.action_assign() self.assertFalse(delivery_with_package_level.package_level_ids.is_done) self.start_tour(url, 'test_show_entire_package', login='admin', timeout=180) self.assertTrue(delivery_with_package_level.package_level_ids.is_done) def test_define_the_destination_package(self): """ Suppose a picking that moves a product from a package to another one This test ensures that the user can scans the destination package """ self.clean_access_rights() group_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, group_pack.id, 0)]}) pack01, pack02 = self.env['stock.quant.package'].create([{ 'name': name, } for name in ('PACK01', 'PACK02')]) self.env['stock.quant']._update_available_quantity(self.product1, self.stock_location, 2, package_id=pack01) picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_out with picking_form.move_ids_without_package.new() as move: move.product_id = self.product1 move.product_uom_qty = 1 delivery = picking_form.save() delivery.action_confirm() url = self._get_client_action_url(delivery.id) self.start_tour(url, 'test_define_the_destination_package', login='admin', timeout=180) self.assertRecordValues(delivery.move_line_ids, [ {'product_id': self.product1.id, 'qty_done': 1, 'result_package_id': pack02.id, 'state': 'done'}, ]) def test_avoid_useless_line_creation(self): """ Suppose - the option "Create New Lots/Serial Numbers" disabled - a tracked product P with an available lot L On a delivery, a user scans L (it should add a line) Then, the user scans a non-existing lot LX (it should not create any line) """ self.clean_access_rights() group_lot = self.env.ref('stock.group_production_lot') self.env.user.write({'groups_id': [(4, group_lot.id, 0)]}) lot01 = self.env['stock.lot'].create({ 'name': "LOT01", 'product_id': self.productlot1.id, 'company_id': self.env.company.id, }) self.env['stock.quant']._update_available_quantity(self.productlot1, self.stock_location, 1, lot_id=lot01) picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True)) picking_form.picking_type_id = self.picking_type_out delivery = picking_form.save() url = self._get_client_action_url(delivery.id) self.start_tour(url, 'test_avoid_useless_line_creation', login='admin', timeout=180) self.assertRecordValues(delivery.move_ids, [ {'product_id': self.productlot1.id, 'lot_ids': lot01.ids, 'quantity_done': 1}, ]) def test_gs1_reserved_delivery(self): """ Process a delivery by scanning multiple quantity multiple times. """ self.clean_access_rights() self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature') # Creates a product and adds some quantity. product_gtin_8 = self.env['product.product'].create({ 'name': 'PRO_GTIN_8', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '11011019', # GTIN-8 format. 'uom_id': self.env.ref('uom.product_uom_unit').id, }) self.env['stock.quant']._update_available_quantity(product_gtin_8, self.stock_location, 99) # Creates and process the delivery. picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_out with picking_form.move_ids_without_package.new() as move: move.product_id = product_gtin_8 move.product_uom_qty = 10 delivery = picking_form.save() delivery.action_confirm() delivery.action_assign() url = self._get_client_action_url(delivery.id) self.start_tour(url, 'test_gs1_reserved_delivery', login='admin', timeout=180) self.assertEqual(delivery.state, 'done') self.assertEqual(len(delivery.move_ids), 1) self.assertEqual(delivery.move_ids.product_qty, 14) self.assertEqual(len(delivery.move_line_ids), 2) self.assertEqual(delivery.move_line_ids[0].qty_done, 10) self.assertEqual(delivery.move_line_ids[1].qty_done, 4) def test_gs1_receipt_conflicting_barcodes(self): """ Creates some receipts for two products but their barcodes mingle together once they are adapted for GS1. """ self.clean_access_rights() self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature') product_gtin_8 = self.env['product.product'].create({ 'name': 'PRO_GTIN_8', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '11011019', # GTIN-8 format -> Will become 00000011011019. 'uom_id': self.env.ref('uom.product_uom_unit').id, }) product_gtin_12 = self.env['product.product'].create({ 'name': 'PRO_GTIN_12', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '000011011019', # GTIN-12 format -> Will also become 00000011011019. 'uom_id': self.env.ref('uom.product_uom_unit').id, }) # Test for product_gtin_8 only. picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_in with picking_form.move_ids_without_package.new() as move: move.product_id = product_gtin_8 move.product_uom_qty = 1 receipt_1 = picking_form.save() receipt_1.action_confirm() receipt_1.action_assign() url = self._get_client_action_url(receipt_1.id) self.start_tour(url, 'test_gs1_receipt_conflicting_barcodes_1', login='admin', timeout=180) self.assertEqual(receipt_1.state, 'done') self.assertEqual(len(receipt_1.move_line_ids), 1) self.assertEqual(receipt_1.move_line_ids.product_id.id, product_gtin_8.id) # Test for product_gtin_12 only. picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_in with picking_form.move_ids_without_package.new() as move: move.product_id = product_gtin_12 move.product_uom_qty = 1 receipt_2 = picking_form.save() receipt_2.action_confirm() receipt_2.action_assign() url = self._get_client_action_url(receipt_2.id) self.start_tour(url, 'test_gs1_receipt_conflicting_barcodes_2', login='admin', timeout=180) self.assertEqual(receipt_2.state, 'done') self.assertEqual(len(receipt_2.move_line_ids), 1) self.assertEqual(receipt_2.move_line_ids.product_id.id, product_gtin_12.id) # Test for both product_gtin_8 and product_gtin_12. picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_in with picking_form.move_ids_without_package.new() as move: move.product_id = product_gtin_8 move.product_uom_qty = 1 with picking_form.move_ids_without_package.new() as move: move.product_id = product_gtin_12 move.product_uom_qty = 1 receipt_3 = picking_form.save() receipt_3.action_confirm() receipt_3.action_assign() self.assertEqual(len(receipt_3.move_line_ids), 2) url = self._get_client_action_url(receipt_3.id) self.start_tour(url, 'test_gs1_receipt_conflicting_barcodes_3', login='admin', timeout=180) self.assertEqual(receipt_3.state, 'done') self.assertEqual(len(receipt_3.move_line_ids), 3) self.assertEqual(receipt_3.move_line_ids[0].product_id.id, product_gtin_8.id) self.assertEqual(receipt_3.move_line_ids[0].qty_done, 1) self.assertEqual(receipt_3.move_line_ids[1].product_id.id, product_gtin_12.id) self.assertEqual(receipt_3.move_line_ids[1].qty_done, 1) self.assertEqual(receipt_3.move_line_ids[2].product_id.id, product_gtin_8.id) self.assertEqual(receipt_3.move_line_ids[2].qty_done, 1) def test_gs1_receipt_lot_serial(self): """ Creates a receipt for a product tracked by lot, then process it in the Barcode App. """ self.clean_access_rights() self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature') picking_form = Form(self.env['stock.picking']) picking_form.picking_type_id = self.picking_type_in with picking_form.move_ids_without_package.new() as move: move.product_id = self.product_tln_gtn8 move.product_uom_qty = 40 receipt = picking_form.save() receipt.action_confirm() receipt.action_assign() url = self._get_client_action_url(receipt.id) self.start_tour(url, 'test_gs1_receipt_lot_serial', login='admin', timeout=180) self.assertEqual(receipt.state, 'done') self.assertEqual(len(receipt.move_line_ids), 5) self.assertEqual( receipt.move_line_ids.lot_id.mapped('name'), ['b1-b001', 'b1-b002', 'b1-b003', 'b1-b004', 'b1-b005'] ) for move_line in receipt.move_line_ids: self.assertEqual(move_line.qty_done, 8) def test_gs1_receipt_quantity_with_uom(self): """ Creates a new receipt and scans barcodes with different combinaisons of product and quantity expressed with different UoM and checks the quantity is taken only if the UoM is compatible with the product's one. """ self.clean_access_rights() # Enables the UoM and the GS1 nomenclature. grp_uom = self.env.ref('uom.group_uom') group_user = self.env.ref('base.group_user') group_user.write({'implied_ids': [(4, grp_uom.id)]}) self.env.user.write({'groups_id': [(4, grp_uom.id)]}) self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature') # Configures three products using units, kg and g. uom_unit = self.env.ref('product.product_category_all') uom_g = self.env.ref('uom.product_uom_gram') uom_kg = self.env.ref('uom.product_uom_kgm') product_by_units = self.env['product.product'].create({ 'name': 'Product by Units', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '15264329', 'uom_id': uom_unit.id, }) product_by_kg = self.env['product.product'].create({ 'name': 'Product by g', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '15264893', 'uom_id': uom_g.id, 'uom_po_id': uom_g.id, }) product_by_g = self.env['product.product'].create({ 'name': 'Product by kg', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '15264879', 'uom_id': uom_kg.id, 'uom_po_id': uom_kg.id, }) # Creates a new receipt. picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True)) picking_form.picking_type_id = self.picking_type_in receipt = picking_form.save() # Runs the tour. url = self._get_client_action_url(receipt.id) self.start_tour(url, 'test_gs1_receipt_quantity_with_uom', login='admin', timeout=180) # Checks the moves' quantities and UoM. self.assertTrue(len(receipt.move_ids), 3) move1, move2, move3 = receipt.move_ids self.assertTrue(move1.product_id.id, product_by_units.id) self.assertTrue(move1.quantity_done, 4) self.assertTrue(move1.product_uom.id, uom_unit.id) self.assertTrue(move2.product_id.id, product_by_kg.id) self.assertTrue(move2.quantity_done, 5) self.assertTrue(move2.product_uom.id, uom_kg.id) self.assertTrue(move3.product_id.id, product_by_g.id) self.assertTrue(move3.quantity_done, 1250) self.assertTrue(move3.product_uom.id, uom_g.id) def test_gs1_package_receipt_and_delivery(self): """ Receives some products and scans a GS1 barcode for a package, then creates a delivery and scans the same package. """ self.clean_access_rights() self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature') grp_pack = self.env.ref('stock.group_tracking_lot') self.env.user.write({'groups_id': [(4, grp_pack.id, 0)]}) # Set package's sequence to 123 to generate always the same package's name in the tour. sequence = self.env['ir.sequence'].search([('code', '=', 'stock.quant.package')], limit=1) sequence.write({'number_next_actual': 123}) # Creates two products and two package's types. product1 = self.env['product.product'].create({ 'name': 'PRO_GTIN_8', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '82655853', # GTIN-8 'uom_id': self.env.ref('uom.product_uom_unit').id }) product2 = self.env['product.product'].create({ 'name': 'PRO_GTIN_12', 'type': 'product', 'categ_id': self.env.ref('product.product_category_all').id, 'barcode': '584687955629', # GTIN-12 'uom_id': self.env.ref('uom.product_uom_unit').id, }) wooden_chest_package_type = self.env['stock.package.type'].create({ 'name': 'Wooden Chest', 'barcode': 'WOODC', }) iron_chest_package_type = self.env['stock.package.type'].create({ 'name': 'Iron Chest', 'barcode': 'IRONC', }) action_id = self.env.ref('stock_barcode.stock_barcode_action_main_menu') url = "/web#action=" + str(action_id.id) self.start_tour(url, 'test_gs1_package_receipt', login='admin', timeout=180) # Checks the package is in the stock location with the products. package = self.env['stock.quant.package'].search([('name', '=', '546879213579461324')]) package2 = self.env['stock.quant.package'].search([('name', '=', '130406658041178543')]) package3 = self.env['stock.quant.package'].search([('name', '=', 'PACK0000123')]) self.assertEqual(len(package), 1) self.assertEqual(len(package.quant_ids), 2) self.assertEqual(package.package_type_id.id, wooden_chest_package_type.id) self.assertEqual(package.quant_ids[0].product_id.id, product1.id) self.assertEqual(package.quant_ids[1].product_id.id, product2.id) self.assertEqual(package.location_id.id, self.stock_location.id) self.assertEqual(package2.package_type_id.id, iron_chest_package_type.id) self.assertEqual(package2.quant_ids.product_id.id, product1.id) self.assertEqual(package3.package_type_id.id, iron_chest_package_type.id) self.assertEqual(package3.quant_ids.product_id.id, product2.id) self.start_tour(url, 'test_gs1_package_delivery', login='admin', timeout=180) # Checks the package is in the customer's location. self.assertEqual(package.location_id.id, self.customer_location.id) def test_gs1_and_packaging(self): """ This test ensures that a user can scan a packaging when processing a receipt """ self.clean_access_rights() group_packaging = self.env.ref('product.group_stock_packaging') self.env.user.write({'groups_id': [(4, group_packaging.id)]}) self.env.company.nomenclature_id = self.env.ref('barcodes_gs1_nomenclature.default_gs1_nomenclature') product = self.env['product.product'].create({ 'name': 'Bottle', 'type': 'product', 'barcode': '1113', 'packaging_ids': [(0, 0, { 'name': '6-bottle pack', 'qty': 6, 'barcode': '2226', })], }) picking_form = Form(self.env['stock.picking'].with_context(default_immediate_transfer=True)) picking_form.picking_type_id = self.picking_type_in receipt = picking_form.save() url = self._get_client_action_url(receipt.id) self.start_tour(url, 'test_gs1_receipt_packaging', login='admin', timeout=180) move = receipt.move_ids self.assertEqual(move.product_id, product) self.assertEqual(move.quantity_done, 30)