# Copyright 2108-2019 Sergio Teruel # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo.exceptions import MissingError, UserError from odoo.tests.common import tagged from .common import TestCommonStockBarcodes @tagged("post_install", "-at_install") class TestStockBarcodesPicking(TestCommonStockBarcodes): @classmethod def setUpClass(cls): super().setUpClass() cls.ScanReadPicking = cls.env["wiz.stock.barcodes.read.picking"] cls.stock_picking_model = cls.env.ref("stock.model_stock_picking") # Model Data cls.barcode_option_group_out = cls._create_barcode_option_group_outgoing() cls.barcode_option_group_in = cls._create_barcode_option_group_incoming() cls.barcode_option_group_out.barcode_guided_mode = False cls.barcode_option_group_in.barcode_guided_mode = False cls.partner_agrolite = cls.env.ref("base.res_partner_2") cls.picking_type_in = cls.env.ref("stock.picking_type_in") cls.picking_type_in.barcode_option_group_id = cls.barcode_option_group_in cls.picking_type_out = cls.env.ref("stock.picking_type_out") cls.picking_type_out.reservation_method = "manual" cls.picking_type_out.barcode_option_group_id = cls.barcode_option_group_out cls.supplier_location = cls.env.ref("stock.stock_location_suppliers") cls.customer_location = cls.env.ref("stock.stock_location_customers") cls.stock_location = cls.env.ref("stock.stock_location_stock") cls.categ_unit = cls.env.ref("uom.product_uom_categ_unit") cls.categ_kgm = cls.env.ref("uom.product_uom_categ_kgm") cls.picking_out_01 = ( cls.env["stock.picking"] .with_context(planned_picking=True) .create( { "location_id": cls.stock_location.id, "location_dest_id": cls.customer_location.id, "partner_id": cls.partner_agrolite.id, "picking_type_id": cls.picking_type_out.id, "move_ids": [ ( 0, 0, { "name": cls.product_tracking.name, "product_id": cls.product_tracking.id, "product_uom_qty": 3, "product_uom": cls.product_tracking.uom_id.id, "location_id": cls.stock_location.id, "location_dest_id": cls.customer_location.id, }, ) ], } ) ) cls.picking_out_02 = cls.picking_out_01.copy() cls.picking_in_01 = ( cls.env["stock.picking"] .with_context(planned_picking=True) .create( { "location_id": cls.supplier_location.id, "location_dest_id": cls.stock_location.id, "partner_id": cls.partner_agrolite.id, "picking_type_id": cls.picking_type_in.id, "move_ids": [ ( 0, 0, { "name": cls.product_wo_tracking.name, "product_id": cls.product_wo_tracking.id, "product_uom_qty": 3, "product_uom": cls.product_wo_tracking.uom_id.id, "location_id": cls.supplier_location.id, "location_dest_id": cls.stock_location.id, }, ), ( 0, 0, { "name": cls.product_wo_tracking.name, "product_id": cls.product_wo_tracking.id, "product_uom_qty": 5, "product_uom": cls.product_wo_tracking.uom_id.id, "location_id": cls.supplier_location.id, "location_dest_id": cls.stock_location.id, }, ), ( 0, 0, { "name": cls.product_tracking.name, "product_id": cls.product_tracking.id, "product_uom_qty": 3, "product_uom": cls.product_tracking.uom_id.id, "location_id": cls.supplier_location.id, "location_dest_id": cls.stock_location.id, }, ), ( 0, 0, { "name": cls.product_tracking.name, "product_id": cls.product_tracking.id, "product_uom_qty": 5, "product_uom": cls.product_tracking.uom_id.id, "location_id": cls.supplier_location.id, "location_dest_id": cls.stock_location.id, }, ), ], } ) ) cls.picking_in_01.action_confirm() action = cls.picking_in_01.action_barcode_scan() cls.wiz_scan_picking = cls.ScanReadPicking.browse(action["res_id"]) # Create a wizard for outgoing picking cls.picking_out_01.action_confirm() action = cls.picking_out_01.action_barcode_scan() cls.wiz_scan_picking_out = cls.ScanReadPicking.browse(action["res_id"]) def test_wiz_picking_values(self): self.assertEqual( self.wiz_scan_picking.location_id, self.picking_in_01.location_id ) self.assertEqual(self.wiz_scan_picking.res_model_id, self.stock_picking_model) self.assertEqual(self.wiz_scan_picking.res_id, self.picking_in_01.id) self.assertIn( "Barcode reader - %s - " % (self.picking_in_01.name), self.wiz_scan_picking.display_name, ) def test_picking_wizard_scan_product(self): # self.wiz_scan_picking.manual_entry = True wiz_scan_picking = self.wiz_scan_picking.with_context( force_create_move=True, no_increase_qty_done=True ) self.action_barcode_scanned(wiz_scan_picking, "8480000723208") sml = self.picking_in_01.move_line_ids.filtered( lambda x: x.product_id == self.product_wo_tracking ) self.assertEqual(sml.qty_done, 1.0) # Scan product with tracking lot enable self.action_barcode_scanned(wiz_scan_picking, "8433281006850") sml = self.picking_in_01.move_line_ids.filtered( lambda x: x.product_id == self.product_tracking ) self.assertEqual(sml.qty_done, 0.0) self.assertEqual( self.wiz_scan_picking.message, "8433281006850 (Scan Product, Packaging, Lot / Serial)", ) # Scan a lot. Increment quantities if scan product or other lot from # this product self.action_barcode_scanned(wiz_scan_picking, "8411822222568") sml = self.picking_in_01.move_line_ids.filtered( lambda x: x.product_id == self.product_tracking and x.lot_id ) self.assertEqual(sml.lot_id, self.lot_1) self.assertEqual(sml.qty_done, 1.0) self.action_barcode_scanned(wiz_scan_picking, "8433281006850") stock_move = sml.move_id self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 1.0) self.action_barcode_scanned(wiz_scan_picking, "8411822222568") self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 1.0) self.assertEqual( self.wiz_scan_picking.message, "8411822222568 (Scan Product, Packaging, Lot / Serial)", ) # Scan a package self.action_barcode_scanned(wiz_scan_picking, "5420008510489") # Package of 5 product units. Already three unit exists self.assertEqual(sum(stock_move.move_line_ids.mapped("qty_done")), 5.0) def test_picking_wizard_scan_product_manual_entry(self): wiz_scan_picking = self.wiz_scan_picking.with_context( force_create_move=True, no_increase_qty_done=True ) wiz_scan_picking.manual_entry = True self.action_barcode_scanned(wiz_scan_picking, "8480000723208") sml = self.picking_in_01.move_line_ids.filtered( lambda x: x.product_id == self.product_wo_tracking ) self.assertEqual(wiz_scan_picking.product_qty, 0.0) wiz_scan_picking.product_qty = 12.0 wiz_scan_picking.action_confirm() self.assertEqual(sml.qty_done, 12.0) def test_barcode_from_operation(self): picking_out_3 = self.picking_out_01.copy() self.picking_out_01.action_assign() self.picking_out_02.action_assign() self.picking_type_out.default_location_dest_id = self.customer_location action = self.picking_type_out.action_barcode_scan() self.wiz_scan_picking = self.ScanReadPicking.browse(action["res_id"]) self.wiz_scan_picking.manual_entry = True self.wiz_scan_picking.product_id = self.product_tracking self.wiz_scan_picking.lot_id = self.lot_1 self.wiz_scan_picking.product_qty = 2 self.wiz_scan_picking.with_context( force_create_move=True, no_increase_qty_done=True ).action_confirm() self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids[0:2]), 2) # Lock first picking candidate = self.wiz_scan_picking.candidate_picking_ids.filtered( lambda c: c.picking_id == self.picking_out_01 ) candidate_wiz = candidate.with_context( wiz_barcode_id=self.wiz_scan_picking.id, picking_id=self.picking_out_01.id ) candidate_wiz.with_context(force_create_move=True).action_lock_picking() self.assertEqual(self.picking_out_01.move_ids.quantity_done, 2) self.wiz_scan_picking.product_qty = 2 self.wiz_scan_picking.with_context( force_create_move=True, no_increase_qty_done=True ).action_confirm() self.assertEqual(self.picking_out_01.move_ids.quantity_done, 2) # Picking out 3 is in confirmed state, so until confirmed moves has # not been activated candidate pickings is 2 picking_out_3.action_confirm() candidate_wiz.action_unlock_picking() self.wiz_scan_picking.product_qty = 2 self.wiz_scan_picking.with_context( force_create_move=True, no_increase_qty_done=True ).action_confirm() self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids[0:2]), 2) candidate_wiz.action_unlock_picking() self.wiz_scan_picking.product_qty = 2 self.wiz_scan_picking.option_group_id.confirmed_moves = True self.wiz_scan_picking.with_context( force_create_move=True, no_increase_qty_done=True ).action_confirm() self.assertEqual(len(self.wiz_scan_picking.candidate_picking_ids[0:3]), 3) def test_picking_wizard_scan_product_auto_lot(self): # Prepare more data lot_2 = self.StockProductionLot.create( { "name": "8411822222578", "product_id": self.product_tracking.id, "company_id": self.company.id, } ) lot_3 = self.StockProductionLot.create( { "name": "8411822222588", "product_id": self.product_tracking.id, "company_id": self.company.id, } ) quant_lot_2 = self.StockQuant.create( { "product_id": self.product_tracking.id, "lot_id": lot_2.id, "location_id": self.stock_location.id, "quantity": 15.0, } ) quant_lot_3 = self.StockQuant.create( { "product_id": self.product_tracking.id, "lot_id": lot_3.id, "location_id": self.stock_location.id, "quantity": 10.0, } ) self.quant_lot_1.in_date = "2021-01-01" quant_lot_2.in_date = "2021-01-05" quant_lot_3.in_date = "2021-01-06" # Scan product with tracking lot enable self.action_barcode_scanned(self.wiz_scan_picking, "8433281006850") self.assertEqual( self.wiz_scan_picking.message, "8433281006850 (Scan Product, Packaging, Lot / Serial)", ) self.wiz_scan_picking.auto_lot = True # self.wiz_scan_picking.manual_entry = True # Removal strategy FIFO # No auto lot for incoming pickings self.action_barcode_scanned(self.wiz_scan_picking, "8433281006850") self.assertFalse(self.wiz_scan_picking.lot_id) # Continue test with a outgoing wizard self.wiz_scan_picking_out.option_group_id.auto_lot = True self.wiz_scan_picking_out.auto_lot = True self.action_barcode_scanned(self.wiz_scan_picking_out, "8433281006850") self.assertEqual(self.wiz_scan_picking_out.lot_id, self.lot_1) # Removal strategy LIFO self.wiz_scan_picking_out.lot_id = False self.product_tracking.categ_id.removal_strategy_id = self.env.ref( "stock.removal_lifo" ) self.wiz_scan_picking_out.action_clean_values() self.action_barcode_scanned(self.wiz_scan_picking_out, "8433281006850") self.assertEqual(self.wiz_scan_picking_out.lot_id, lot_3) @classmethod def _create_barcode_option_group_incoming(cls): return cls.env["stock.barcodes.option.group"].create( { "name": "option group incoming for tests", "option_ids": [ ( 0, 0, { "step": 1, "name": "Location", "field_name": "location_id", "filled_default": True, "to_scan": False, "required": True, }, ), ( 0, 0, { "step": 2, "name": "Product", "field_name": "product_id", "to_scan": True, "required": True, "clean_after_done": True, }, ), ( 0, 0, { "step": 2, "name": "Packaging", "field_name": "packaging_id", "to_scan": True, "required": False, }, ), ( 0, 0, { "step": 2, "name": "Lot / Serial", "field_name": "lot_id", "to_scan": True, "required": True, }, ), ( 0, 0, { "step": 3, "name": "Location Dest", "field_name": "location_dest_id", "filled_default": True, "to_scan": False, "required": True, }, ), ( 0, 0, { "step": 4, "name": "Quantity", "field_name": "product_qty", "required": True, "clean_after_done": True, }, ), ], } ) @classmethod def _create_barcode_option_group_outgoing(cls): return cls.env["stock.barcodes.option.group"].create( { "name": "option group outgoing for tests", "option_ids": [ ( 0, 0, { "step": 1, "name": "Location", "field_name": "location_id", "to_scan": True, "required": True, "filled_default": True, }, ), ( 0, 0, { "step": 2, "name": "Product", "field_name": "product_id", "to_scan": True, "required": True, }, ), ( 0, 0, { "step": 2, "name": "Packaging", "field_name": "packaging_id", "to_scan": True, "required": False, }, ), ( 0, 0, { "step": 2, "name": "Lot / Serial", "field_name": "lot_id", "to_scan": True, "required": True, }, ), ( 0, 0, { "step": 3, "name": "Location Dest", "field_name": "location_dest_id", "filled_default": True, "to_scan": False, "required": True, }, ), ( 0, 0, { "step": 4, "name": "Quantity", "field_name": "product_qty", "required": True, "clean_after_done": True, }, ), ], } ) def test_stock_picking_validate(self): self.picking_in_01.state = False with self.assertRaises(UserError): self.picking_in_01.with_context( stock_barcodes_validate_picking=True ).button_validate() def test_barcode_read_picking(self): self.picking_in_01.state = "done" self.wiz_scan_picking._compute_enable_add_product() self.assertFalse(self.wiz_scan_picking.enable_add_product) self.wiz_scan_picking.show_detailed_operations = False self.wiz_scan_picking.action_show_detailed_operations() self.assertTrue(self.wiz_scan_picking.action_show_detailed_operations) self.wiz_scan_picking.action_show_detailed_operations() self.assertFalse(self.wiz_scan_picking.show_detailed_operations) def test_barcode_read_inventory(self): context = { "params": { "model": "wiz.stock.barcodes.read.inventory", "id": self.quant_lot_1.id, } } with self.assertRaises(MissingError): self.quant_lot_1.with_context( **context ).action_barcode_inventory_quant_unlink() context = { "params": { "model": self.wiz_scan_read_inventory._name, "id": self.wiz_scan_read_inventory.id, } } self.quant_lot_1.with_context(**context).action_barcode_inventory_quant_unlink() self.assertIsNone( self.quant_lot_1.with_context( **context ).action_barcode_inventory_quant_unlink() ) self.assertIsNone(self.quant_lot_1.enable_current_operations()) self.assertIsNone(self.quant_lot_1.action_barcode_inventory_quant_edit()) with self.assertRaises(ValueError): self.quant_lot_1.write({"inventory_quantity": "test"}) self.quant_lot_1.operation_quantities_rest() self.quant_lot_1.operation_quantities() self.assertEqual( type(self.picking_in_01.picking_type_id.get_action_picking_tree_ready()), dict, ) self.assertEqual( type( self.picking_in_01.picking_type_id.with_context( **{"operations_mode": True} ).get_action_picking_tree_ready() ), dict, ) self.assertIsNone(self.wiz_scan_candidate_picking._compute_picking_quantity()) self.assertIsNone(self.wiz_scan_candidate_picking._compute_is_pending()) self.assertEqual( self.wiz_scan_candidate_picking._get_picking_to_validate()._name, self.picking_in_01._name, ) self.assertEqual( type(self.wiz_scan_candidate_picking.action_validate_picking()), tuple )