diff --git a/setup/stock_release_channel_shipment_lead_time/odoo/addons/stock_release_channel_shipment_lead_time b/setup/stock_release_channel_shipment_lead_time/odoo/addons/stock_release_channel_shipment_lead_time new file mode 120000 index 0000000000..9d42bf0a13 --- /dev/null +++ b/setup/stock_release_channel_shipment_lead_time/odoo/addons/stock_release_channel_shipment_lead_time @@ -0,0 +1 @@ +../../../../stock_release_channel_shipment_lead_time \ No newline at end of file diff --git a/setup/stock_release_channel_shipment_lead_time/setup.py b/setup/stock_release_channel_shipment_lead_time/setup.py new file mode 100644 index 0000000000..28c57bb640 --- /dev/null +++ b/setup/stock_release_channel_shipment_lead_time/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +) diff --git a/stock_release_channel_shipment_lead_time/README.rst b/stock_release_channel_shipment_lead_time/README.rst new file mode 100644 index 0000000000..20d47beaa1 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/README.rst @@ -0,0 +1,96 @@ +================================== +Release channel shipment lead time +================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:87e7e2feb009b92bbd09928c5595063318fc6e124884cb346f6b57e459cfb917 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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_shipment_lead_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_shipment_lead_time + :alt: Translate me on Weblate +.. |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| + +This module allows to set delivery lead time for release channel. +When setting on shipment lead time on release channel, the shipment date is computed automatically base on +lead time days or warehouse calendar. There are 2 main enhanced features with this module: +- Filter deliveries base on shipment date defined on a channel +- Set delivery date for shipment advice if picking is linked to a channel + +**Table of contents** + +.. contents:: + :local: + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* Camptocamp +* BCIM + +Contributors +~~~~~~~~~~~~ + +* `Trobz `_: + + * Hoang Diep + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by Camptocamp + +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-jbaudoux| image:: https://github.com/jbaudoux.png?size=40px + :target: https://github.com/jbaudoux + :alt: jbaudoux + +Current `maintainer `__: + +|maintainer-jbaudoux| + +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_shipment_lead_time/__init__.py b/stock_release_channel_shipment_lead_time/__init__.py new file mode 100644 index 0000000000..aee8895e7a --- /dev/null +++ b/stock_release_channel_shipment_lead_time/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizards diff --git a/stock_release_channel_shipment_lead_time/__manifest__.py b/stock_release_channel_shipment_lead_time/__manifest__.py new file mode 100644 index 0000000000..816d79be72 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/__manifest__.py @@ -0,0 +1,25 @@ +# Copyright 2023 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) + +{ + "name": "Release channel shipment lead time", + "summary": "Release channel with shipment lead time", + "version": "16.0.1.0.0", + "development_status": "Beta", + "license": "AGPL-3", + "author": "Camptocamp, BCIM, Odoo Community Association (OCA)", + "maintainers": ["jbaudoux"], + "website": "https://github.com/OCA/wms", + "depends": [ + "web", + "stock_release_channel", # OCA/wms + "stock_release_channel_process_end_time", # OCA/wms + "stock_warehouse_calendar", # OCA/stock-logistics-warehouse + "shipment_advice_planner", # OCA/stock-logistics-transport + ], + "data": [ + "views/shipment_advice_views.xml", + "views/stock_release_channel_views.xml", + ], + "installable": True, +} diff --git a/stock_release_channel_shipment_lead_time/models/__init__.py b/stock_release_channel_shipment_lead_time/models/__init__.py new file mode 100644 index 0000000000..961f47bde7 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/models/__init__.py @@ -0,0 +1,3 @@ +from . import stock_release_channel +from . import stock_picking +from . import shipment_advice diff --git a/stock_release_channel_shipment_lead_time/models/shipment_advice.py b/stock_release_channel_shipment_lead_time/models/shipment_advice.py new file mode 100644 index 0000000000..92d40e1c84 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/models/shipment_advice.py @@ -0,0 +1,18 @@ +# Copyright 2023 Camptocamp SA +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import fields, models + + +class ShipmentAdvice(models.Model): + _inherit = "shipment.advice" + + delivery_date = fields.Date( + states={ + "draft": [("readonly", False)], + "confirmed": [("readonly", False)], + "in_progress": [("readonly", False)], + }, + readonly=True, + help=("maxium shipment date on the channel of related pickings"), + ) diff --git a/stock_release_channel_shipment_lead_time/models/stock_picking.py b/stock_release_channel_shipment_lead_time/models/stock_picking.py new file mode 100644 index 0000000000..4e2da8b3b1 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/models/stock_picking.py @@ -0,0 +1,32 @@ +# Copyright 2023 Camptocamp +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class StockPicking(models.Model): + + _inherit = "stock.picking" + + def _get_release_channel_possible_candidate_domain(self): + """ + override to exclude deliveries (OUT pickings) where + the date_deadline is after the shipment date. + """ + self.ensure_one() + domain = super()._get_release_channel_possible_candidate_domain() + + # date_deadline is datetime UTC => convert to timezone user to compare + # with shipment_date is date + if self.date_deadline: + date_deadline = fields.Datetime.context_timestamp( + self, self.date_deadline + ).date() + + domain.extend( + [ + "|", + ("shipment_date", "=", False), + ("shipment_date", ">=", date_deadline), + ] + ) + return domain diff --git a/stock_release_channel_shipment_lead_time/models/stock_release_channel.py b/stock_release_channel_shipment_lead_time/models/stock_release_channel.py new file mode 100644 index 0000000000..e113394402 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/models/stock_release_channel.py @@ -0,0 +1,47 @@ +# Copyright 2023 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +from datetime import timedelta + +from odoo import api, fields, models + + +class StockReleaseChannel(models.Model): + _inherit = "stock.release.channel" + + shipment_lead_time = fields.Integer(help="Shipment Lead Time (days)") + shipment_date = fields.Date( + compute="_compute_shipment_date", + store=True, + help=( + "if no warehouse or no calendar on the warehouse:" + "process end date + shipment lead time." + "Otherwise, it's counted by calendar included leaves:" + "number of days = lead time + 1" + ), + ) + + @api.depends( + "process_end_date", + "shipment_lead_time", + "warehouse_id", + "warehouse_id.calendar_id", + ) + def _compute_shipment_date(self): + """ + if no warehouse or no calendar on the warehouse: + process end date + lead time + else: use calendar.plan_days(days, date_from, compute_leaves=True) + where days is amount of required open days (= lead time + 1) + """ + for channel in self: + shipment_date = False + if channel.process_end_date: + shipment_date = channel.process_end_date + timedelta( + days=channel.shipment_lead_time + ) + if channel.warehouse_id.calendar_id: + days = channel.shipment_lead_time + 1 + shipment_date = channel.warehouse_id.calendar_id.plan_days( + days, channel.process_end_date, compute_leaves=True + ) + channel.shipment_date = shipment_date diff --git a/stock_release_channel_shipment_lead_time/readme/CONTRIBUTORS.rst b/stock_release_channel_shipment_lead_time/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..cb0728bb53 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/readme/CONTRIBUTORS.rst @@ -0,0 +1,3 @@ +* `Trobz `_: + + * Hoang Diep diff --git a/stock_release_channel_shipment_lead_time/readme/CREDITS.rst b/stock_release_channel_shipment_lead_time/readme/CREDITS.rst new file mode 100644 index 0000000000..934a2b2dc6 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/readme/CREDITS.rst @@ -0,0 +1 @@ +The development of this module has been financially supported by Camptocamp diff --git a/stock_release_channel_shipment_lead_time/readme/DESCRIPTION.rst b/stock_release_channel_shipment_lead_time/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..0165c5b0e2 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/readme/DESCRIPTION.rst @@ -0,0 +1,5 @@ +This module allows to set delivery lead time for release channel. +When setting on shipment lead time on release channel, the shipment date is computed automatically base on +lead time days or warehouse calendar. There are 2 main enhanced features with this module: +- Filter deliveries base on shipment date defined on a channel +- Set delivery date for shipment advice if picking is linked to a channel diff --git a/stock_release_channel_shipment_lead_time/static/description/index.html b/stock_release_channel_shipment_lead_time/static/description/index.html new file mode 100644 index 0000000000..a8fb9a610a --- /dev/null +++ b/stock_release_channel_shipment_lead_time/static/description/index.html @@ -0,0 +1,439 @@ + + + + + + +Release channel shipment lead time + + + +
+

