Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion shopfloor_reception/data/shopfloor_scenario_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"auto_post_line": true,
"allow_return": true,
"scan_location_or_pack_first": true,
"allow_filter_today_scheduled_pickings": true
"allow_filter_today_scheduled_pickings": true,
"allow_reception_display_move_lines": false
}
</field>
</record>
Expand Down
34 changes: 34 additions & 0 deletions shopfloor_reception/models/shopfloor_menu.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@
pickings with the ones that are scheduled for today.
"""

RECEPTION_DISPLAY_MOVE_LINES_HELP = """
By default, at first step, stock moves are displayed
for a given picking. Checking this box allows to
manage stock move lines instead.
"""


class ShopfloorMenu(models.Model):
_inherit = "shopfloor.menu"
Expand All @@ -19,13 +25,41 @@ class ShopfloorMenu(models.Model):
help=FILTER_TODAY_SCHEDULED_PICKINGS_HELP,
)

# TODO: This should be removed in further version as
# we should always use move lines instead of moves.
# Keep it for compatibility with existings installs.
reception_display_move_lines_is_possible = fields.Boolean(
compute="_compute_reception_display_move_lines_is_possible",
)
reception_display_move_lines = fields.Boolean(
compute="_compute_reception_display_move_lines",
store=True,
readonly=False,
default=False,
help=RECEPTION_DISPLAY_MOVE_LINES_HELP,
)

@api.depends("scenario_id")
def _compute_filter_today_scheduled_pickings_is_possible(self):
for menu in self:
menu.filter_today_scheduled_pickings_is_possible = bool(
menu.scenario_id.has_option("allow_filter_today_scheduled_pickings")
)

@api.depends("scenario_id")
def _compute_reception_display_move_lines_is_possible(self):
for menu in self:
menu.reception_display_move_lines_is_possible = bool(
menu.scenario_id.has_option("allow_reception_display_move_lines")
)

@api.depends("reception_display_move_lines_is_possible")
def _compute_reception_display_move_lines(self):
for menu in self:
menu.reception_display_move_lines = (
menu.reception_display_move_lines_is_possible
)

@api.onchange("filter_today_scheduled_pickings_is_possible")
def onchange_filter_today_scheduled_pickings_is_possible(self):
self.filter_today_scheduled_pickings = (
Expand Down
43 changes: 38 additions & 5 deletions shopfloor_reception/services/reception.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ def _get_today_start_end_datetime(self, naive=True):
def filter_today_scheduled_pickings(self):
return self.work.menu.filter_today_scheduled_pickings

@property
def reception_display_move_lines(self):
return self.work.menu.reception_display_move_lines

# DOMAIN METHODS

def _domain_move_line_by_packaging(self, packaging):
Expand Down Expand Up @@ -150,7 +154,12 @@ def _select_picking(self, picking):
return self._response_for_select_move(picking)

def _response_for_select_move(self, picking, message=None):
data = {"picking": self._data_for_stock_picking(picking, with_lines=True)}
if self.reception_display_move_lines:
data = {
"picking": self._data_for_stock_picking(picking, with_move_lines=True)
}
else:
data = {"picking": self._data_for_stock_picking(picking, with_lines=True)}
return self._response(next_state="select_move", data=data, message=message)

def _response_for_confirm_done(self, picking, message=None):
Expand Down Expand Up @@ -269,7 +278,7 @@ def _scan_line__find_or_create_line(self, picking, move, qty_done=1):
line = None
unassigned_lines = self.env["stock.move.line"]
for move_line in move.move_line_ids:
if move_line.result_package_id:
if move_line.progress == 100.0 or move_line.result_package_id:
continue
if move_line.shopfloor_user_id.id == self.env.uid:
line = move_line
Expand Down Expand Up @@ -501,8 +510,11 @@ def _scan_line__by_packaging(self, picking, packaging):
def _scan_line__by_lot(self, picking, lot):
lines = picking.move_line_ids.filtered(
lambda l: (
lot == l.lot_id
or (lot.name == l.lot_name and lot.product_id == l.product_id)
(
lot == l.lot_id
or (lot.name == l.lot_name and lot.product_id == l.product_id)
)
and not l.progress == 100.0
and not l.result_package_id
)
)
Expand Down Expand Up @@ -714,12 +726,18 @@ def _assign_user_to_line(self, line):

# DATA METHODS

def _data_for_stock_picking(self, picking, with_lines=False, **kw):
def _data_for_stock_picking(
self, picking, with_lines=False, with_move_lines=False, **kw
):
if "with_progress" not in kw:
kw["with_progress"] = True
data = self.data.picking(picking, **kw)
if with_lines:
data.update({"moves": self._data_for_moves(picking.move_ids)})
if with_move_lines:
data.update(
{"move_lines": self._data_for_move_lines(picking.move_line_ids)}
)
return data

def _data_for_stock_pickings(self, pickings, with_lines=False):
Expand Down Expand Up @@ -974,6 +992,11 @@ def manual_select_move(self, move_id):
picking = move.picking_id
return self._scan_line__find_or_create_line(picking, move)

def manual_select_move_line(self, move_line_id):
move_line = self.env["stock.move.line"].browse(move_line_id)
picking = move_line.picking_id
return self._scan_line__assign_user(picking, move_line, move_line.qty_done)

def done_action(self, picking_id, confirmation=False):
"""Mark a picking as done

Expand Down Expand Up @@ -1473,6 +1496,11 @@ def manual_select_move(self):
"move_id": {"required": True, "type": "integer"},
}

