Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[16.0][IMP] stock_available_to_promise_release: Improved release process ro… #939

Open
wants to merge 1 commit into
base: 16.0
Choose a base branch
from
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
34 changes: 33 additions & 1 deletion stock_available_to_promise_release/models/stock_move.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,12 +562,16 @@ def _run_stock_rule(self):

# Pull the released moves
for move in released_moves:
qty_to_release = move._get_qty_to_release()
rounding = move.product_uom.rounding
if float_compare(qty_to_release, 0, precision_rounding=rounding) <= 0:
continue
move._before_release()
values = move._prepare_procurement_values()
procurement_requests.append(
self.env["procurement.group"].Procurement(
move.product_id,
move.product_uom_qty,
qty_to_release,
move.product_uom,
move.location_id,
move.rule_id and move.rule_id.name or "/",
Expand All @@ -583,6 +587,34 @@ def _run_stock_rule(self):

return released_moves

def _get_qty_to_release(self):
"""Return the qty to release for the move

The qty to release is the move qty minus the qty released for this move
minus the the qty already reserved for the move.

This qty will never exceed the ordered available to promise qty.
"""
self.ensure_one()
released_moves = self.move_orig_ids.filtered(
lambda m: m.state not in ("done", "cancel")
)
all_released_qty = sum(released_moves.mapped("product_uom_qty"))
others_requesting_moves = (
released_moves.move_dest_ids.filtered(
lambda m: m.state not in ("done", "cancel")
)
- self
)
others_moves_requested_qty = sum(
others_requesting_moves.mapped("product_uom_qty")
) - sum(others_requesting_moves.mapped("reserved_availability"))
current_released_qty = all_released_qty - others_moves_requested_qty
to_release = (
self.product_uom_qty - self.reserved_availability - current_released_qty
)
return min(to_release, self.ordered_available_to_promise_qty)

def _before_release(self):
"""Hook that aims to be overridden."""
self._release_set_expected_date()
Expand Down
81 changes: 81 additions & 0 deletions stock_available_to_promise_release/tests/test_reservation.py
Original file line number Diff line number Diff line change
Expand Up @@ -1242,3 +1242,84 @@ def test_release_policy(self):
picking.release_available_to_promise()
new_picking = self._pickings_in_group(picking.group_id) - picking
self.assertEqual(new_picking.move_type, "one")

def test_release_available_multiple_calls(self):
self.wh.delivery_route_id.write({"available_to_promise_defer_pull": True})
# put some qty in output location
self._update_qty_in_location(self.wh.lot_stock_id, self.product1, 5.0)
ship = self._create_picking_chain(self.wh, [(self.product1, 10)])

ship.release_available_to_promise()
pick_pick = ship.move_ids.move_orig_ids.picking_id
self.assertEqual(pick_pick.move_ids.product_uom_qty, 5.0)

ship_backorder = ship.backorder_ids
self.assertTrue(ship_backorder)
self.assertEqual(ship_backorder.move_ids.product_uom_qty, 5.0)
self.assertFalse(ship_backorder.move_ids.move_orig_ids.picking_id)
ships = ship + ship_backorder

# the same call to release_available_to_promise should not create a new picking
# nor change the qty of the existing one
ships.release_available_to_promise()
self.assertEqual(pick_pick, ship.move_ids.move_orig_ids.picking_id)
self.assertEqual(pick_pick.move_ids.product_uom_qty, 5.0)
self.assertEqual(ship_backorder.move_ids.product_uom_qty, 5.0)
self.assertFalse(ship_backorder.move_ids.move_orig_ids.picking_id)

# put more qty in output location
# and force release
self._update_qty_in_location(self.wh.lot_stock_id, self.product1, 10.0)
ships.move_ids.need_release = True

# the release should update the qty of the existing picking to the new qty
# available
ships.release_available_to_promise()
self.assertEqual(pick_pick, ship.move_ids.move_orig_ids.picking_id)
self.assertEqual(pick_pick.move_ids.product_uom_qty, 10.0)
self.assertEqual(ship_backorder.move_ids.move_orig_ids.picking_id, pick_pick)

# partially process the picking
pick_pick.action_assign()
pick_pick.move_line_ids.qty_done = 3.0
pick_pick._action_done()

# the pick should still contain the remaining qty
pick_pick = ship.move_ids.move_orig_ids.filtered(
lambda p: p.state not in ("done", "cancel")
).picking_id
self.assertEqual(pick_pick.move_ids.product_uom_qty, 7.0)

# force release again
ship.move_ids.need_release = True
ship.release_available_to_promise()

# release should take into account already processed qty
self.assertEqual(pick_pick.move_ids.product_uom_qty, 7.0)

# force release of backorder
ship_backorder.move_ids.need_release = True
ship_backorder.release_available_to_promise()
self.assertEqual(pick_pick.move_ids.product_uom_qty, 7.0)

# if we release the two ships at same time, it's without effect
ships.move_ids.need_release = True
ships.release_available_to_promise()
self.assertEqual(pick_pick.move_ids.product_uom_qty, 7.0)

# if we cancel the remaining pick and release again, the new
# picking must be for the remaining qty
pick_pick.action_cancel()
ship.move_ids.need_release = True
ship.release_available_to_promise()

pick_pick = ship.move_ids.move_orig_ids.picking_id.filtered(
lambda p: p.state not in ("done", "cancel")
)
# only the first picking is released -> 5.0 - 3.0 = 2.0
self.assertEqual(pick_pick.move_ids.product_uom_qty, 2.0)

ship_backorder.move_ids.need_release = True
ship_backorder.release_available_to_promise()
# the backorder is for all the remaining qty
self.assertEqual(pick_pick.move_ids.product_uom_qty, 7.0)
Loading