Skip to content

Commit

Permalink
[IMP] stock_move_auto_assign_auto_release: Release release_ready pick…
Browse files Browse the repository at this point in the history
…ings instead of moves

Instead of just releasing the release ready moves of a give product
it now releases the whole transfer
This ensures that a transfer with a release_policy=one gets not split
  • Loading branch information
mt-software-de committed Aug 7, 2024
1 parent 18c4152 commit 6eb9c8f
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record
id="job_function_product_product_moves_auto_release"
id="job_function_product_product_pickings_auto_release"
model="queue.job.function"
>
<field name="model_id" ref="product.model_product_product" />
<field name="method">moves_auto_release</field>
<field name="method">pickings_auto_release</field>
<field name="channel_id" ref="channel_stock_auto_release" />
</record>

<record id="job_function_stock_picking_auto_release" model="queue.job.function">
<field name="model_id" ref="stock.model_stock_picking" />
<field name="method">auto_release_available_to_promise</field>
<field name="channel_id" ref="channel_stock_auto_release" />
<field name="retry_pattern" eval="{1: 1, 5: 5, 10: 10, 15: 30}" />
</record>
</odoo>
12 changes: 6 additions & 6 deletions stock_move_auto_assign_auto_release/models/product_product.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2022 ACSONE SA/NV
# Copyright 2024 Michael Tietz (MT Software) <mtietz@mt-software.de>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import models
Expand All @@ -14,16 +15,15 @@ def _moves_auto_release_domain(self):
("is_auto_release_allowed", "=", True),
]