def manual_select_move_line(self):
return {
"move_line_id": {"required": True, "type": "integer"},
}

def set_lot(self):
return {
"picking_id": {"coerce": to_int, "required": True, "type": "integer"},
Expand Down Expand Up @@ -1777,6 +1805,11 @@ def _schema_stock_picking_with_lines(self, lines_with_packaging=False):
# instead of this method.
schema = self.schemas.picking()
schema.update({"moves": self.schemas._schema_list_of(self.schemas.move())})
schema["moves"].update({"required": False})
schema.update(
{"move_lines": self.schemas._schema_list_of(self.schemas.move_line())}
)
schema["move_lines"].update({"required": False})
return schema

# ENDPOINTS
Expand Down
87 changes: 87 additions & 0 deletions shopfloor_reception/tests/test_select_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,93 @@ def test_scan_product(self):
},
)

def test_scan_product_partial(self):
# Scan a line
# Set a partial quantity done
# Try to scan the product again
# The selected line should be the other one
picking = self._create_picking()
lot = self._create_lot()
self.assertFalse(picking.printed)
selected_move_line = picking.move_line_ids.filtered(
lambda l: l.product_id == self.product_a
)

# Activate INPUT location
selected_move_line.location_dest_id.sudo().active = True

selected_move_line.lot_id = lot
response = self.service.dispatch(
"scan_line",
params={"picking_id": picking.id, "barcode": lot.name},
)
data = self.data.picking(picking)

self.assertTrue(selected_move_line.picking_id.printed)
self.assert_response(
response,
next_state="set_quantity",
data={
"picking": data,
"selected_move_line": self.data.move_lines(selected_move_line),
"confirmation_required": None,
},
)

selected_move_line.shopfloor_user_id = self.env.uid
response = self.service.dispatch(
"set_quantity",
params={
"picking_id": picking.id,
"selected_line_id": selected_move_line.id,
"quantity": 5.0,
},
)

response = self.service.dispatch(
"process_without_pack",
params={
"picking_id": picking.id,
"selected_line_id": selected_move_line.id,
"quantity": 5.0,
},
)
data = self.data.picking(picking)
self.assert_response(
response,
next_state="set_destination",
data={
"picking": data,
"selected_move_line": self.data.move_lines(selected_move_line),
},
)

response = self.service.dispatch(
"set_destination",
params={
"picking_id": picking.id,
"selected_line_id": selected_move_line.id,
"location_name": "INPUT",
},
)
self.assert_response(
response,
next_state="select_move",
data=self._data_for_select_move(picking),
)
lines = picking.move_line_ids.filtered(lambda l: l.product_id == self.product_a)
self.assertEqual(2, len(lines))
previous_line = selected_move_line

response = self.service.dispatch(
"scan_line",
params={"picking_id": picking.id, "barcode": lot.name},
)

self.assertNotEqual(
previous_line.id, response["data"]["set_lot"]["selected_move_line"][0]["id"]
)

def test_scan_packaging(self):
picking = self._create_picking()
self._add_package(picking)
Expand Down
11 changes: 10 additions & 1 deletion shopfloor_reception/views/shopfloor_menu.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@
/>
<field name="filter_today_scheduled_pickings" />
</group>
<group
name="reception_display_move_lines"
attrs="{'invisible': [('reception_display_move_lines_is_possible', '=', False)]}"
>
<field
name="reception_display_move_lines_is_possible"
invisible="1"
/>
<field name="reception_display_move_lines" />
</group>
</group>
</field>
</record>

</odoo>
20 changes: 18 additions & 2 deletions shopfloor_reception_mobile/static/src/scenario/reception.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,11 @@ const Reception = {
return this.state.data.selected_move_line[0] || {};
},
ordered_moves: function () {
const moves = _.result(this.state, "data.picking.moves", []);
// We can either have moves or move lines
var moves = _.result(this.state, "data.picking.moves", []);
if (_.isEmpty(moves)) {
moves = _.result(this.state, "data.picking.move_lines", []);
}
if (_.isEmpty(moves)) {
return;
}
Expand Down Expand Up @@ -354,6 +358,11 @@ const Reception = {
};
},
picking_detail_options_for_select_move: function () {
var move_lines = _.has(this.state, "data.picking.move_lines");
var qty_done_field = "quantity_done";
if (move_lines) {
qty_done_field = "qty_done";
}
return {
show_title: true,
showActions: false,
Expand All @@ -378,9 +387,16 @@ const Reception = {
label: "Vendor code",
},
{
path: "quantity_done",
path: qty_done_field,
label: "Qty done",
display_no_value: true,
renderer: function (rec, field) {
if (_.has(rec, "qty_done")) {
return rec.qty_done + " / " + rec.quantity;
} else {
return rec.quantity_done + " / " + rec.quantity;
}
},
},
],
},
Expand Down
19 changes: 14 additions & 5 deletions shopfloor_reception_mobile/static/src/scenario/reception_states.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,21 @@ export const reception_states = function () {
);
},
on_select: (selected) => {
this.wait_call(
this.odoo.call("manual_select_move", {
move_id: selected.id,
})
);
if (_.has(selected, "qty_done")) {
this.wait_call(
this.odoo.call("manual_select_move_line", {
move_line_id: selected.id,
})
);
} else {
this.wait_call(
this.odoo.call("manual_select_move", {
move_id: selected.id,
})
);
}
},

on_cancel: () => {
// TODO: this endpoing is currently missing in the backend,
// and it's currently in the roadmap.
Expand Down