Add Put-Away workflow

This commit is contained in:
Guewen Baconnier
2019-10-10 17:14:38 +02:00
parent 178b45604f
commit bcfb269e2f
15 changed files with 636 additions and 97 deletions

View File

@@ -3,6 +3,7 @@
<record id="product_running_socks" model="product.product">
<field name="default_code">RS200</field>
<field name="barcode">4491673293664</field>
<field name="name">Running Socks</field>
<field name="type">product</field>
<field name="categ_id" ref="product.product_category_6"/>
@@ -17,6 +18,7 @@
<record id="product_recovery_socks" model="product.product">
<field name="default_code">RS300</field>
<field name="barcode">2779891103531</field>
<field name="name">Recovery Socks</field>
<field name="type">product</field>
<field name="categ_id" ref="product.product_category_6"/>

View File

@@ -27,4 +27,26 @@
<value model="stock.picking" eval="[obj().env.ref('stock_vertical_lift.stock_picking_out_demo_vertical_lift_1').id]"/>
</function>
<record id="stock_picking_in_demo_vertical_lift_1" model="stock.picking">
<field name="picking_type_id" ref="stock.picking_type_in"/>
<field name="origin">Incoming shipment from Vertical Lift (demo)</field>
<field name="partner_id" ref="base.res_partner_1"/>
<field name="date" eval="DateTime.today()"/>
<field name="location_id" ref="stock.stock_location_suppliers"/>
<field name="location_dest_id" ref="stock.stock_location_stock"/>
<field name="move_lines" model="stock.move" eval="[(0, 0, {
'name': obj().env.ref('stock_vertical_lift.product_running_socks').name,
'product_id': ref('stock_vertical_lift.product_running_socks'),
'product_uom': ref('uom.product_uom_unit'),
'product_uom_qty': 15.0,
'picking_type_id': ref('stock.picking_type_in'),
'location_id': ref('stock.stock_location_suppliers'),
'location_dest_id': ref('stock.stock_location_stock'),
})]"/>
</record>
<function model="stock.picking" name="action_confirm">
<value model="stock.picking" eval="[obj().env.ref('stock_vertical_lift.stock_picking_in_demo_vertical_lift_1').id]"/>
</function>
</odoo>

View File

@@ -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()

View File

@@ -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()

View File

@@ -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

View File

@@ -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
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
3 access_vertical_lift_shuttle_manager access_vertical_lift_shuttle stock manager model_vertical_lift_shuttle stock.group_stock_manager 1 1 1 1
4 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
5 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
6 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
7 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

View File

@@ -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;
}
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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()

View File

@@ -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()

View File

@@ -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)

View File

@@ -40,7 +40,7 @@
</div>
</div>
<div class="o_shuttle_actions">
<div class="o_shuttle_content">
<div class="o_shuttle_content o_shuttle_content_left">
<button name="action_menu" type="object"
class="btn-secondary o_shuttle_icon_btn"
string="" icon="fa-bars"

View File

@@ -23,6 +23,63 @@
<form position="attributes">
<attribute name="string">Put-Away Screen</attribute>
</form>
<xpath expr="//div[hasclass('o_shuttle_actions')]/div[hasclass('o_shuttle_content_left')]"
position="inside" >
<button name="action_select_operations" type="object"
class="btn-primary"
string="Select Operations"
aria-label="Select Operations"
title="Select Operations"/>
</xpath>
<field name="_barcode_scanned" position="before">
<!-- these fields have to be in the view otherwise they
would be empty in the record sent to the _barcode_scanned
onchange method
-->
<field name="state" invisible="1"/>
<field name="current_operation_line_id" invisible="1"/>
</field>
</field>
</record>
<record id="vertical_lift_operation_put_select_view" model="ir.ui.view">
<field name="name">vertical.lift.operation.put.select.view</field>
<field name="model">vertical.lift.operation.put.select</field>
<field name="arch" type="xml">
<form string="Select" class="o_vlift_shuttle_popup">
<field name="move_line_ids">
<tree create="0" edit="0">
<field name="product_id" />
</tree>
<form>
<group>
<field name="product_id" readonly="1" />
<field name="product_uom_qty" readonly="1" />
<field name="qty_done" readonly="1" />
</group>
</form>
</field>
<!-- this field has to be in the view otherwise it
would be empty in the record sent to the _barcode_scanned
onchange method
-->
<field name="operation_id" invisible="1" />
<field name="_barcode_scanned" widget="barcode_handler"/>
<footer>
<button name="action_validate"
string="Validate"
type="object"
class="btn-primary"/>
<button string="Cancel"
class="btn-secondary"
special="cancel" />
<button name="action_add_all"
string="Add all"
type="object"
groups="base.group_no_one"
class="btn-secondary"/>
</footer>
</form>
</field>
</record>

View File

@@ -37,7 +37,7 @@
<field name="model">vertical.lift.shuttle.manual.barcode</field>
<field name="arch" type="xml">
<form string="Shuttle">
<div class="row o_vlift_shuttle_manual_barcode">
<div class="row o_vlift_shuttle_popup">
<div class="col-8">
<field name="barcode"/>
</div>