Release channel shipment lead time

+ + +

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

+

This module allows to set delivery lead time for release channel. +When setting on shipment lead time on release channel, the shipment date is computed automatically base on +lead time days or warehouse calendar. There are 2 main enhanced features with this module: +- Filter deliveries base on shipment date defined on a channel +- Set delivery date for shipment advice if picking is linked to a channel

+

Table of contents

+ +
+

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 to smash it by providing a detailed and welcomed +feedback.

+

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

+
+
+

Credits

+
+

Authors

+
    +
  • Camptocamp
  • +
  • BCIM
  • +
+
+
+

Contributors

+ +
+
+

Other credits

+

The development of this module has been financially supported by Camptocamp

+
+
+

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:

+

jbaudoux

+

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_shipment_lead_time/tests/__init__.py b/stock_release_channel_shipment_lead_time/tests/__init__.py new file mode 100644 index 0000000000..0e45eb99ad --- /dev/null +++ b/stock_release_channel_shipment_lead_time/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_assign_channel_shipment_date +from . import test_release_shipment_date diff --git a/stock_release_channel_shipment_lead_time/tests/test_assign_channel_shipment_date.py b/stock_release_channel_shipment_lead_time/tests/test_assign_channel_shipment_date.py new file mode 100644 index 0000000000..83656bed56 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/tests/test_assign_channel_shipment_date.py @@ -0,0 +1,95 @@ +# Copyright 2023 Camptocamp +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) + + +from odoo import fields +from odoo.tests.common import Form + +from odoo.addons.queue_job.tests.common import trap_jobs +from odoo.addons.stock_release_channel.tests.common import ReleaseChannelCase + + +class TestReleaseChannel(ReleaseChannelCase): + def test_shipment_advice_planner(self): + # deactive all existing channels before testing + all_default_channel = self.env["stock.release.channel"].search([]) + all_default_channel.action_sleep() + + # create a testing lead time without calendar + # => shipment_date = 2023-07-06 + self._create_channel( + name="Test Shipment Lead Time", + sequence=1, + process_end_date="2023-06-30", + shipment_lead_time=6, + ) + + # create a picking with deadline <= channel shipment date = 2023-07-06 + self._update_qty_in_location( + self.wh.out_type_id.default_location_src_id, + self.product1, + 20, + ) + move = self._create_single_move(self.product1, 10) + move.date_deadline = "2023-07-03 00:00:00" + move.picking_id.action_assign() + with trap_jobs() as trap: + picking = move.picking_id + picking._delay_assign_release_channel() + trap.assert_jobs_count(1, only=picking.assign_release_channel) + trap.perform_enqueued_jobs() + + self.context = { + "active_ids": [picking.id], + "active_model": "stock.picking", + } + self.wizard_form = Form( + self.env["shipment.advice.planner"].with_context(**self.context) + ) + self.wizard_form.warehouse_id = self.wh + self.wizard_form.picking_to_plan_ids.add(picking) + wizard = self.wizard_form.save() + action = wizard.button_plan_shipments() + shipment = self.env[action.get("res_model")].search(action.get("domain")) + self.assertEqual(len(shipment), 1) + self.assertEqual( + "2023-07-06", + fields.Date.to_string(shipment.delivery_date), + ) + + def test_assign_channel_with_shipment_date(self): + + # deactive all existing channels before testing + all_default_channel = self.env["stock.release.channel"].search([]) + all_default_channel.action_sleep() + + # create a testing lead time without calendar + # => shipment_date = 2023-07-06 + self._create_channel( + name="Test Shipment Lead Time", + sequence=1, + process_end_date="2023-06-30", + shipment_lead_time=6, + ) + + # create a picking with deadline <= channel shipment date = 2023-07-06 + move = self._create_single_move(self.product1, 10) + move.date_deadline = "2023-07-06 00:00:00" + with trap_jobs() as trap: + picking = move.picking_id + picking._delay_assign_release_channel() + trap.assert_jobs_count(1, only=picking.assign_release_channel) + trap.perform_enqueued_jobs() + + self.assertTrue(picking.release_channel_id) + + # create a picking with deadline > channel shipment date = 2023-07-06 + move = self._create_single_move(self.product1, 10) + move.date_deadline = "2023-07-07 00:00:00" + with trap_jobs() as trap: + picking = move.picking_id + picking._delay_assign_release_channel() + trap.assert_jobs_count(1, only=picking.assign_release_channel) + trap.perform_enqueued_jobs() + + self.assertFalse(picking.release_channel_id) diff --git a/stock_release_channel_shipment_lead_time/tests/test_release_shipment_date.py b/stock_release_channel_shipment_lead_time/tests/test_release_shipment_date.py new file mode 100644 index 0000000000..a1755ea2c6 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/tests/test_release_shipment_date.py @@ -0,0 +1,30 @@ +# Copyright 2023 Camptocamp +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). +from odoo import fields + +from odoo.addons.stock_release_channel.tests.common import ChannelReleaseCase + + +class TestChannelReleaseShipmentLeadTime(ChannelReleaseCase): + def test_shipment_date(self): + self.channel.warehouse_id.calendar_id = False + self.channel.process_end_time = 12 + self.channel.process_end_date = "2023-09-11" + self.channel.shipment_lead_time = 4 + self.assertEqual( + "2023-09-15", + fields.Date.to_string(self.channel.shipment_date), + ) + + def test_shipment_date_with_calendar(self): + self.channel.warehouse_id = self.wh + self.channel.warehouse_id.calendar_id = self.env.ref( + "resource.resource_calendar_std" + ) + self.channel.process_end_time = 12 + self.channel.process_end_date = "2023-06-30" + self.channel.shipment_lead_time = 6 + self.assertEqual( + "2023-07-10", + fields.Date.to_string(self.channel.shipment_date), + ) diff --git a/stock_release_channel_shipment_lead_time/views/shipment_advice_views.xml b/stock_release_channel_shipment_lead_time/views/shipment_advice_views.xml new file mode 100644 index 0000000000..2951dd3a97 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/views/shipment_advice_views.xml @@ -0,0 +1,13 @@ + + + + shipment.advice.form + shipment.advice + + + + + + + + diff --git a/stock_release_channel_shipment_lead_time/views/stock_release_channel_views.xml b/stock_release_channel_shipment_lead_time/views/stock_release_channel_views.xml new file mode 100644 index 0000000000..e9055ac885 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/views/stock_release_channel_views.xml @@ -0,0 +1,19 @@ + + + + + stock.release.channel.form + stock.release.channel + + + + + + + + + + diff --git a/stock_release_channel_shipment_lead_time/wizards/__init__.py b/stock_release_channel_shipment_lead_time/wizards/__init__.py new file mode 100644 index 0000000000..f1f4bd57b3 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/wizards/__init__.py @@ -0,0 +1 @@ +from . import shipment_advice_planner diff --git a/stock_release_channel_shipment_lead_time/wizards/shipment_advice_planner.py b/stock_release_channel_shipment_lead_time/wizards/shipment_advice_planner.py new file mode 100644 index 0000000000..0cd6e03cf8 --- /dev/null +++ b/stock_release_channel_shipment_lead_time/wizards/shipment_advice_planner.py @@ -0,0 +1,26 @@ +# Copyright 2023 Camptocamp +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import models + + +class ShipmentAdvicePlanner(models.TransientModel): + _inherit = "shipment.advice.planner" + + def _prepare_shipment_advice_simple_vals_list(self, picking_type, pickings_to_plan): + """ + override to set delivery_date in vals as shipment date of + release channel + - select max shipment date in the list of picking in case + there are many pickings + """ + self.ensure_one() + res = super()._prepare_shipment_advice_simple_vals_list( + picking_type, pickings_to_plan + ) + for vals in res: + shipment_dates = pickings_to_plan.mapped("release_channel_id").mapped( + "shipment_date" + ) + vals["delivery_date"] = shipment_dates and max(shipment_dates) or False + return res