diff --git a/stock_vertical_lift/demo/product_demo.xml b/stock_vertical_lift/demo/product_demo.xml index bfe8ab90d..b55184196 100644 --- a/stock_vertical_lift/demo/product_demo.xml +++ b/stock_vertical_lift/demo/product_demo.xml @@ -3,6 +3,7 @@ RS200 + 4491673293664 Running Socks product @@ -17,6 +18,7 @@ RS300 + 2779891103531 Recovery Socks product diff --git a/stock_vertical_lift/demo/stock_picking_demo.xml b/stock_vertical_lift/demo/stock_picking_demo.xml index 520d97925..bff48ee18 100644 --- a/stock_vertical_lift/demo/stock_picking_demo.xml +++ b/stock_vertical_lift/demo/stock_picking_demo.xml @@ -27,4 +27,26 @@ + + + Incoming shipment from Vertical Lift (demo) + + + + + + + + + + + diff --git a/stock_vertical_lift/models/vertical_lift_operation_base.py b/stock_vertical_lift/models/vertical_lift_operation_base.py index a23cdc2ec..8d0656cbc 100644 --- a/stock_vertical_lift/models/vertical_lift_operation_base.py +++ b/stock_vertical_lift/models/vertical_lift_operation_base.py @@ -59,7 +59,9 @@ class VerticalLiftOperationTransfer(models.AbstractModel): _inherit = "vertical.lift.operation.base" _description = "Vertical Lift Operation - Transfer" - current_move_line_id = fields.Many2one(comodel_name="stock.move.line") + current_move_line_id = fields.Many2one( + comodel_name="stock.move.line", readonly=True + ) number_of_ops = fields.Integer( compute="_compute_number_of_ops", string="Number of Operations" @@ -217,7 +219,6 @@ class VerticalLiftOperationTransfer(models.AbstractModel): def on_screen_open(self): """Called when the screen is open""" - self.select_next_move_line() def button_release(self): """Release the operation, go to the next""" @@ -245,20 +246,3 @@ class VerticalLiftOperationTransfer(models.AbstractModel): def fetch_tray(self): return - - def select_next_move_line(self): - self.ensure_one() - next_move_line = self.env["stock.move.line"].search( - self._domain_move_lines_to_do(), limit=1 - ) - self.current_move_line_id = next_move_line - # TODO use a state machine to define next steps and - # description? - descr = ( - _("Scan New Destination Location") - if next_move_line - else _("No operations") - ) - self.operation_descr = descr - if next_move_line: - self.fetch_tray() diff --git a/stock_vertical_lift/models/vertical_lift_operation_pick.py b/stock_vertical_lift/models/vertical_lift_operation_pick.py index 25bd40173..030541c82 100644 --- a/stock_vertical_lift/models/vertical_lift_operation_pick.py +++ b/stock_vertical_lift/models/vertical_lift_operation_pick.py @@ -51,3 +51,24 @@ class VerticalLiftOperationPick(models.Model): if line.state != "done": line.qty_done = line.product_qty line.move_id._action_done() + + def on_screen_open(self): + """Called when the screen is open""" + self.select_next_move_line() + + def select_next_move_line(self): + self.ensure_one() + next_move_line = self.env["stock.move.line"].search( + self._domain_move_lines_to_do(), limit=1 + ) + self.current_move_line_id = next_move_line + # TODO use a state machine to define next steps and + # description? + descr = ( + _("Scan New Destination Location") + if next_move_line + else _("No operations") + ) + self.operation_descr = descr + if next_move_line: + self.fetch_tray() diff --git a/stock_vertical_lift/models/vertical_lift_operation_put.py b/stock_vertical_lift/models/vertical_lift_operation_put.py index 92903fb01..694ca30f9 100644 --- a/stock_vertical_lift/models/vertical_lift_operation_put.py +++ b/stock_vertical_lift/models/vertical_lift_operation_put.py @@ -1,7 +1,8 @@ # Copyright 2019 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import _, exceptions, models +from odoo import _, fields, models +from odoo.osv import expression class VerticalLiftOperationPut(models.Model): @@ -9,27 +10,227 @@ class VerticalLiftOperationPut(models.Model): _inherit = "vertical.lift.operation.transfer" _description = "Vertical Lift Operation Put" - def _domain_move_lines_to_do(self): - # TODO check domain - domain = [ - ("state", "=", "assigned"), - ("location_dest_id", "child_of", self.location_id.id), - ] - return domain + operation_line_ids = fields.One2many( + comodel_name="vertical.lift.operation.put.line", + inverse_name="operation_id", + readonly=True, + ) + current_operation_line_id = fields.Many2one( + comodel_name="vertical.lift.operation.put.line", readonly=True + ) + current_move_line_id = fields.Many2one( + related="current_operation_line_id.move_line_id", readonly=True + ) + # TODO think about moving the "steps" to the base model, + # integrate 'save' and 'release' in 'next_step()', use states + # in 'pick' as well + state = fields.Selection( + selection=[ + ("scan_product", "Scan Product"), + ("scan_tray_type", "Scan Tray Type"), + ("save", "Save"), + ("release", "Release"), + ], + default="scan_product", + ) - def _domain_move_lines_to_do_all(self): - shuttle_locations = self.env["stock.location"].search( - [("vertical_lift_kind", "=", "view")] + next_operation = object() + + def _transitions(self): + return { + "scan_product": "scan_tray_type", + "scan_tray_type": "save", + "save": "release", + "release": self.next_operation, + } + + # The steps cannot be in 'vertical.lift.operation.put.line' because the + # state has to be modified by on_barcode_scanned. As this method is an + # onchange underneath, it has to be on the same model. + def step(self): + return self.state + + def next_step(self): + next_state = self._transitions().get(self.state) + if next_state is not self.next_operation: + self.state = next_state + self.update_step_description() + + def step_description(self): + state_field = self._fields["state"] + return state_field.convert_to_export(self.state, self) + + def reset_steps(self): + self.state = "scan_product" + self.update_step_description() + + def count_move_lines_to_do(self): + """Count move lines to process in current shuttle""" + self.ensure_one() + return self.env["vertical.lift.operation.put.line"].search_count( + [("operation_id", "=", self.id)] ) - domain = [ - # TODO check state - ("state", "=", "assigned"), - ("location_dest_id", "child_of", shuttle_locations.ids), - ] - return domain + + def count_move_lines_to_do_all(self): + """Count move lines to process in all shuttles""" + self.ensure_one() + return self.env["vertical.lift.operation.put.line"].search_count([]) + + def on_barcode_scanned(self, barcode): + self.ensure_one() + operation_line = self.current_operation_line_id + if operation_line: + if self.step() == "scan_product": + if self._check_product(barcode): + self.next_step() + if self.step() == "scan_tray_type": + if self._check_tray_type(barcode): + self.next_step() + + def _check_product(self, barcode): + return barcode == self.current_move_line_id.product_id.barcode + + def _check_tray_type(self, barcode): + location = self.current_move_line_id.location_dest_id + tray_type = location.cell_in_tray_type_id + return barcode == tray_type.code + + def update_step_description(self): + if self.current_operation_line_id: + descr = self.step_description() + else: + descr = _("No operations") + self.operation_descr = descr def fetch_tray(self): self.current_move_line_id.fetch_vertical_lift_tray_dest() def process_current(self): - raise exceptions.UserError(_("Put workflow not implemented")) + self.current_operation_line_id.process() + + def button_release(self): + self.write( + {"operation_line_ids": [(2, self.current_operation_line_id.id)]} + ) + return super().button_release() + + def button_save(self): + if not (self and self.current_operation_line_id): + return + self.ensure_one() + self.process_current() + self.next_step() + + def on_screen_open(self): + """Called when the screen is open""" + if self.operation_line_ids: + self.select_next_move_line() + else: + return self.action_select_operations() + + def select_next_move_line(self): + self.ensure_one() + next_operation = fields.first(self.operation_line_ids) + self.current_operation_line_id = next_operation + self.reset_steps() + if next_operation: + self.fetch_tray() + + def action_select_operations(self): + self.ensure_one() + menu_xmlid = ( + "stock_vertical_lift." "vertical_lift_operation_put_select_view" + ) + select_model = self.env["vertical.lift.operation.put.select"] + select = select_model.create( + { + "operation_id": self.id, + "move_line_ids": [ + (6, 0, self.mapped("operation_line_ids.move_line_id.id")) + ], + } + ) + return { + "type": "ir.actions.act_window", + "res_model": "vertical.lift.operation.put.select", + "views": [[self.env.ref(menu_xmlid).id, "form"]], + "name": _("Scan Operations"), + "target": "new", + "res_id": select.id, + } + + +class VerticalLiftOperationPutLine(models.Model): + _name = "vertical.lift.operation.put.line" + _description = "Vertical Lift Operation Put Line" + + operation_id = fields.Many2one( + comodel_name="vertical.lift.operation.put", + required=True, + readonly=True, + ) + move_line_id = fields.Many2one( + comodel_name="stock.move.line", readonly=True + ) + + def process(self): + line = self.move_line_id + if line.state != "done": + line.qty_done = line.product_qty + line.move_id._action_done() + + +class VerticalLiftOperationPutSelect(models.TransientModel): + _name = "vertical.lift.operation.put.select" + _inherit = "barcodes.barcode_events_mixin" + _description = "Vertical Lift Operation Put Select" + + operation_id = fields.Many2one( + comodel_name="vertical.lift.operation.put", + required=True, + readonly=True, + ) + move_line_ids = fields.Many2many(comodel_name="stock.move.line") + + def _sync_lines(self): + self.operation_id.operation_line_ids.unlink() + operation_line_model = self.env["vertical.lift.operation.put.line"] + operation_line_model.create( + [ + { + "operation_id": self.operation_id.id, + "move_line_id": move_line.id, + } + for move_line in self.move_line_ids + ] + ) + self.operation_id.select_next_move_line() + + def action_validate(self): + self._sync_lines() + return {"type": "ir.actions.act_window_close"} + + def _move_line_domain(self): + return [ + ("state", "=", "assigned"), + ("location_dest_id", "child_of", self.operation_id.location_id.id), + ] + + def action_add_all(self): + move_lines = self.env["stock.move.line"].search( + self._move_line_domain() + ) + self.move_line_ids = move_lines + self._sync_lines() + return {"type": "ir.actions.act_window_close"} + + def on_barcode_scanned(self, barcode): + self.ensure_one() + domain = self._move_line_domain() + domain = expression.AND( + [domain, [("product_id.barcode", "=", barcode)]] + ) + move_lines = self.env["stock.move.line"].search(domain) + # note: on_barcode_scanned is called in an onchange, so 'self' + # is a NewID, we can't use 'write()' on it. + self.move_line_ids |= move_lines diff --git a/stock_vertical_lift/security/ir.model.access.csv b/stock_vertical_lift/security/ir.model.access.csv index 2a69468e4..63741764a 100644 --- a/stock_vertical_lift/security/ir.model.access.csv +++ b/stock_vertical_lift/security/ir.model.access.csv @@ -3,4 +3,5 @@ access_vertical_lift_shuttle_stock_user,access_vertical_lift_shuttle stock user, access_vertical_lift_shuttle_manager,access_vertical_lift_shuttle stock manager,model_vertical_lift_shuttle,stock.group_stock_manager,1,1,1,1 access_vertical_lift_operation_pick_stock_user,access_vertical_lift_operation_pick stock user,model_vertical_lift_operation_pick,stock.group_stock_user,1,1,1,1 access_vertical_lift_operation_put_stock_user,access_vertical_lift_operation_put stock user,model_vertical_lift_operation_put,stock.group_stock_user,1,1,1,1 +access_vertical_lift_operation_put_line_stock_user,access_vertical_lift_operation_put_line stock user,model_vertical_lift_operation_put_line,stock.group_stock_user,1,1,1,1 access_vertical_lift_operation_inventory_stock_user,access_vertical_lift_operation_inventory stock user,model_vertical_lift_operation_inventory,stock.group_stock_user,1,1,1,1 diff --git a/stock_vertical_lift/static/src/scss/vertical_lift.scss b/stock_vertical_lift/static/src/scss/vertical_lift.scss index 11678e055..8cd7878aa 100644 --- a/stock_vertical_lift/static/src/scss/vertical_lift.scss +++ b/stock_vertical_lift/static/src/scss/vertical_lift.scss @@ -109,7 +109,18 @@ } } - .o_vlift_shuttle_manual_barcode { + .o_vlift_shuttle_popup { + + table tr { + line-height: 3; + font-size: 1.1em; + + .o_list_record_remove { + width: 50px; + text-align: center; + } + } + .o_field_char { padding: 1em; font-size: 2em; @@ -120,5 +131,12 @@ font-size: 2em; text-transform: uppercase; } + + } + + footer .btn { + padding: 1em; + font-size: 2em; + text-transform: uppercase; } } diff --git a/stock_vertical_lift/tests/__init__.py b/stock_vertical_lift/tests/__init__.py index c73943e06..dd10460b3 100644 --- a/stock_vertical_lift/tests/__init__.py +++ b/stock_vertical_lift/tests/__init__.py @@ -1,2 +1,4 @@ from . import test_location -from . import test_vertical_lift_shuttle +from . import test_inventory +from . import test_pick +from . import test_put diff --git a/stock_vertical_lift/tests/common.py b/stock_vertical_lift/tests/common.py index 811121cae..f40456ffc 100644 --- a/stock_vertical_lift/tests/common.py +++ b/stock_vertical_lift/tests/common.py @@ -1,6 +1,7 @@ # Copyright 2019 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import _ from odoo.addons.stock_location_tray.tests import common @@ -14,6 +15,9 @@ class VerticalLiftCase(common.LocationTrayTypeCase): cls.product_socks = cls.env.ref( 'stock_vertical_lift.product_running_socks' ) + cls.product_recovery = cls.env.ref( + 'stock_vertical_lift.product_recovery_socks' + ) cls.vertical_lift_loc = cls.env.ref( 'stock_vertical_lift.stock_location_vertical_lift' ) @@ -47,3 +51,51 @@ class VerticalLiftCase(common.LocationTrayTypeCase): ], } ) + + @classmethod + def _create_simple_picking_in(cls, product, quantity, dest_location): + supplier_loc = cls.env.ref('stock.stock_location_suppliers') + picking_type = cls.env.ref('stock.picking_type_in') + partner = cls.env.ref('base.res_partner_1') + return cls.env['stock.picking'].create( + { + 'picking_type_id': picking_type.id, + 'partner_id': partner.id, + 'location_id': supplier_loc.id, + 'location_dest_id': dest_location.id, + 'move_lines': [ + ( + 0, + 0, + { + 'name': product.name, + 'product_id': product.id, + 'product_uom': product.uom_id.id, + 'product_uom_qty': quantity, + 'picking_type_id': picking_type.id, + 'location_id': supplier_loc.id, + 'location_dest_id': dest_location.id, + }, + ) + ], + } + ) + + def _test_button_release(self, move_line): + # for the test, we'll consider our last line has been delivered + move_line.qty_done = move_line.product_qty + move_line.move_id._action_done() + # release, no further operation in queue + operation = self.shuttle._operation_for_mode() + result = operation.button_release() + self.assertFalse(operation.current_move_line_id) + self.assertEqual(operation.operation_descr, _("No operations")) + expected_result = { + "effect": { + "fadeout": "slow", + "message": _("Congrats, you cleared the queue!"), + "img_url": "/web/static/src/img/smile.svg", + "type": "rainbow_man", + } + } + self.assertEqual(result, expected_result) diff --git a/stock_vertical_lift/tests/test_inventory.py b/stock_vertical_lift/tests/test_inventory.py new file mode 100644 index 000000000..864c022ba --- /dev/null +++ b/stock_vertical_lift/tests/test_inventory.py @@ -0,0 +1,31 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +import unittest + +from .common import VerticalLiftCase + + +class TestInventory(VerticalLiftCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.picking_out = cls.env.ref( + "stock_vertical_lift.stock_picking_out_demo_vertical_lift_1" + ) + # we have a move line to pick created by demo picking + # stock_picking_out_demo_vertical_lift_1 + cls.out_move_line = cls.picking_out.move_line_ids + + def test_switch_inventory(self): + self.shuttle.switch_inventory() + self.assertEqual(self.shuttle.mode, "inventory") + + @unittest.skip("Not implemented") + def test_inventory_count_move_lines(self): + pass + + @unittest.skip("Not implemented") + def test_process_current_inventory(self): + # test to implement when the code is implemented + self.shuttle.switch_inventory() diff --git a/stock_vertical_lift/tests/test_vertical_lift_shuttle.py b/stock_vertical_lift/tests/test_pick.py similarity index 80% rename from stock_vertical_lift/tests/test_vertical_lift_shuttle.py rename to stock_vertical_lift/tests/test_pick.py index b80e46c1e..894eb5fa6 100644 --- a/stock_vertical_lift/tests/test_vertical_lift_shuttle.py +++ b/stock_vertical_lift/tests/test_pick.py @@ -1,14 +1,12 @@ # Copyright 2019 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -import unittest - -from odoo import _, exceptions +from odoo import _ from .common import VerticalLiftCase -class TestVerticalLiftTrayType(VerticalLiftCase): +class TestPick(VerticalLiftCase): @classmethod def setUpClass(cls): super().setUpClass() @@ -27,19 +25,6 @@ class TestVerticalLiftTrayType(VerticalLiftCase): self.out_move_line, ) - def test_switch_put(self): - self.shuttle.switch_put() - self.assertEqual(self.shuttle.mode, "put") - # TODO check that we have an incoming move when switching - self.assertEqual( - self.shuttle._operation_for_mode().current_move_line_id, - self.env["stock.move.line"].browse(), - ) - - def test_switch_inventory(self): - self.shuttle.switch_inventory() - self.assertEqual(self.shuttle.mode, "inventory") - def test_pick_action_open_screen(self): self.shuttle.switch_pick() action = self.shuttle.action_open_screen() @@ -170,37 +155,21 @@ class TestVerticalLiftTrayType(VerticalLiftCase): self.assertEqual(operation1.number_of_ops_all, 6) self.assertEqual(operation2.number_of_ops_all, 6) - @unittest.skip("Not implemented") - def test_put_count_move_lines(self): - pass - - @unittest.skip("Not implemented") - def test_inventory_count_move_lines(self): - pass - - @unittest.skip("Not implemented") def test_on_barcode_scanned(self): - # test to implement when the code is implemented - pass + self.shuttle.switch_pick() + operation = self.shuttle._operation_for_mode() + move_line = operation.current_move_line_id + current_destination = move_line.location_dest_id + stock_location = self.env.ref("stock.stock_location_stock") + self.assertEqual( + current_destination, self.env.ref("stock.stock_location_customers") + ) + operation.on_barcode_scanned(stock_location.barcode) + self.assertEqual(move_line.location_dest_id, stock_location) def test_button_release(self): - # for the test, we'll consider our last line has been delivered - self.out_move_line.qty_done = self.out_move_line.product_qty - self.out_move_line.move_id._action_done() - # release, no further operation in queue - operation = self.shuttle._operation_for_mode() - result = operation.button_release() - self.assertFalse(operation.current_move_line_id) - self.assertEqual(operation.operation_descr, _("No operations")) - expected_result = { - "effect": { - "fadeout": "slow", - "message": _("Congrats, you cleared the queue!"), - "img_url": "/web/static/src/img/smile.svg", - "type": "rainbow_man", - } - } - self.assertEqual(result, expected_result) + self.shuttle.switch_pick() + self._test_button_release(self.out_move_line) def test_process_current_pick(self): self.shuttle.switch_pick() @@ -211,17 +180,6 @@ class TestVerticalLiftTrayType(VerticalLiftCase): self.assertEqual(self.out_move_line.state, "done") self.assertEqual(self.out_move_line.qty_done, qty_to_process) - def test_process_current_put(self): - # test to implement when the code is implemented - self.shuttle.switch_put() - operation = self.shuttle._operation_for_mode() - with self.assertRaises(exceptions.UserError): - operation.process_current() - - def test_process_current_inventory(self): - # test to implement when the code is implemented - self.shuttle.switch_inventory() - def test_matrix(self): self.shuttle.switch_pick() operation = self.shuttle._operation_for_mode() diff --git a/stock_vertical_lift/tests/test_put.py b/stock_vertical_lift/tests/test_put.py new file mode 100644 index 000000000..f13038906 --- /dev/null +++ b/stock_vertical_lift/tests/test_put.py @@ -0,0 +1,190 @@ +# Copyright 2019 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from .common import VerticalLiftCase + + +class TestPut(VerticalLiftCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.picking_in = cls.env.ref( + "stock_vertical_lift.stock_picking_in_demo_vertical_lift_1" + ) + cls.picking_in.action_confirm() + cls.in_move_line = cls.picking_in.move_line_ids + cls.location_1a_x1y1 = cls.env.ref( + "stock_vertical_lift." + "stock_location_vertical_lift_demo_tray_1a_x1y1" + ) + cls.location_1a_x2y1 = cls.env.ref( + "stock_vertical_lift." + "stock_location_vertical_lift_demo_tray_1a_x2y1" + ) + cls.location_1a_x3y1 = cls.env.ref( + "stock_vertical_lift." + "stock_location_vertical_lift_demo_tray_1a_x3y1" + ) + cls.location_1a_x1y2 = cls.env.ref( + "stock_vertical_lift." + "stock_location_vertical_lift_demo_tray_1a_x1y2" + ) + cls.in_move_line.location_dest_id = cls.location_1a_x3y1 + + def _select_move_lines(self, shuttle, move_lines=None): + select_model = self.env["vertical.lift.operation.put.select"] + operation = shuttle._operation_for_mode() + select = select_model.create({"operation_id": operation.id}) + if move_lines: + select.move_line_ids = [(6, 0, move_lines.ids)] + else: + select.action_add_all() + select._sync_lines() + + def test_put_action_open_screen(self): + self.shuttle.switch_put() + action = self.shuttle.action_open_screen() + operation = self.shuttle._operation_for_mode() + self.assertEqual(action["type"], "ir.actions.act_window") + self.assertEqual(action["res_model"], "vertical.lift.operation.put") + self.assertEqual(action["res_id"], operation.id) + + def test_switch_put(self): + self.shuttle.switch_put() + self.assertEqual(self.shuttle.mode, "put") + self.assertEqual( + self.shuttle._operation_for_mode().current_move_line_id, + self.env["stock.move.line"].browse(), + ) + + def test_select_from_barcode(self): + self.shuttle.switch_put() + self.picking_in.action_cancel() + put1 = self._create_simple_picking_in( + self.product_socks, 10, self.location_1a_x1y1 + ) + put1.action_confirm() + put2 = self._create_simple_picking_in( + self.product_recovery, 10, self.location_1a_x2y1 + ) + put2.action_confirm() + select_model = self.env["vertical.lift.operation.put.select"] + operation = self.shuttle._operation_for_mode() + select = select_model.create({"operation_id": operation.id}) + select.on_barcode_scanned(self.product_socks.barcode) + self.assertRecordValues( + select, [{"move_line_ids": put1.move_line_ids.ids}] + ) + select.on_barcode_scanned(self.product_recovery.barcode) + self.assertRecordValues( + select, + [{"move_line_ids": (put1.move_line_ids | put2.move_line_ids).ids}], + ) + select.action_validate() + self.assertEqual(len(operation.operation_line_ids), 2) + self.assertRecordValues( + operation.mapped("operation_line_ids"), + [ + {"move_line_id": put1.move_line_ids.id}, + {"move_line_id": put2.move_line_ids.id}, + ], + ) + + def test_no_select_from_barcode_outside_location(self): + self.shuttle.switch_put() + self.picking_in.action_cancel() + location = self.env.ref("stock.location_refrigerator_small") + put1 = self._create_simple_picking_in(self.product_socks, 10, location) + put1.action_confirm() + select_model = self.env["vertical.lift.operation.put.select"] + operation = self.shuttle._operation_for_mode() + select = select_model.create({"operation_id": operation.id}) + select.on_barcode_scanned(self.product_socks.barcode) + # the move line is outside of the vertical lift, should not be + # selected + self.assertRecordValues(select, [{"move_line_ids": []}]) + + def test_put_count_move_lines(self): + self.shuttle.switch_put() + self.picking_in.action_cancel() + location_2a_x1y1 = self.env.ref( + "stock_vertical_lift." + "stock_location_vertical_lift_demo_tray_2a_x1y1" + ) + put1 = self._create_simple_picking_in( + self.product_socks, 10, self.location_1a_x1y1 + ) + put1.action_confirm() + put2 = self._create_simple_picking_in( + self.product_recovery, 10, self.location_1a_x2y1 + ) + put2.action_confirm() + put3 = self._create_simple_picking_in( + self.product_recovery, 10, location_2a_x1y1 + ) + put3.action_confirm() + operation = self.shuttle._operation_for_mode() + self._select_move_lines(self.shuttle) + shuttle2 = self.env.ref( + "stock_vertical_lift.stock_vertical_lift_demo_shuttle_2" + ) + shuttle2.switch_put() + operation2 = shuttle2._operation_for_mode() + self._select_move_lines(shuttle2) + + self.assertEqual(operation.number_of_ops, 2) + self.assertEqual(operation.number_of_ops_all, 3) + self.assertEqual(operation2.number_of_ops, 1) + self.assertEqual(operation2.number_of_ops_all, 3) + + def test_process_current_put(self): + self.shuttle.switch_put() + operation = self.shuttle._operation_for_mode() + self._select_move_lines(self.shuttle, self.in_move_line) + self.assertEqual(operation.current_move_line_id, self.in_move_line) + qty_to_process = self.in_move_line.product_qty + operation.process_current() + self.assertEqual(self.in_move_line.state, "done") + self.assertEqual(self.in_move_line.qty_done, qty_to_process) + + def test_transition_reset(self): + self.shuttle.switch_put() + operation = self.shuttle._operation_for_mode() + self._select_move_lines(self.shuttle, self.in_move_line) + operation.state = "scan_tray_type" + operation.reset_steps() + self.assertEqual(operation.step(), "scan_product") + + def test_transition_scan_product(self): + self.shuttle.switch_put() + operation = self.shuttle._operation_for_mode() + self._select_move_lines(self.shuttle, self.in_move_line) + operation.state = "scan_product" + # wrong barcode, nothing happens + operation.on_barcode_scanned("foo") + # product scanned, move to next step + operation.on_barcode_scanned(self.product_socks.barcode) + self.assertEqual(operation.step(), "scan_tray_type") + + def test_transition_scan_tray_type(self): + self.shuttle.switch_put() + operation = self.shuttle._operation_for_mode() + self._select_move_lines(self.shuttle, self.in_move_line) + operation.state = "scan_tray_type" + # wrong barcode, nothing happens + operation.on_barcode_scanned("foo") + # tray type scanned, move to next step + operation.on_barcode_scanned(operation.tray_type_id.code) + self.assertEqual(operation.step(), "save") + + def test_transition_save(self): + self.shuttle.switch_put() + operation = self.shuttle._operation_for_mode() + self._select_move_lines(self.shuttle, self.in_move_line) + operation.state = "save" + operation.button_save() + self.assertEqual(operation.step(), "release") + + def test_transition_button_release(self): + self.shuttle.switch_put() + self._test_button_release(self.in_move_line) diff --git a/stock_vertical_lift/views/vertical_lift_operation_base_views.xml b/stock_vertical_lift/views/vertical_lift_operation_base_views.xml index 876e6012a..60457ce93 100644 --- a/stock_vertical_lift/views/vertical_lift_operation_base_views.xml +++ b/stock_vertical_lift/views/vertical_lift_operation_base_views.xml @@ -40,7 +40,7 @@
-
+