def moves_auto_release(self):
"""Job trying to auto release moves based on product
def pickings_auto_release(self):
"""Job trying to auto release pickings based on product
It searches all* the moves auto releasable and trigger the release
available to promise process.
It searches all* the moves auto releasable
and triggers a delayed release available to promise for their pickings.
"""
self.ensure_one()
moves = self.env["stock.move"].search(self._moves_auto_release_domain())
pickings = moves.picking_id
if not pickings:
return
self._lock_pickings_or_retry(pickings)
moves.release_available_to_promise()
pickings._delay_auto_release_available_to_promise()
3 changes: 2 additions & 1 deletion stock_move_auto_assign_auto_release/models/stock_move.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2022 ACSONE SA/NV
# Copyright 2024 Michael Tietz (MT Software) <mtietz@mt-software.de>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import _, api, fields, models
Expand Down Expand Up @@ -67,6 +68,6 @@ def _enqueue_auto_assign(self, product, locations, **job_options):
)
job_options.setdefault("identity_key", identity_exact)
delayable = product.delayable(**job_options)
release_job = delayable.moves_auto_release()
release_job = delayable.pickings_auto_release()
job.on_done(release_job)
return job
19 changes: 18 additions & 1 deletion stock_move_auto_assign_auto_release/models/stock_picking.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
# Copyright 2022 ACSONE SA/NV
# Copyright 2024 Michael Tietz (MT Software) <mtietz@mt-software.de>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import api, fields, models
from odoo import _, api, fields, models
from odoo.osv.expression import NEGATIVE_TERM_OPERATORS

from odoo.addons.queue_job.job import identity_exact


class StockPicking(models.Model):

Expand Down Expand Up @@ -47,3 +50,17 @@ def _search_is_auto_release_allowed(self, operator, value):
if not is_auto_release_allowed:
domain = [("id", "not in", self.search(domain).ids)]
return domain

def _delay_auto_release_available_to_promise(self):
for picking in self:
picking.with_delay(
identity_key=identity_exact,
description=_(
"Auto release available to promise %(name)s", name=picking.name
),
).auto_release_available_to_promise()

def auto_release_available_to_promise(self):
to_release = self.filtered("is_auto_release_allowed")
to_release.release_available_to_promise()
return to_release
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
* Laurent Mignon <laurent.mignon@acsone.eu>
* Michael Tietz (MT Software) <mtietz@mt-software.de>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Copyright 2022 ACSONE SA/NV
# Copyright 2024 Michael Tietz (MT Software) <mtietz@mt-software.de>
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
from datetime import datetime

Expand Down Expand Up @@ -82,15 +83,21 @@ def _receive_product(self, product=None, qty=None):
move.move_line_ids.location_dest_id = self.loc_bin1.id
move._action_done()

def test_product_moves_auto_release(self):
def test_product_pickings_auto_release(self):
"""Test job method, update qty available and launch auto release on
the product"""
self.assertEqual(1, len(self.unreleased_move))
self.assertEqual(1, len(self.picking.move_lines))
self.assertEqual(5, self.picking.move_lines.product_qty)
# put stock in Stock/Shelf 1, the move has a source location in Stock
self._update_qty_in_location(self.loc_bin1, self.product1, 100)
self.product1.moves_auto_release()
with trap_jobs() as trap:
self.product1.pickings_auto_release()
job = self._get_job_for_method(
trap.enqueued_jobs,
self.unreleased_move.picking_id.auto_release_available_to_promise,
)
job.perform()
self.assertFalse(self.unreleased_move.need_release)
self.assertEqual(1, len(self.picking.move_lines))
self.assertEqual(10, self.picking.move_lines.product_qty)
Expand All @@ -115,7 +122,7 @@ def test_move_done_enqueue_job(self):
)
# and a second one to auto release
trap.assert_enqueued_job(
self.product1.moves_auto_release,
self.product1.pickings_auto_release,
args=(),
kwargs={},
properties=dict(
Expand All @@ -127,7 +134,7 @@ def test_move_done_enqueue_job(self):
trap.enqueued_jobs, self.product1.moves_auto_assign
)
job2 = self._get_job_for_method(
trap.enqueued_jobs, self.product1.moves_auto_release
trap.enqueued_jobs, self.product1.pickings_auto_release
)
self.assertIn(job1, job2.depends_on)

Expand Down Expand Up @@ -166,3 +173,54 @@ def test_move_field_is_auto_release_allowed(self):
for domain in NOT_RELEASABLE_DOMAINS:
self.assertIn(move_released, self.env["stock.move"].search(domain))
self.assertNotIn(move_not_released, self.env["stock.move"].search(domain))

def test_picking_policy_one_async_receive(self):
self.shipping.action_cancel()
self.picking.action_cancel()
shipping = self._out_picking(
self._create_picking_chain(
self.wh,
[(self.product1, 10), (self.product2, 10)],
date=datetime(2019, 9, 2, 16, 0),
)
)
shipping.release_policy = "one"
shipping.move_type = "one"
self.assertTrue(
all(
move.need_release and not move.release_ready
for move in shipping.move_lines
)
)
with trap_jobs() as trap:
self._receive_product(self.product1, 100)
shipping.invalidate_cache()
shipping.move_lines.invalidate_cache()
jobs = trap.enqueued_jobs
with trap_jobs() as trap:
for job in jobs:
job.perform()
job = self._get_job_for_method(
trap.enqueued_jobs, shipping.auto_release_available_to_promise
)
self.assertFalse(job)
with trap_jobs() as trap:
self._receive_product(self.product2, 100)
shipping.invalidate_cache()
shipping.move_lines.invalidate_cache()
jobs = trap.enqueued_jobs
with trap_jobs() as trap:
for job in jobs:
job.perform()
job = self._get_job_for_method(
trap.enqueued_jobs, shipping.auto_release_available_to_promise
)
job.perform()
move_product1 = shipping.move_lines.filtered(
lambda m: m.product_id == self.product1
)
move_product2 = shipping.move_lines - move_product1
self.assertFalse(move_product2.release_ready)
self.assertFalse(move_product2.need_release)
self.assertFalse(move_product1.need_release)
self.assertFalse(move_product1.release_ready)

0 comments on commit 6eb9c8f

Please sign in to comment.