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

[14.0][IMP] stock_move_auto_assign_auto_release: Release release_ready moves together #2122

Merged
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
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()
Copy link
Contributor

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know why yet, i will have a look.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Locally it works. :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Somehow the trap jobs wasn't working as expected with the current queue.job version. I fixed it

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