From 3b56bf2d0a5c491be7c58afb8dddc972fd10843c Mon Sep 17 00:00:00 2001 From: Denis Roussel Date: Mon, 30 Jan 2023 17:39:25 +0100 Subject: [PATCH 01/10] [16.0][ADD] stock_release_channel_process_end_date --- .../stock_release_channel_process_end_time | 1 + .../setup.py | 6 + .../README.rst | 99 ++++ .../__init__.py | 1 + .../__manifest__.py | 20 + .../models/__init__.py | 1 + .../models/stock_release_channel.py | 58 +++ .../readme/CONTRIBUTORS.rst | 1 + .../readme/DESCRIPTION.rst | 4 + .../readme/ROADMAP.rst | 2 + .../readme/USAGE.rst | 4 + .../static/description/icon.png | Bin 0 -> 9455 bytes .../static/description/index.html | 441 ++++++++++++++++++ .../tests/__init__.py | 1 + .../tests/test_release_end_date.py | 93 ++++ .../utils.py | 21 + .../views/stock_release_channel.xml | 99 ++++ 17 files changed, 852 insertions(+) create mode 120000 setup/stock_release_channel_process_end_time/odoo/addons/stock_release_channel_process_end_time create mode 100644 setup/stock_release_channel_process_end_time/setup.py create mode 100644 stock_release_channel_process_end_time/README.rst create mode 100644 stock_release_channel_process_end_time/__init__.py create mode 100644 stock_release_channel_process_end_time/__manifest__.py create mode 100644 stock_release_channel_process_end_time/models/__init__.py create mode 100644 stock_release_channel_process_end_time/models/stock_release_channel.py create mode 100644 stock_release_channel_process_end_time/readme/CONTRIBUTORS.rst create mode 100644 stock_release_channel_process_end_time/readme/DESCRIPTION.rst create mode 100644 stock_release_channel_process_end_time/readme/ROADMAP.rst create mode 100644 stock_release_channel_process_end_time/readme/USAGE.rst create mode 100644 stock_release_channel_process_end_time/static/description/icon.png create mode 100644 stock_release_channel_process_end_time/static/description/index.html create mode 100644 stock_release_channel_process_end_time/tests/__init__.py create mode 100644 stock_release_channel_process_end_time/tests/test_release_end_date.py create mode 100644 stock_release_channel_process_end_time/utils.py create mode 100644 stock_release_channel_process_end_time/views/stock_release_channel.xml diff --git a/setup/stock_release_channel_process_end_time/odoo/addons/stock_release_channel_process_end_time b/setup/stock_release_channel_process_end_time/odoo/addons/stock_release_channel_process_end_time new file mode 120000 index 00000000000..8e8afb11768 --- /dev/null +++ b/setup/stock_release_channel_process_end_time/odoo/addons/stock_release_channel_process_end_time @@ -0,0 +1 @@ +../../../../stock_release_channel_process_end_time \ No newline at end of file diff --git a/setup/stock_release_channel_process_end_time/setup.py b/setup/stock_release_channel_process_end_time/setup.py new file mode 100644 index 00000000000..28c57bb6403 --- /dev/null +++ b/setup/stock_release_channel_process_end_time/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_release_channel_process_end_time/README.rst b/stock_release_channel_process_end_time/README.rst new file mode 100644 index 00000000000..35bf0b4e117 --- /dev/null +++ b/stock_release_channel_process_end_time/README.rst @@ -0,0 +1,99 @@ +====================================== +Stock Release Channel Process End Date +====================================== + +.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fwms-lightgray.png?logo=github + :target: https://github.com/OCA/wms/tree/16.0/stock_release_channel_process_end_time + :alt: OCA/wms +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/wms-16-0/wms-16-0-stock_release_channel_process_end_time + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png + :target: https://runbot.odoo-community.org/runbot/285/16.0 + :alt: Try me on Runbot + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to set an end time on a release channel that will be transmitted on +related stock pickings (on scheduled date) when the channel awakes. + +That allows to use Odoo core sorting feature on stock pickings level. + +**Table of contents** + +.. contents:: + :local: + +Usage +===== + +#. Go To Release Channels +#. Set an end time +#. Wake up the channel +#. The assigned pickings have their scheduled date set at the next end time. + +Known issues / Roadmap +====================== + +* Maybe something to do on pickings level when setting channels asleep (storing + the original scheduled date or ...). + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues `_. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* ACSONE SA/NV + +Contributors +~~~~~~~~~~~~ + +* Denis Roussel + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +.. |maintainer-rousseldenis| image:: https://github.com/rousseldenis.png?size=40px + :target: https://github.com/rousseldenis + :alt: rousseldenis + +Current `maintainer `__: + +|maintainer-rousseldenis| + +This module is part of the `OCA/wms `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/stock_release_channel_process_end_time/__init__.py b/stock_release_channel_process_end_time/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/stock_release_channel_process_end_time/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/stock_release_channel_process_end_time/__manifest__.py b/stock_release_channel_process_end_time/__manifest__.py new file mode 100644 index 00000000000..eabf863a78c --- /dev/null +++ b/stock_release_channel_process_end_time/__manifest__.py @@ -0,0 +1,20 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Stock Release Channel Process End Date", + "summary": """ + Allows to define an end date (and time) on a release channel and + propagate it to the concerned pickings""", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "maintainers": ["rousseldenis"], + "author": "ACSONE SA/NV,Odoo Community Association (OCA)", + "website": "https://github.com/OCA/wms", + "depends": [ + "stock_release_channel", + ], + "data": [ + "views/stock_release_channel.xml", + ], +} diff --git a/stock_release_channel_process_end_time/models/__init__.py b/stock_release_channel_process_end_time/models/__init__.py new file mode 100644 index 00000000000..a0504e84078 --- /dev/null +++ b/stock_release_channel_process_end_time/models/__init__.py @@ -0,0 +1 @@ +from . import stock_release_channel diff --git a/stock_release_channel_process_end_time/models/stock_release_channel.py b/stock_release_channel_process_end_time/models/stock_release_channel.py new file mode 100644 index 00000000000..98a1421702e --- /dev/null +++ b/stock_release_channel_process_end_time/models/stock_release_channel.py @@ -0,0 +1,58 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + +from ..utils import float_to_time, next_datetime + + +class StockReleaseChannel(models.Model): + + _inherit = "stock.release.channel" + + process_end_time = fields.Float( + help="Fill in this to indicates when this channel release process would" + "be ended. This information will be used to compute the channel pickings" + "scheduled date at channel awaking.", + ) + process_end_time_can_edit = fields.Boolean( + compute="_compute_process_end_time_can_edit", + help="Technical field in order to know if user can edit the end date in views", + ) + process_end_date = fields.Datetime( + compute="_compute_process_end_date", + store=True, + readonly=False, + help="This is the end date for this window of opened channel.", + ) + + @api.depends_context("uid") + def _compute_process_end_time_can_edit(self): + if self.user_has_groups("stock.group_stock_manager"): + self.update({"process_end_time_can_edit": True}) + else: + self.update({"process_end_time_can_edit": False}) + + @api.depends("state", "process_end_time") + def _compute_process_end_date(self): + now = fields.Datetime.now() + for channel in self: + # We check if a date is not already set (manually) + if channel.state != "asleep" and not channel.process_end_date: + channel.process_end_date = next_datetime( + now, float_to_time(channel.process_end_time) + ) + elif channel.state == "asleep": + channel.process_end_date = False + + @api.model + def assign_release_channel(self, picking): + res = super().assign_release_channel(picking) + # Check if a channel has been assigned to the picking and write scheduled_date + # if different to avoid unnecessary write + if ( + picking.release_channel_id + and picking.scheduled_date != picking.release_channel_id.process_end_date + ): + picking.scheduled_date = picking.release_channel_id.process_end_date + return res diff --git a/stock_release_channel_process_end_time/readme/CONTRIBUTORS.rst b/stock_release_channel_process_end_time/readme/CONTRIBUTORS.rst new file mode 100644 index 00000000000..9179ee4b8fa --- /dev/null +++ b/stock_release_channel_process_end_time/readme/CONTRIBUTORS.rst @@ -0,0 +1 @@ +* Denis Roussel diff --git a/stock_release_channel_process_end_time/readme/DESCRIPTION.rst b/stock_release_channel_process_end_time/readme/DESCRIPTION.rst new file mode 100644 index 00000000000..b52c964d3d4 --- /dev/null +++ b/stock_release_channel_process_end_time/readme/DESCRIPTION.rst @@ -0,0 +1,4 @@ +This module allows to set an end time on a release channel that will be transmitted on +related stock pickings (on scheduled date) when the channel awakes. + +That allows to use Odoo core sorting feature on stock pickings level. diff --git a/stock_release_channel_process_end_time/readme/ROADMAP.rst b/stock_release_channel_process_end_time/readme/ROADMAP.rst new file mode 100644 index 00000000000..9f8713d2d53 --- /dev/null +++ b/stock_release_channel_process_end_time/readme/ROADMAP.rst @@ -0,0 +1,2 @@ +* Maybe something to do on pickings level when setting channels asleep (storing + the original scheduled date or ...). diff --git a/stock_release_channel_process_end_time/readme/USAGE.rst b/stock_release_channel_process_end_time/readme/USAGE.rst new file mode 100644 index 00000000000..4cfa97ff690 --- /dev/null +++ b/stock_release_channel_process_end_time/readme/USAGE.rst @@ -0,0 +1,4 @@ +#. Go To Release Channels +#. Set an end time +#. Wake up the channel +#. The assigned pickings have their scheduled date set at the next end time. diff --git a/stock_release_channel_process_end_time/static/description/icon.png b/stock_release_channel_process_end_time/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..3a0328b516c4980e8e44cdb63fd945757ddd132d GIT binary patch literal 9455 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 z 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 Xviia!U7SGha1wx#SCgwmn*{w2TRX*I literal 0 HcmV?d00001 diff --git a/stock_release_channel_process_end_time/static/description/index.html b/stock_release_channel_process_end_time/static/description/index.html new file mode 100644 index 00000000000..d0a4b7460ad --- /dev/null +++ b/stock_release_channel_process_end_time/static/description/index.html @@ -0,0 +1,441 @@ + + + + + + +Stock Release Channel Process End Date + + + +
+

