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
6 changes: 6 additions & 0 deletions stock_release_channel/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ button is used, in addition to the state change, the system looks for pending
transfers requiring a release and try to assign them to a channel in the
"Open" or "Locked" state.

You can also use the "Sleep and Reassign" action on channels that have transfers
in progress. This is particularly useful if you don't want to use the current
channel and want to reassign pickings to another(s) channel(s). Before using that
action, you should check that channels for those pickings are open (as the transfers
could not be assigned if there is no corresponding channel found).

Bug Tracker
===========

Expand Down
1 change: 0 additions & 1 deletion stock_release_channel/models/stock_picking.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Copyright 2020 Camptocamp
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from odoo import _, exceptions, fields, models
from odoo.osv import expression

Expand Down
32 changes: 30 additions & 2 deletions stock_release_channel/models/stock_release_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,10 @@ class StockReleaseChannel(models.Model):
compute="_compute_is_action_wake_up_allowed",
help="Technical field to check if the " "action 'Wake Up' is allowed.",
)
is_action_safe_sleep_allowed = fields.Boolean(
compute="_compute_is_action_safe_sleep_allowed",
help="Technical field to check if the action 'Safe Sleep' is allowed.",
)
is_release_allowed = fields.Boolean(
compute="_compute_is_release_allowed",
search="_search_is_release_allowed",
Expand Down Expand Up @@ -270,6 +274,13 @@ def _compute_is_release_allowed(self):
for rec in self:
rec.is_release_allowed = rec.state == "open" and not rec.release_forbidden

@api.depends("state")
def _compute_is_action_safe_sleep_allowed(self):
for rec in self:
rec.is_action_safe_sleep_allowed = bool(
rec.state in ["locked", "open"] and rec.open_picking_ids
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think checking open_picking_ids is useful

)

def _compute_show_last_picking_done(self):
for rec in self:
rec.show_last_picking_done = (
Expand Down Expand Up @@ -872,16 +883,33 @@ def action_unlock(self):
self._check_is_action_unlock_allowed()
self.write({"state": "open"})

def action_sleep(self):
def _sleep(self, safe=False):
"""

Allows to make the channels asleep.

That will unassign all the related pickings,
unrelease them and then try to reassign them
Args:
safe (bool, optional): This will allow to not unrelease
the pickings that have related done pickings.
"""
self._check_is_action_sleep_allowed()
pickings_to_unassign = self.env["stock.picking"].search(
self._get_picking_to_unassign_domain()
)
pickings_to_unassign.write({"release_channel_id": False})
pickings_to_unassign.unrelease()
pickings_to_unassign.unrelease(safe_unrelease=safe)
self.write({"state": "asleep"})
pickings_to_unassign._delay_assign_release_channel()

def action_sleep(self):
self._sleep()

def action_safe_sleep(self):
self._sleep(safe=True)
return {"type": "ir.actions.act_window_close"}
Comment on lines +906 to +911
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would not add a second button but manage it in existing action_sleep.
Test if there are done internal transfer.
If True, raise a wizard popup showing them and asking to confirm the reassignment of those to another channel. on confirm, call self._sleep(safe=True).
Else call self._sleep()


def action_wake_up(self):
self._check_is_action_wake_up_allowed()
for rec in self:
Expand Down
6 changes: 6 additions & 0 deletions stock_release_channel/readme/USAGE.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ A asleep channel can be waken up by using the "Wake up" button. When the "Wake u
button is used, in addition to the state change, the system looks for pending
transfers requiring a release and try to assign them to a channel in the
"Open" or "Locked" state.

You can also use the "Sleep and Reassign" action on channels that have transfers
in progress. This is particularly useful if you don't want to use the current
channel and want to reassign pickings to another(s) channel(s). Before using that
action, you should check that channels for those pickings are open (as the transfers
could not be assigned if there is no corresponding channel found).
5 changes: 5 additions & 0 deletions stock_release_channel/static/description/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,11 @@ <h1><a class="toc-backref" href="#toc-entry-2">Usage</a></h1>
button is used, in addition to the state change, the system looks for pending
transfers requiring a release and try to assign them to a channel in the
“Open” or “Locked” state.</p>
<p>You can also use the “Sleep and Reassign” action on channels that have transfers
in progress. This is particularly useful if you don’t want to use the current
channel and want to reassign pickings to another(s) channel(s). Before using that
action, you should check that channels for those pickings are open (as the transfers
could not be assigned if there is no corresponding channel found).</p>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#toc-entry-3">Bug Tracker</a></h1>
Expand Down
1 change: 1 addition & 0 deletions stock_release_channel/tests/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@
test_release_channel,
test_release_channel_lifecycle,
test_release_channel_partner,
test_release_channel_safe_sleep,
)
96 changes: 96 additions & 0 deletions stock_release_channel/tests/test_release_channel_safe_sleep.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
# Copyright 2025 ACSONE SA/NV (https://acsone.eu)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)


from .common import ReleaseChannelCase


class TestReleaseChannelCancel(ReleaseChannelCase):
def test_release_channel_cancel(self):
"""
Create a new channel with a priority filter
Release the move with that picking priority

The created channel is assigned to the move picking

Call the unassign on the channel and check the
picking is unassigned

Set the channel to 'asleep' state
Try to assign the picking

The default channel should be set
"""
self.env.company.recompute_channel_on_pickings_at_release = True
channel = self._create_channel(
name="Test Domain",
sequence=1,
rule_domain=[("priority", "=", "1")],
)
move = self._create_single_move(self.product1, 10)
move.picking_id.priority = "1"
move.release_available_to_promise()
self.assertEqual(move.picking_id.release_channel_id, channel)

channel.action_safe_sleep()
self.assertFalse(move.picking_id.release_channel_id)

move.release_available_to_promise()
self.assertEqual(move.picking_id.release_channel_id, self.default_channel)

def test_release_channel_cancel_after_picking(self):
"""
Create a new channel with a priority filter
Release the move with that picking priority

The created channel is assigned to the move picking

Transfer the picking from stock location

Call the unassign on the channel and check the
picking is unassigned

Set the channel to 'asleep' state
Try to assign the picking

The default channel should be set

Transfer the outgoing picking

Try to unassign it. It should be impossible

"""
self.env.company.recompute_channel_on_pickings_at_release = True
channel = self._create_channel(
name="Test Domain",
sequence=1,
rule_domain=[("priority", "=", "1")],
)
self._update_qty_in_location(self.wh.lot_stock_id, self.product1, 10.0)
move = self._create_single_move(self.product1, 10)
move.warehouse_id = self.wh
move.procure_method = "make_to_order"
move.rule_id = self.wh.delivery_route_id.rule_ids.filtered(
lambda rule: rule.location_dest_id == move.location_dest_id
)
move.route_ids = move.rule_id.route_id
move.picking_id.priority = "1"
move.need_release = True
move.invalidate_cache(["ordered_available_to_promise_qty"])
move.release_available_to_promise()
self.assertEqual(move.picking_id.release_channel_id, channel)

self.assertTrue(move.move_orig_ids)

move.move_orig_ids.quantity_done = 10.0
move.move_orig_ids._action_done()

channel.action_safe_sleep()
self.assertFalse(move.picking_id.release_channel_id)

move.release_available_to_promise()
self.assertEqual(move.picking_id.release_channel_id, self.default_channel)

move.quantity_done = 10.0
move._action_done()
self.assertEqual("done", move.state)
11 changes: 11 additions & 0 deletions stock_release_channel/views/stock_release_channel_views.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
<field name="is_action_unlock_allowed" invisible="1" />
<field name="is_action_sleep_allowed" invisible="1" />
<field name="is_action_wake_up_allowed" invisible="1" />
<field name="is_action_safe_sleep_allowed" invisible="1" />
<button
name="action_lock"
string="Lock"
Expand Down Expand Up @@ -43,6 +44,16 @@
icon="fa-bolt"
attrs="{'invisible': [('is_action_wake_up_allowed', '=', False)]}"
/>
<button
name="action_safe_sleep"
string="Sleep and Reassign"
type="object"
class="oe_stat_button"
icon="fa-random"
confirm="Are you sure you want to make this channel fall asleep and reassign pickings? You maybe need to check if new channel to catch these pickings is open before doing this."
help="Click here to make this channel sleep and reassign current pickings"
attrs="{'invisible': [('is_action_safe_sleep_allowed', '=', False)]}"
/>
<field
name="state"
widget="statusbar"
Expand Down