Stock Release Channel Process End Date

+ + +

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runbot

+

This module allows to set an end time on a release channel that will be transmitted on +related stock pickings (on scheduled date) when the channel awakes.

+

That allows to use Odoo core sorting feature on stock pickings level.

+

Table of contents

+ +
+

Usage

+
    +
  1. Go To Release Channels
  2. +
  3. Set an end time
  4. +
  5. Wake up the channel
  6. +
  7. The assigned pickings have their scheduled date set at the next end time.
  8. +
+
+
+

Known issues / Roadmap

+
    +
  • Maybe something to do on pickings level when setting channels asleep (storing +the original scheduled date or …).
  • +
+
+
+

Bug Tracker

+

Bugs are tracked on GitHub Issues. +In case of trouble, please check there if your issue has already been reported. +If you spotted it first, help us smashing it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • ACSONE SA/NV
  • +
+
+
+

Contributors

+ +
+
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

Current maintainer:

+

rousseldenis

+

This module is part of the OCA/wms project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/stock_release_channel_process_end_time/tests/__init__.py b/stock_release_channel_process_end_time/tests/__init__.py new file mode 100644 index 00000000000..b2a297cef20 --- /dev/null +++ b/stock_release_channel_process_end_time/tests/__init__.py @@ -0,0 +1 @@ +from . import test_release_end_date diff --git a/stock_release_channel_process_end_time/tests/test_release_end_date.py b/stock_release_channel_process_end_time/tests/test_release_end_date.py new file mode 100644 index 00000000000..a5ae1b60ac1 --- /dev/null +++ b/stock_release_channel_process_end_time/tests/test_release_end_date.py @@ -0,0 +1,93 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from freezegun import freeze_time + +from odoo import fields + +from odoo.addons.queue_job.job import Job +from odoo.addons.stock_release_channel.tests.common import ChannelReleaseCase + + +class ReleaseChannelEndDateCase(ChannelReleaseCase): + @freeze_time("2023-01-27") + def test_channel_end_date(self): + # Set the end time + self.channel.process_end_time = 23.0 + # Asleep the release channel to void the process end date + self.channel.action_sleep() + self.channel.invalidate_recordset() + # Wake up the channel to set the process end date + self.channel.action_wake_up() + self.assertEqual( + "2023-01-27 23:00:00", + fields.Datetime.to_string(self.channel.process_end_date), + ) + + @freeze_time("2023-01-27 10:00:00") + def test_channel_end_date_tomorrow(self): + # Set the end time + self.channel.process_end_time = 1.0 + # Asleep the release channel to void the process end date + self.channel.action_sleep() + self.channel.invalidate_recordset() + # Wake up the channel to set the process end date + # Current time is 10:00:00 + self.channel.action_wake_up() + self.assertEqual( + "2023-01-28 01:00:00", + fields.Datetime.to_string(self.channel.process_end_date), + ) + + @freeze_time("2023-01-27 10:00:00") + def test_channel_end_date_manual(self): + # Set the end time + self.channel.process_end_time = 1.0 + # Asleep the release channel to void the process end date + self.channel.action_sleep() + self.channel.invalidate_recordset() + # Wake up the channel to set the process end date + # Current time is 10:00:00 + self.channel.action_wake_up() + self.assertEqual( + "2023-01-28 01:00:00", + fields.Datetime.to_string(self.channel.process_end_date), + ) + + # We force the end date + self.channel.process_end_date = "2023-01-27 23:30:00" + self.assertEqual( + "2023-01-27 23:30:00", + fields.Datetime.to_string(self.channel.process_end_date), + ) + + @freeze_time("2023-01-27 10:00:00") + def test_picking_scheduled_date(self): + # Remove existing jobs as some already exists to assign pickings to channel + jobs_before = self.env["queue.job"].search([]) + jobs_before.unlink() + # Set the end time + self.channel.process_end_time = 23.0 + # Asleep the release channel to void the process end date + self.channel.action_sleep() + self.channel.invalidate_recordset() + self.channel.action_wake_up() + # Execute the picking channel assignations + jobs_after = self.env["queue.job"].search([]) + for job in jobs_after: + job = Job.load(job.env, job.uuid) + job.perform() + pickings = self.channel.picking_ids + # Check the scheduled date is corresponding to the one on channel + for picking in pickings: + self.assertEqual( + "2023-01-27 23:00:00", fields.Datetime.to_string(picking.scheduled_date) + ) + + def test_can_edit_time(self): + user = self.env.ref("base.user_demo") + group = self.env.ref("stock.group_stock_manager") + user.groups_id -= group + self.assertFalse(self.channel.with_user(user).process_end_time_can_edit) + + user.groups_id |= self.env.ref("stock.group_stock_manager") + self.assertTrue(self.channel.with_user(user).process_end_time_can_edit) diff --git a/stock_release_channel_process_end_time/utils.py b/stock_release_channel_process_end_time/utils.py new file mode 100644 index 00000000000..b15c2ded36f --- /dev/null +++ b/stock_release_channel_process_end_time/utils.py @@ -0,0 +1,21 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +import math +from datetime import datetime, time, timedelta + +from odoo.tools import float_round + + +def float_to_time(hours) -> time: + """This returns a tuple (hours, minutes) from a float representation of time""" + if hours == 24.0: + return time.max + fractional, integral = math.modf(hours) + return time(int(integral), int(float_round(60 * fractional, precision_digits=0)), 0) + + +def next_datetime(current: datetime, hours: time, **kwargs) -> datetime: + repl = current.replace(hour=hours.hour, minute=hours.minute, **kwargs) + while repl <= current: + repl = repl + timedelta(days=1) + return repl diff --git a/stock_release_channel_process_end_time/views/stock_release_channel.xml b/stock_release_channel_process_end_time/views/stock_release_channel.xml new file mode 100644 index 00000000000..da03404bef1 --- /dev/null +++ b/stock_release_channel_process_end_time/views/stock_release_channel.xml @@ -0,0 +1,99 @@ + + + + + + stock.release.channel.form (in stock_release_channel_process_end_date) + stock.release.channel + + + + + + + + + + + + stock.release.channel.search (in stock_release_channel_process_end_date) + stock.release.channel + + + + + + + + + + + + stock.release.channel.tree (in stock_release_channel_process_end_date) + stock.release.channel + + + + + + + + + + + stock.release.channel.kanban + stock.release.channel + + + +
+
+
+
+
+ +
+
+
+ +
+ + + + + + From 9b0bb2badd20bda9dc60ed20890456056032d97a Mon Sep 17 00:00:00 2001 From: Denis Roussel Date: Wed, 8 Feb 2023 11:17:33 +0100 Subject: [PATCH 02/10] [IMP] stock_release_channel_process_end_time: Add timezone for end time As time should be timezoned to correctly compute the real end date, consider the channel warehouse timezone or company one. If not defined, consider the timezone as UTC. --- .../__manifest__.py | 1 + .../models/stock_release_channel.py | 27 +++++++++++++-- .../readme/USAGE.rst | 7 ++++ .../tests/test_release_end_date.py | 34 +++++++++++++++++++ .../utils.py | 16 +++++++-- .../views/stock_release_channel.xml | 8 ++++- 6 files changed, 87 insertions(+), 6 deletions(-) diff --git a/stock_release_channel_process_end_time/__manifest__.py b/stock_release_channel_process_end_time/__manifest__.py index eabf863a78c..0b3a2c360dd 100644 --- a/stock_release_channel_process_end_time/__manifest__.py +++ b/stock_release_channel_process_end_time/__manifest__.py @@ -12,6 +12,7 @@ "author": "ACSONE SA/NV,Odoo Community Association (OCA)", "website": "https://github.com/OCA/wms", "depends": [ + "partner_tz", "stock_release_channel", ], "data": [ diff --git a/stock_release_channel_process_end_time/models/stock_release_channel.py b/stock_release_channel_process_end_time/models/stock_release_channel.py index 98a1421702e..17d62bbf3e2 100644 --- a/stock_release_channel_process_end_time/models/stock_release_channel.py +++ b/stock_release_channel_process_end_time/models/stock_release_channel.py @@ -1,8 +1,9 @@ # Copyright 2023 ACSONE SA/NV # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). - from odoo import api, fields, models +from odoo.addons.base.models.res_partner import _tz_get + from ..utils import float_to_time, next_datetime @@ -15,6 +16,11 @@ class StockReleaseChannel(models.Model): "be ended. This information will be used to compute the channel pickings" "scheduled date at channel awaking.", ) + process_end_time_tz = fields.Selection( + selection=_tz_get, + compute="_compute_process_end_time_tz", + help="Technical field to compute the timezone for the process end time.", + ) process_end_time_can_edit = fields.Boolean( compute="_compute_process_end_time_can_edit", help="Technical field in order to know if user can edit the end date in views", @@ -39,12 +45,27 @@ def _compute_process_end_date(self): for channel in self: # We check if a date is not already set (manually) if channel.state != "asleep" and not channel.process_end_date: - channel.process_end_date = next_datetime( - now, float_to_time(channel.process_end_time) + end = next_datetime( + now, + float_to_time( + channel.process_end_time, tz=channel.process_end_time_tz + ), ) + channel.process_end_date = end elif channel.state == "asleep": channel.process_end_date = False + @api.depends("warehouse_id.partner_id.tz") + @api.depends_context("company") + def _compute_process_end_time_tz(self): + # As the time is timezone-agnostic, we use the channel warehouse adress timezone + # or the company one, either it will be considered as UTC + company_tz = self.env.company.partner_id.tz + for channel in self: + channel.process_end_time_tz = ( + channel.warehouse_id.partner_id.tz or company_tz or False + ) + @api.model def assign_release_channel(self, picking): res = super().assign_release_channel(picking) diff --git a/stock_release_channel_process_end_time/readme/USAGE.rst b/stock_release_channel_process_end_time/readme/USAGE.rst index 4cfa97ff690..2c2db208251 100644 --- a/stock_release_channel_process_end_time/readme/USAGE.rst +++ b/stock_release_channel_process_end_time/readme/USAGE.rst @@ -1,3 +1,10 @@ +#. Assign a timezone on the Warehouse address if defined and if needed + If you have a lot of warehouses in the same timezone, you can also define + the timezone on the company partner. + If you don't define a timezone on the warehouse(s) nor the company, the process + end time on channels will be considered as UTC. + + #. Go To Release Channels #. Set an end time #. Wake up the channel diff --git a/stock_release_channel_process_end_time/tests/test_release_end_date.py b/stock_release_channel_process_end_time/tests/test_release_end_date.py index a5ae1b60ac1..c421fa01720 100644 --- a/stock_release_channel_process_end_time/tests/test_release_end_date.py +++ b/stock_release_channel_process_end_time/tests/test_release_end_date.py @@ -91,3 +91,37 @@ def test_can_edit_time(self): user.groups_id |= self.env.ref("stock.group_stock_manager") self.assertTrue(self.channel.with_user(user).process_end_time_can_edit) + + @freeze_time("2023-01-27") + def test_channel_end_date_warehouse_timezone(self): + # Set a warehouse with an adress and a timezone on channel + self.channel.warehouse_id = self.env.ref("stock.warehouse0") + self.channel.warehouse_id.partner_id.tz = "Europe/Brussels" + # Set the end time - In UTC == 22:00 + self.channel.process_end_time = 23.0 + # Asleep the release channel to void the process end date + self.channel.action_sleep() + self.channel.invalidate_recordset() + # Wake up the channel to set the process end date + self.channel.action_wake_up() + self.assertEqual( + "2023-01-27 22:00:00", + fields.Datetime.to_string(self.channel.process_end_date), + ) + + @freeze_time("2023-01-27") + def test_channel_end_date_company_timezone(self): + # Set a warehouse with an adress and a timezone on channel + self.assertFalse(self.channel.warehouse_id) + self.env.company.partner_id.tz = "Europe/Brussels" + # Set the end time - In UTC == 22:00 + self.channel.process_end_time = 23.0 + # Asleep the release channel to void the process end date + self.channel.action_sleep() + self.channel.invalidate_recordset() + # Wake up the channel to set the process end date + self.channel.action_wake_up() + self.assertEqual( + "2023-01-27 22:00:00", + fields.Datetime.to_string(self.channel.process_end_date), + ) diff --git a/stock_release_channel_process_end_time/utils.py b/stock_release_channel_process_end_time/utils.py index b15c2ded36f..76465829bda 100644 --- a/stock_release_channel_process_end_time/utils.py +++ b/stock_release_channel_process_end_time/utils.py @@ -3,19 +3,31 @@ import math from datetime import datetime, time, timedelta +import pytz + from odoo.tools import float_round +from odoo.addons.partner_tz.tools.tz_utils import tz_to_utc_time + -def float_to_time(hours) -> time: +def float_to_time(hours, tz=False) -> time: """This returns a tuple (hours, minutes) from a float representation of time""" + if not tz: + tz = pytz.utc if hours == 24.0: return time.max fractional, integral = math.modf(hours) - return time(int(integral), int(float_round(60 * fractional, precision_digits=0)), 0) + time_result = time( + int(integral), int(float_round(60 * fractional, precision_digits=0)), 0 + ) + time_result = tz_to_utc_time(tz, time_result) + return time_result def next_datetime(current: datetime, hours: time, **kwargs) -> datetime: repl = current.replace(hour=hours.hour, minute=hours.minute, **kwargs) + if hours.tzinfo: + repl += hours.utcoffset() while repl <= current: repl = repl + timedelta(days=1) return repl diff --git a/stock_release_channel_process_end_time/views/stock_release_channel.xml b/stock_release_channel_process_end_time/views/stock_release_channel.xml index da03404bef1..2aaea456e68 100644 --- a/stock_release_channel_process_end_time/views/stock_release_channel.xml +++ b/stock_release_channel_process_end_time/views/stock_release_channel.xml @@ -78,7 +78,13 @@
-
+
+ +
Date: Thu, 9 Feb 2023 14:36:34 +0100 Subject: [PATCH 03/10] [FIX] stock_release_channel_process_end_time: Don't take into account seconds and microseconds --- stock_release_channel_process_end_time/utils.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/stock_release_channel_process_end_time/utils.py b/stock_release_channel_process_end_time/utils.py index 76465829bda..81e01fe6e40 100644 --- a/stock_release_channel_process_end_time/utils.py +++ b/stock_release_channel_process_end_time/utils.py @@ -25,7 +25,9 @@ def float_to_time(hours, tz=False) -> time: def next_datetime(current: datetime, hours: time, **kwargs) -> datetime: - repl = current.replace(hour=hours.hour, minute=hours.minute, **kwargs) + repl = current.replace( + hour=hours.hour, minute=hours.minute, second=0, microsecond=0, **kwargs + ) if hours.tzinfo: repl += hours.utcoffset() while repl <= current: From 5a22ca3b47fdf822f37508a847992f1904533550 Mon Sep 17 00:00:00 2001 From: Denis Roussel Date: Thu, 27 Apr 2023 14:53:37 +0200 Subject: [PATCH 04/10] [IMP] stock_release_channel_process_end_time: Check if end time is defined before assignation to scheduled_date --- .../models/stock_release_channel.py | 1 + 1 file changed, 1 insertion(+) diff --git a/stock_release_channel_process_end_time/models/stock_release_channel.py b/stock_release_channel_process_end_time/models/stock_release_channel.py index 17d62bbf3e2..ba99a02da47 100644 --- a/stock_release_channel_process_end_time/models/stock_release_channel.py +++ b/stock_release_channel_process_end_time/models/stock_release_channel.py @@ -73,6 +73,7 @@ def assign_release_channel(self, picking): # if different to avoid unnecessary write if ( picking.release_channel_id + and picking.release_channel_id.process_end_date and picking.scheduled_date != picking.release_channel_id.process_end_date ): picking.scheduled_date = picking.release_channel_id.process_end_date From 8f2038535412a6fc13f34c30793da926d9ee5a73 Mon Sep 17 00:00:00 2001 From: "Laurent Mignon (ACSONE)" Date: Tue, 16 May 2023 11:42:23 +0200 Subject: [PATCH 05/10] [FIX] stock_release_channel_process_end_time: releasable picking computation The compute of releasable picking must take into account the process end date of the stock release channel and not the current datetime. The way the ORM is working into Odoo prevent to makes query on model with criteria based on values from linked model (comparing column from model A to column of model B). To work arround this limitation, we use a specialized field with a search method. Field's search methods are considered as 'internal' search methods and therefore allows you to use a special 'inselect' operator. This kind operator is usefull since it allows to express a criteria as a plain SQL quey. Thanks to this approach we've an easy way to implement our needs with the Odoo's ORM. --- .../models/__init__.py | 1 + .../models/stock_picking.py | 61 +++++++++++++++++++ .../models/stock_release_channel.py | 29 ++++++++- .../tests/test_release_end_date.py | 12 ++++ 4 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 stock_release_channel_process_end_time/models/stock_picking.py diff --git a/stock_release_channel_process_end_time/models/__init__.py b/stock_release_channel_process_end_time/models/__init__.py index a0504e84078..b82444463ee 100644 --- a/stock_release_channel_process_end_time/models/__init__.py +++ b/stock_release_channel_process_end_time/models/__init__.py @@ -1 +1,2 @@ +from . import stock_picking from . import stock_release_channel diff --git a/stock_release_channel_process_end_time/models/stock_picking.py b/stock_release_channel_process_end_time/models/stock_picking.py new file mode 100644 index 00000000000..b252c26879f --- /dev/null +++ b/stock_release_channel_process_end_time/models/stock_picking.py @@ -0,0 +1,61 @@ +# Copyright 2023 ACSONE SA/NV +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import _, api, fields, models +from odoo.exceptions import UserError + + +class StockPicking(models.Model): + + _inherit = "stock.picking" + + schedule_date_prior_to_channel_process_end_date_search = fields.Boolean( + store=False, + search="_search_schedule_date_prior_to_channel_process_end_date", + help="Technical field to search on not processed pickings where the scheduled" + "date is prior to the process end date of available channels.", + ) + + @api.model + def fields_get(self, allfields=None, attributes=None): + """Hide schedule_date_prior_to_channel_process_end_date_search from + filterable/searchable fields""" + res = super().fields_get(allfields, attributes) + if res.get("schedule_date_prior_to_channel_process_end_date_search"): + res["schedule_date_prior_to_channel_process_end_date_search"][ + "searchable" + ] = False + return res + + @api.model + def _search_schedule_date_prior_to_channel_process_end_date(self, operator, value): + """Search on not processed pickings where the scheduled date is prior to + the process end date of available channels. + """ + # We use a searchable field to be able to use 'inselect' operator in + # order to avoid a subquery in the search method. This is a workaround + # since the 'inselect' operator is not supported when calling search + # method but only into search method definition. + if operator not in ["=", "!="] or not isinstance(value, bool): + raise UserError(_("Operation not supported")) + query = """ + SELECT + stock_picking.id + FROM + stock_picking + WHERE + stock_picking.state NOT IN ('done', 'cancel') + AND exists ( + SELECT + TRUE + FROM + stock_release_channel + WHERE + stock_release_channel.process_end_date is not null + and stock_picking.scheduled_date <= stock_release_channel.process_end_date + ) + """ + if value: + operator_inselect = "inselect" if operator == "=" else "not inselect" + else: + operator_inselect = "not inselect" if operator == "=" else "inselect" + return [("id", operator_inselect, (query, []))] diff --git a/stock_release_channel_process_end_time/models/stock_release_channel.py b/stock_release_channel_process_end_time/models/stock_release_channel.py index ba99a02da47..e0fb4dd4515 100644 --- a/stock_release_channel_process_end_time/models/stock_release_channel.py +++ b/stock_release_channel_process_end_time/models/stock_release_channel.py @@ -48,7 +48,8 @@ def _compute_process_end_date(self): end = next_datetime( now, float_to_time( - channel.process_end_time, tz=channel.process_end_time_tz + channel.process_end_time, + tz=channel.process_end_time_tz, ), ) channel.process_end_date = end @@ -78,3 +79,29 @@ def assign_release_channel(self, picking): ): picking.scheduled_date = picking.release_channel_id.process_end_date return res + + def _field_picking_domains(self): + res = super()._field_picking_domains() + release_ready_domain = res["count_picking_release_ready"] + # the initial scheduled_date condition based on datetime.now() must + # be replaced by a condition based on the process_end_date + # since the processe_end_date is a field on the release channel + # and not on the picking we all use an 'inselect' operator + # (join in where clause is not possible). The 'inselect' operator + # is not available in the ORM so we use a search with a domain + # on a specialized field defined in the stock.picking model + # (see stock.picking._search_schedule_date_prior_to_channel_process_end_date) + new_domain = [] + for criteria in release_ready_domain: + if criteria[0] == "scheduled_date": + new_domain.append( + ( + "schedule_date_prior_to_channel_process_end_date_search", + "=", + True, + ) + ) + else: + new_domain.append(criteria) + res["count_picking_release_ready"] = new_domain + return res diff --git a/stock_release_channel_process_end_time/tests/test_release_end_date.py b/stock_release_channel_process_end_time/tests/test_release_end_date.py index c421fa01720..ef1a5e98c6c 100644 --- a/stock_release_channel_process_end_time/tests/test_release_end_date.py +++ b/stock_release_channel_process_end_time/tests/test_release_end_date.py @@ -82,6 +82,18 @@ def test_picking_scheduled_date(self): self.assertEqual( "2023-01-27 23:00:00", fields.Datetime.to_string(picking.scheduled_date) ) + # at this stage, the pickings are not ready to be released as the + # qty available is not enough + self.assertFalse(self.channel._get_pickings_to_release()) + self._update_qty_in_location(self.loc_bin1, self.product1, 100.0) + self._update_qty_in_location(self.loc_bin1, self.product2, 100.0) + pickings.refresh() + # the pickings are now ready to be released + self.assertEqual(pickings, self.channel._get_pickings_to_release()) + # if the scheduled date of one picking is changed to be after the + # process end date, it should not be releasable anymore + pickings[0].scheduled_date = fields.Datetime.from_string("2023-01-28 00:00:00") + self.assertEqual(pickings[1:], self.channel._get_pickings_to_release()) def test_can_edit_time(self): user = self.env.ref("base.user_demo") From 310a615aeaef53bd6b0f4e0813b2aac8021a4b54 Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Thu, 27 Jul 2023 13:23:54 +0200 Subject: [PATCH 06/10] [FIX] - assign channel end date to picking after release --- .../__manifest__.py | 1 + .../models/stock_picking.py | 13 +++++++++++++ .../models/stock_release_channel.py | 9 +-------- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/stock_release_channel_process_end_time/__manifest__.py b/stock_release_channel_process_end_time/__manifest__.py index 0b3a2c360dd..c6ed53852fc 100644 --- a/stock_release_channel_process_end_time/__manifest__.py +++ b/stock_release_channel_process_end_time/__manifest__.py @@ -14,6 +14,7 @@ "depends": [ "partner_tz", "stock_release_channel", + "stock_available_to_promise_release", ], "data": [ "views/stock_release_channel.xml", diff --git a/stock_release_channel_process_end_time/models/stock_picking.py b/stock_release_channel_process_end_time/models/stock_picking.py index b252c26879f..5ae4f3657e0 100644 --- a/stock_release_channel_process_end_time/models/stock_picking.py +++ b/stock_release_channel_process_end_time/models/stock_picking.py @@ -59,3 +59,16 @@ def _search_schedule_date_prior_to_channel_process_end_date(self, operator, valu else: operator_inselect = "not inselect" if operator == "=" else "inselect" return [("id", operator_inselect, (query, []))] + + def _after_release_set_expected_date(self): + res = super()._after_release_set_expected_date() + for rec in self: + # Check if a channel has been assigned to the picking and write + # scheduled_date if different to avoid unnecessary write + if ( + rec.release_channel_id + and rec.release_channel_id.process_end_date + and rec.scheduled_date != rec.release_channel_id.process_end_date + ): + rec.scheduled_date = rec.release_channel_id.process_end_date + return res diff --git a/stock_release_channel_process_end_time/models/stock_release_channel.py b/stock_release_channel_process_end_time/models/stock_release_channel.py index e0fb4dd4515..1806f289549 100644 --- a/stock_release_channel_process_end_time/models/stock_release_channel.py +++ b/stock_release_channel_process_end_time/models/stock_release_channel.py @@ -70,14 +70,7 @@ def _compute_process_end_time_tz(self): @api.model def assign_release_channel(self, picking): res = super().assign_release_channel(picking) - # Check if a channel has been assigned to the picking and write scheduled_date - # if different to avoid unnecessary write - if ( - picking.release_channel_id - and picking.release_channel_id.process_end_date - and picking.scheduled_date != picking.release_channel_id.process_end_date - ): - picking.scheduled_date = picking.release_channel_id.process_end_date + picking._after_release_set_expected_date() return res def _field_picking_domains(self): From 33d1e2be1f75726f4a9d3be63ca07cc86b531e2a Mon Sep 17 00:00:00 2001 From: sbejaoui Date: Fri, 4 Aug 2023 12:24:20 +0200 Subject: [PATCH 07/10] [IMP] - improve process end time location in form view --- .../views/stock_release_channel.xml | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/stock_release_channel_process_end_time/views/stock_release_channel.xml b/stock_release_channel_process_end_time/views/stock_release_channel.xml index 2aaea456e68..474239df95e 100644 --- a/stock_release_channel_process_end_time/views/stock_release_channel.xml +++ b/stock_release_channel_process_end_time/views/stock_release_channel.xml @@ -13,18 +13,21 @@ ref="stock_release_channel.stock_release_channel_form_view" /> - - + + + + + name="process_end_time" + widget="float_time" + attrs="{'readonly': [('process_end_time_can_edit', '=', False)]}" + /> - + name="process_end_date" + attrs="{'readonly': [('process_end_time_can_edit', '=', False)]}" + /> + + From 5809742c8f12ee49278d354bb527da7104edffd7 Mon Sep 17 00:00:00 2001 From: Bejaoui Souheil Date: Tue, 5 Sep 2023 15:26:30 +0200 Subject: [PATCH 08/10] [FIX] fix help message & add UTC as defauly value for process_end_time_tz Co-authored-by: Jacques-Etienne Baudoux --- .../models/stock_picking.py | 2 +- .../models/stock_release_channel.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/stock_release_channel_process_end_time/models/stock_picking.py b/stock_release_channel_process_end_time/models/stock_picking.py index 5ae4f3657e0..ceb4ed1288a 100644 --- a/stock_release_channel_process_end_time/models/stock_picking.py +++ b/stock_release_channel_process_end_time/models/stock_picking.py @@ -11,7 +11,7 @@ class StockPicking(models.Model): schedule_date_prior_to_channel_process_end_date_search = fields.Boolean( store=False, search="_search_schedule_date_prior_to_channel_process_end_date", - help="Technical field to search on not processed pickings where the scheduled" + help="Technical field to search on not processed pickings where the scheduled " "date is prior to the process end date of available channels.", ) diff --git a/stock_release_channel_process_end_time/models/stock_release_channel.py b/stock_release_channel_process_end_time/models/stock_release_channel.py index 1806f289549..1c84fcf6dba 100644 --- a/stock_release_channel_process_end_time/models/stock_release_channel.py +++ b/stock_release_channel_process_end_time/models/stock_release_channel.py @@ -12,8 +12,8 @@ class StockReleaseChannel(models.Model): _inherit = "stock.release.channel" process_end_time = fields.Float( - help="Fill in this to indicates when this channel release process would" - "be ended. This information will be used to compute the channel pickings" + help="Fill in this to indicates when this channel release process would " + "be ended. This information will be used to compute the channel pickings " "scheduled date at channel awaking.", ) process_end_time_tz = fields.Selection( @@ -64,7 +64,7 @@ def _compute_process_end_time_tz(self): company_tz = self.env.company.partner_id.tz for channel in self: channel.process_end_time_tz = ( - channel.warehouse_id.partner_id.tz or company_tz or False + channel.warehouse_id.partner_id.tz or company_tz or "UTC" ) @api.model From da2138b068923b34c372ede07baf1ba400ad1ebb Mon Sep 17 00:00:00 2001 From: Diep Huu Hoang Date: Mon, 18 Sep 2023 20:29:41 +0700 Subject: [PATCH 09/10] [IMP] add configuration to enable/disable set scheduled date --- .../README.rst | 28 +++++++++++++------ .../__manifest__.py | 1 + .../models/__init__.py | 1 + .../models/res_config_settings.py | 14 ++++++++++ .../models/stock_picking.py | 6 ++++ .../readme/USAGE.rst | 5 ++-- .../static/description/index.html | 15 ++++++++-- .../tests/test_release_end_date.py | 3 ++ .../views/res_config_settings.xml | 26 +++++++++++++++++ 9 files changed, 85 insertions(+), 14 deletions(-) create mode 100644 stock_release_channel_process_end_time/models/res_config_settings.py create mode 100644 stock_release_channel_process_end_time/views/res_config_settings.xml diff --git a/stock_release_channel_process_end_time/README.rst b/stock_release_channel_process_end_time/README.rst index 35bf0b4e117..0d1845a3913 100644 --- a/stock_release_channel_process_end_time/README.rst +++ b/stock_release_channel_process_end_time/README.rst @@ -2,10 +2,13 @@ Stock Release Channel Process End Date ====================================== -.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:84e66197d5580b417a6dc94e376b88b6466a007cd8946990b65689e9dea8647e + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png :target: https://odoo-community.org/page/development-status @@ -19,11 +22,11 @@ Stock Release Channel Process End Date .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png :target: https://translation.odoo-community.org/projects/wms-16-0/wms-16-0-stock_release_channel_process_end_time :alt: Translate me on Weblate -.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png - :target: https://runbot.odoo-community.org/runbot/285/16.0 - :alt: Try me on Runbot +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/webui/builds.html?repo=OCA/wms&target_branch=16.0 + :alt: Try me on Runboat -|badge1| |badge2| |badge3| |badge4| |badge5| +|badge1| |badge2| |badge3| |badge4| |badge5| This module allows to set an end time on a release channel that will be transmitted on related stock pickings (on scheduled date) when the channel awakes. @@ -38,24 +41,31 @@ That allows to use Odoo core sorting feature on stock pickings level. Usage ===== +#. Assign a timezone on the Warehouse address if defined and if needed + If you have a lot of warehouses in the same timezone, you can also define + the timezone on the company partner. + If you don't define a timezone on the warehouse(s) nor the company, the process + end time on channels will be considered as UTC. +#. Propagate/update scheduled date of picking follow process end date/time automatically + by setting "Update Scheduled Date" configuration in Settings/General Settings/Inventory + #. Go To Release Channels #. Set an end time #. Wake up the channel -#. The assigned pickings have their scheduled date set at the next end time. +#. The assigned pickings have their scheduled date set at the next end time. (if enabled "Update Scheduled Date" config) Known issues / Roadmap ====================== -* Maybe something to do on pickings level when setting channels asleep (storing +* Maybe something to do on pickings level when setting channels asleep (storing the original scheduled date or ...). - Bug Tracker =========== Bugs are tracked on `GitHub Issues `_. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +If you spotted it first, help us to smash it by providing a detailed and welcomed `feedback `_. Do not contact contributors directly about support or help with technical issues. diff --git a/stock_release_channel_process_end_time/__manifest__.py b/stock_release_channel_process_end_time/__manifest__.py index c6ed53852fc..b2f66da5e7f 100644 --- a/stock_release_channel_process_end_time/__manifest__.py +++ b/stock_release_channel_process_end_time/__manifest__.py @@ -17,6 +17,7 @@ "stock_available_to_promise_release", ], "data": [ + "views/res_config_settings.xml", "views/stock_release_channel.xml", ], } diff --git a/stock_release_channel_process_end_time/models/__init__.py b/stock_release_channel_process_end_time/models/__init__.py index b82444463ee..b2326b58745 100644 --- a/stock_release_channel_process_end_time/models/__init__.py +++ b/stock_release_channel_process_end_time/models/__init__.py @@ -1,2 +1,3 @@ from . import stock_picking from . import stock_release_channel +from . import res_config_settings diff --git a/stock_release_channel_process_end_time/models/res_config_settings.py b/stock_release_channel_process_end_time/models/res_config_settings.py new file mode 100644 index 00000000000..05f87c77bec --- /dev/null +++ b/stock_release_channel_process_end_time/models/res_config_settings.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +from odoo import api, fields, models, SUPERUSER_ID, _ +from odoo.exceptions import UserError + + +class ResConfigSettings(models.TransientModel): + _inherit = 'res.config.settings' + + update_scheduled_date = fields.Boolean( + help="Will update scheduled date of picking based on process end date(time).", + config_parameter='stock_release_channel_process_end_time.update_scheduled_date' + ) \ No newline at end of file diff --git a/stock_release_channel_process_end_time/models/stock_picking.py b/stock_release_channel_process_end_time/models/stock_picking.py index ceb4ed1288a..c57a0ca069a 100644 --- a/stock_release_channel_process_end_time/models/stock_picking.py +++ b/stock_release_channel_process_end_time/models/stock_picking.py @@ -61,6 +61,11 @@ def _search_schedule_date_prior_to_channel_process_end_date(self, operator, valu return [("id", operator_inselect, (query, []))] def _after_release_set_expected_date(self): + enabled_update_scheduled_date = bool( + self.env["ir.config_parameter"] + .sudo() + .get_param("stock_release_channel_process_end_time.update_scheduled_date") + ) res = super()._after_release_set_expected_date() for rec in self: # Check if a channel has been assigned to the picking and write @@ -69,6 +74,7 @@ def _after_release_set_expected_date(self): rec.release_channel_id and rec.release_channel_id.process_end_date and rec.scheduled_date != rec.release_channel_id.process_end_date + and enabled_update_scheduled_date ): rec.scheduled_date = rec.release_channel_id.process_end_date return res diff --git a/stock_release_channel_process_end_time/readme/USAGE.rst b/stock_release_channel_process_end_time/readme/USAGE.rst index 2c2db208251..3ddf7ed70b5 100644 --- a/stock_release_channel_process_end_time/readme/USAGE.rst +++ b/stock_release_channel_process_end_time/readme/USAGE.rst @@ -3,9 +3,10 @@ the timezone on the company partner. If you don't define a timezone on the warehouse(s) nor the company, the process end time on channels will be considered as UTC. - +#. Propagate/update scheduled date of picking follow process end date/time automatically + by setting "Update Scheduled Date" configuration in Settings/General Settings/Inventory #. Go To Release Channels #. Set an end time #. Wake up the channel -#. The assigned pickings have their scheduled date set at the next end time. +#. The assigned pickings have their scheduled date set at the next end time. (if enabled "Update Scheduled Date" config) diff --git a/stock_release_channel_process_end_time/static/description/index.html b/stock_release_channel_process_end_time/static/description/index.html index d0a4b7460ad..96704294ec4 100644 --- a/stock_release_channel_process_end_time/static/description/index.html +++ b/stock_release_channel_process_end_time/static/description/index.html @@ -366,8 +366,10 @@

Stock Release Channel Process End Date

-

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runbot

+

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

This module allows to set an end time on a release channel that will be transmitted on related stock pickings (on scheduled date) when the channel awakes.

That allows to use Odoo core sorting feature on stock pickings level.

@@ -388,10 +390,17 @@

Stock Release Channel Process End Date

Usage

    +
  1. Assign a timezone on the Warehouse address if defined and if needed +If you have a lot of warehouses in the same timezone, you can also define +the timezone on the company partner. +If you don’t define a timezone on the warehouse(s) nor the company, the process +end time on channels will be considered as UTC.
  2. +
  3. Propagate/update scheduled date of picking follow process end date/time automatically +by setting “Update Scheduled Date” configuration in Settings/General Settings/Inventory
  4. Go To Release Channels
  5. Set an end time
  6. Wake up the channel
  7. -
  8. The assigned pickings have their scheduled date set at the next end time.
  9. +
  10. The assigned pickings have their scheduled date set at the next end time. (if enabled “Update Scheduled Date” config)
@@ -405,7 +414,7 @@

Known issues / Roadmap

Bug Tracker

Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. -If you spotted it first, help us smashing it by providing a detailed and welcomed +If you spotted it first, help us to smash it by providing a detailed and welcomed feedback.

Do not contact contributors directly about support or help with technical issues.

diff --git a/stock_release_channel_process_end_time/tests/test_release_end_date.py b/stock_release_channel_process_end_time/tests/test_release_end_date.py index ef1a5e98c6c..e2bd038fc66 100644 --- a/stock_release_channel_process_end_time/tests/test_release_end_date.py +++ b/stock_release_channel_process_end_time/tests/test_release_end_date.py @@ -62,6 +62,9 @@ def test_channel_end_date_manual(self): @freeze_time("2023-01-27 10:00:00") def test_picking_scheduled_date(self): + self.env["ir.config_parameter"].sudo().set_param( + "stock_release_channel_process_end_time.update_scheduled_date", True + ) # Remove existing jobs as some already exists to assign pickings to channel jobs_before = self.env["queue.job"].search([]) jobs_before.unlink() diff --git a/stock_release_channel_process_end_time/views/res_config_settings.xml b/stock_release_channel_process_end_time/views/res_config_settings.xml new file mode 100644 index 00000000000..1162b49968b --- /dev/null +++ b/stock_release_channel_process_end_time/views/res_config_settings.xml @@ -0,0 +1,26 @@ + + + + stock_release_channel_end_time res.config.settings form + res.config.settings + + +
+
+
+ +
+
+
+
+
+
+
+
From 19bbe61cc182efdaf93941a1b60e15a71356d55c Mon Sep 17 00:00:00 2001 From: Diep Huu Hoang Date: Tue, 19 Sep 2023 15:09:31 +0700 Subject: [PATCH 10/10] [IMP] stock_release_channel_process_end_time: pre-commit issue --- .../models/res_config_settings.py | 10 ++++------ .../views/res_config_settings.xml | 11 +++++++---- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/stock_release_channel_process_end_time/models/res_config_settings.py b/stock_release_channel_process_end_time/models/res_config_settings.py index 05f87c77bec..e928534cbdf 100644 --- a/stock_release_channel_process_end_time/models/res_config_settings.py +++ b/stock_release_channel_process_end_time/models/res_config_settings.py @@ -1,14 +1,12 @@ -# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models, SUPERUSER_ID, _ -from odoo.exceptions import UserError +from odoo import fields, models class ResConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' + _inherit = "res.config.settings" update_scheduled_date = fields.Boolean( help="Will update scheduled date of picking based on process end date(time).", - config_parameter='stock_release_channel_process_end_time.update_scheduled_date' - ) \ No newline at end of file + config_parameter="stock_release_channel_process_end_time.update_scheduled_date", + ) diff --git a/stock_release_channel_process_end_time/views/res_config_settings.xml b/stock_release_channel_process_end_time/views/res_config_settings.xml index 1162b49968b..ae5b53cddcc 100644 --- a/stock_release_channel_process_end_time/views/res_config_settings.xml +++ b/stock_release_channel_process_end_time/views/res_config_settings.xml @@ -8,13 +8,16 @@
-
+
- +
-