diff --git a/sale_order_safe_commitment_date/README.rst b/sale_order_safe_commitment_date/README.rst new file mode 100644 index 00000000000..97f877e1bd4 --- /dev/null +++ b/sale_order_safe_commitment_date/README.rst @@ -0,0 +1,128 @@ +=============================== +Sale order safe commitment date +=============================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c47b35103c45553d6342b739f08528e0bb434daabc9e9e1794bb3d518c250424 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png + :target: https://odoo-community.org/page/development-status + :alt: Alpha +.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png + :target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html + :alt: License: LGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fsale--workflow-lightgray.png?logo=github + :target: https://github.com/OCA/sale-workflow/tree/18.0/sale_order_safe_commitment_date + :alt: OCA/sale-workflow +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/sale-workflow-18-0/sale-workflow-18-0-sale_order_safe_commitment_date + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/sale-workflow&target_branch=18.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +This module allows to avoid that the salesmen set a delivery date before +the minimum expected date. + +.. IMPORTANT:: + This is an alpha version, the data model and design can change at any time without warning. + Only for development or testing purpose, do not use in production. + `More details on development status `_ + +**Table of contents** + +.. contents:: + :local: + +Use Cases / Context +=================== + +When salesmen set a delivery date there's nothing disallowing them to +set it before the expected date computed according to the order lead +times. That can cause great issues with deliveries schedules, as those +lead times are set to ensure that the orders are delivered on time. + +Configuration +============= + +To set the sales cut-off hours: + +1. Go to *Sales > Configuration > Settings* and then to the *Shipping* + section. +2. Set the *Sales cut-off schedule*: you can create a new one or select + an existing one. +3. In that calendar, set the cut-off hours for the given days. + +Usage +===== + +To test the module: + +- Go to *Sales > Orders* and create a new quotation. +- Set whatever customer and order lines. +- Go to the *Other info* tab, section *Delivery* and set a *Delivery + date* previous to the expected date. +- A popup will raise warning about setting a delivery before the minimum + date possible to promise the delivery. +- Additionally, a warning alert will be shown on the top side of the + order advising the salesman to fix that issue. +- If the salesman ignores it, the delivery date will be set to the + minimum expected date when the order gets confirmed. + +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 +------- + +* Moduon + +Contributors +------------ + +- David Vidal (`Moduon `__) + +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-chienandalu| image:: https://github.com/chienandalu.png?size=40px + :target: https://github.com/chienandalu + :alt: chienandalu +.. |maintainer-rafaelbn| image:: https://github.com/rafaelbn.png?size=40px + :target: https://github.com/rafaelbn + :alt: rafaelbn + +Current `maintainers `__: + +|maintainer-chienandalu| |maintainer-rafaelbn| + +This module is part of the `OCA/sale-workflow `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/sale_order_safe_commitment_date/__init__.py b/sale_order_safe_commitment_date/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/sale_order_safe_commitment_date/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/sale_order_safe_commitment_date/__manifest__.py b/sale_order_safe_commitment_date/__manifest__.py new file mode 100644 index 00000000000..47742c36bf0 --- /dev/null +++ b/sale_order_safe_commitment_date/__manifest__.py @@ -0,0 +1,18 @@ +# Copyright 2025 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) +{ + "name": "Sale order safe commitment date", + "summary": "Avoid confirming a commitment date previous to the expected date", + "version": "18.0.1.0.0", + "development_status": "Alpha", + "category": "Sales Management", + "website": "https://github.com/OCA/sale-workflow", + "author": "Moduon, Odoo Community Association (OCA)", + "maintainers": ["chienandalu", "rafaelbn"], + "license": "LGPL-3", + "depends": ["sale"], + "data": [ + "views/sale_order_views.xml", + "views/res_config_settings_views.xml", + ], +} diff --git a/sale_order_safe_commitment_date/i18n/es.po b/sale_order_safe_commitment_date/i18n/es.po new file mode 100644 index 00000000000..fd2735cf6f6 --- /dev/null +++ b/sale_order_safe_commitment_date/i18n/es.po @@ -0,0 +1,71 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_safe_commitment_date +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-05-22 07:27+0000\n" +"PO-Revision-Date: 2025-08-13 17:25+0000\n" +"Last-Translator: David Vidal \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.10.4\n" + +#. module: sale_order_safe_commitment_date +#: model_terms:ir.ui.view,arch_db:sale_order_safe_commitment_date.view_order_form +msgid "" +", but it's not possible based on current lead times. Odoo will adjust the " +"date automatically. Contact your sales manager if it’s urgent." +msgstr "" +", pero no es posible según los plazos actuales. Odoo ajustará la fecha " +"automáticamente. Contacta con tu responsable si es urgente." + +#. module: sale_order_safe_commitment_date +#: model_terms:ir.ui.view,arch_db:sale_order_safe_commitment_date.view_order_form +msgid " You requested delivery on" +msgstr " Has solicitado entregar el pedido el" + +#. module: sale_order_safe_commitment_date +#: model:ir.model.fields,field_description:sale_order_safe_commitment_date.field_sale_order__date_for_commitment +msgid "Delivery date" +msgstr "Fecha de entrega" + +#. module: sale_order_safe_commitment_date +#: model:ir.model.fields,field_description:sale_order_safe_commitment_date.field_sale_order__is_commitment_date_unsafe +msgid "Is Commitment Date Unsafe" +msgstr "La fecha de entrega es insegura" + +#. module: sale_order_safe_commitment_date +#: model:ir.model,name:sale_order_safe_commitment_date.model_sale_order +msgid "Sales Order" +msgstr "Pedido de venta" + +#. module: sale_order_safe_commitment_date +#: model:ir.model.fields,help:sale_order_safe_commitment_date.field_sale_order__date_for_commitment +msgid "" +"This is the delivery date promised to the customer. If set, the delivery " +"order will be scheduled based on this date rather than product lead times." +msgstr "" +"Es la fecha de entrega prometida al cliente. Si se establece, la orden de " +"entrega se programará en función de esta fecha en lugar de los plazos de " +"entrega del producto." + +#~ msgid "" +#~ " You requested delivery on" +msgstr "" + +#. module: sale_order_safe_commitment_date +#: model:ir.model.fields,field_description:sale_order_safe_commitment_date.field_sale_order__date_for_commitment +msgid "Delivery date" +msgstr "" + +#. module: sale_order_safe_commitment_date +#: model:ir.model.fields,field_description:sale_order_safe_commitment_date.field_sale_order__is_commitment_date_unsafe +msgid "Is Commitment Date Unsafe" +msgstr "" + +#. module: sale_order_safe_commitment_date +#: model:ir.model,name:sale_order_safe_commitment_date.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: sale_order_safe_commitment_date +#: model:ir.model.fields,help:sale_order_safe_commitment_date.field_sale_order__date_for_commitment +msgid "" +"This is the delivery date promised to the customer. If set, the delivery " +"order will be scheduled based on this date rather than product lead times." +msgstr "" diff --git a/sale_order_safe_commitment_date/models/__init__.py b/sale_order_safe_commitment_date/models/__init__.py new file mode 100644 index 00000000000..9359829439e --- /dev/null +++ b/sale_order_safe_commitment_date/models/__init__.py @@ -0,0 +1,3 @@ +from . import sale_order +from . import res_config_settings +from . import res_company diff --git a/sale_order_safe_commitment_date/models/res_company.py b/sale_order_safe_commitment_date/models/res_company.py new file mode 100644 index 00000000000..573e9182cb6 --- /dev/null +++ b/sale_order_safe_commitment_date/models/res_company.py @@ -0,0 +1,39 @@ +# Copyright 2025 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) +import pytz +from dateutil.relativedelta import relativedelta + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + sales_cutoff_calendar = fields.Many2one( + comodel_name="resource.calendar", + string="Sales cut-off schedule", + ) + in_sale_cutoff_hour = fields.Boolean( + compute="_compute_in_sale_cutoff_hour", + help="Sales can't be delivered according to their lead times and will be" + "postponed for a day", + ) + + def _compute_in_sale_cutoff_hour(self): + # TODO: Maybe we should seek for the next period that isn't cutoff + self.in_sale_cutoff_hour = False + now = self.env.context.get("sale_cutoff_datetime") + for company in self.filtered("sales_cutoff_calendar"): + resource = self.env["resource.resource"] + if not now: + now = fields.Datetime.now().replace(tzinfo=pytz.utc) + now = now.astimezone(pytz.timezone(self.env.user.tz or "UTC")) + range_end = now + relativedelta(days=1, hour=0, minute=0, second=0) + interval = sorted( + company.sales_cutoff_calendar._work_intervals_batch( + now, range_end, resource + )[resource.id], + key=lambda i, now=now: abs(i[0] - now), + ) + if interval: + company.in_sale_cutoff_hour = interval[0][0] <= now diff --git a/sale_order_safe_commitment_date/models/res_config_settings.py b/sale_order_safe_commitment_date/models/res_config_settings.py new file mode 100644 index 00000000000..8812775ab95 --- /dev/null +++ b/sale_order_safe_commitment_date/models/res_config_settings.py @@ -0,0 +1,11 @@ +# Copyright 2025 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + sales_cutoff_calendar = fields.Many2one( + related="company_id.sales_cutoff_calendar", readonly=False + ) diff --git a/sale_order_safe_commitment_date/models/sale_order.py b/sale_order_safe_commitment_date/models/sale_order.py new file mode 100644 index 00000000000..50c5075718a --- /dev/null +++ b/sale_order_safe_commitment_date/models/sale_order.py @@ -0,0 +1,172 @@ +# Copyright 2025 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) +from datetime import datetime, time, timedelta + +import pytz + +from odoo import api, fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + date_for_commitment = fields.Date( + string="Delivery date", + compute="_compute_date_for_commitment", + inverse="_inverse_date_for_commitment", + readonly=False, + help="This is the delivery date promised to the customer. " + "If set, the delivery order will be scheduled based on " + "this date rather than product lead times.", + ) + expected_day = fields.Date( + compute="_compute_expected_day", + help="Based on lead time and order allowance", + ) + delivery_day = fields.Date( + compute="_compute_delivery_day", + ) + is_commitment_date_unsafe = fields.Boolean( + compute="_compute_is_commitment_date_unsafe" + ) + in_sale_cutoff_hour = fields.Boolean(compute="_compute_in_sale_cutoff_hour") + + # We need to avoid this trigger as the UI will do a circular change when we try + # to set the hour manually -> it would trigger the compute -> and the compute would + # trigger the onchange. Maybe this could be solved more properly adding a compute + # to commitment_date, but currently the side effect is minimal so we keep it simple. + # @api.depends("commitment_date") + def _compute_date_for_commitment(self): + """By default we simply get the commitment date attending to the proper tz""" + tz = pytz.timezone(self.env.user.tz or "UTC") + self.date_for_commitment = False + for order in self.filtered("commitment_date"): + commitment_utc = order.commitment_date.replace(tzinfo=pytz.utc) + order.date_for_commitment = commitment_utc.astimezone(tz).date() + + def _inverse_date_for_commitment(self): + """Always set the last possible minute of that date so users don't have to + worry about non integer lead times""" + tz = pytz.timezone(self.env.user.tz or "UTC") + for order in self: + if order.date_for_commitment: + # Compose a datetime at 00:00 in user's local time + local_dt = tz.localize( + datetime.combine(order.date_for_commitment, time(0, 0, 0)) + ) + dt_utc = local_dt.astimezone(pytz.utc) + # Store as naive UTC datetime in Odoo + order.commitment_date = dt_utc.replace(tzinfo=None) + else: + order.commitment_date = False + + @api.depends("order_line.customer_lead", "date_order", "state") + def _compute_expected_day(self): + """Get the next day based on lead times. I.e.: 0 -> today, 1 -> tomorrow, and so + on. When we're in the order cut-off window, a day will be added, so: + 0 -> tomorrow, 1 -> the day after tomorrow. + """ + # We can't just declare the field as related, as the tz computation wouldn't + # be correct. + # Prefetch indication + self.mapped("order_line") + for order in self: + if order.state == "cancel": + order.expected_day = False + continue + dates_list = order.order_line.filtered( + lambda line: not line.display_type and not line._is_delivery() + ).mapped(lambda line: line and line._expected_day()) + if dates_list: + # We can use `_select_expected_date` as it returns min or max dates + order.expected_day = order._select_expected_date(dates_list) + else: + order.expected_day = False + + def _compute_delivery_day(self): + self.delivery_day = False + for order in self.filtered(lambda x: x.state in {"draft", "sent"}): + if order.date_for_commitment: + order.delivery_day = order.date_for_commitment + continue + dates_list = order.order_line.filtered( + lambda line: not line.display_type and not line._is_delivery() + ).mapped( + lambda line: line + and line.with_context(ignore_cutoff_hour=True)._expected_day() + ) + if dates_list: + # We can use `_select_expected_date` as it returns min or max dates + order.delivery_day = order._select_expected_date(dates_list) + + @api.depends("company_id") + def _compute_in_sale_cutoff_hour(self): + self.in_sale_cutoff_hour = False + for company, orders in ( + self.filtered(lambda x: x.state in {"draft", "sent"}) + .grouped("company_id") + .items() + ): + orders.in_sale_cutoff_hour = company.in_sale_cutoff_hour + + @api.onchange("date_for_commitment") + def _onchange_date_for_commitment(self): + """React on the UI""" + self._inverse_date_for_commitment() + + @api.onchange("commitment_date", "expected_date") + def _onchange_commitment_date(self): + # Just consider the whole days + if ( + self.date_for_commitment + and self.expected_day + and self.date_for_commitment < self.expected_day + ): + return super()._onchange_commitment_date() + + @api.depends( + "date_for_commitment", + "delivery_day", + "order_line.customer_lead", + "order_line", + "state", + ) + def _compute_is_commitment_date_unsafe(self): + """A commitment date is considered unsafe if it is before the expected day as + the products won't be delivered on time.""" + self.is_commitment_date_unsafe = False + self.filtered( + lambda x: x.expected_day + and x.state in {"draft", "sent"} + and (x.date_for_commitment or x.delivery_day) + and (x.date_for_commitment or x.delivery_day) < x.expected_day + ).is_commitment_date_unsafe = True + + def action_confirm(self): + # Ensure that the deliveries get on time + unsafe_commitment_orders = self.filtered("is_commitment_date_unsafe") + for order in unsafe_commitment_orders: + order.date_for_commitment = order.expected_day + return super().action_confirm() + + +class SaleOrderLine(models.Model): + _inherit = "sale.order.line" + + def _expected_day(self) -> datetime.date: + """Similar to `_expected_date` but we return a `datetime.date`""" + self.ensure_one() + tz = pytz.timezone(self.env.user.tz or "UTC") + if self.state == "sale" and self.order_id.date_order: + order_date_utc = self.order_id.date_order.replace(tzinfo=pytz.utc) + order_date = order_date_utc.astimezone(tz).date() + else: + order_date = fields.Date.today() + customer_lead = self.customer_lead + # Consider here the cut-off window + if self.env.company.in_sale_cutoff_hour and not self.env.context.get( + "ignore_cutoff_hour" + ): + # TODO: Consider succesive cut-offs. I.e: weekend periods or holidays + customer_lead += 1 + return order_date + timedelta(days=customer_lead or 0.0) diff --git a/sale_order_safe_commitment_date/pyproject.toml b/sale_order_safe_commitment_date/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/sale_order_safe_commitment_date/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sale_order_safe_commitment_date/readme/CONFIGURE.md b/sale_order_safe_commitment_date/readme/CONFIGURE.md new file mode 100644 index 00000000000..e10990253d5 --- /dev/null +++ b/sale_order_safe_commitment_date/readme/CONFIGURE.md @@ -0,0 +1,5 @@ +To set the sales cut-off hours: + +1. Go to _Sales > Configuration > Settings_ and then to the _Shipping_ section. +1. Set the *Sales cut-off schedule*: you can create a new one or select an existing one. +1. In that calendar, set the cut-off hours for the given days. diff --git a/sale_order_safe_commitment_date/readme/CONTEXT.md b/sale_order_safe_commitment_date/readme/CONTEXT.md new file mode 100644 index 00000000000..9ec41a88ec4 --- /dev/null +++ b/sale_order_safe_commitment_date/readme/CONTEXT.md @@ -0,0 +1,4 @@ +When salesmen set a delivery date there's nothing disallowing them to set it before +the expected date computed according to the order lead times. That can cause great issues +with deliveries schedules, as those lead times are set to ensure that the orders are +delivered on time. diff --git a/sale_order_safe_commitment_date/readme/CONTRIBUTORS.md b/sale_order_safe_commitment_date/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..e278905bd79 --- /dev/null +++ b/sale_order_safe_commitment_date/readme/CONTRIBUTORS.md @@ -0,0 +1 @@ +- David Vidal ([Moduon](https://www.moduon.team/)) diff --git a/sale_order_safe_commitment_date/readme/DESCRIPTION.md b/sale_order_safe_commitment_date/readme/DESCRIPTION.md new file mode 100644 index 00000000000..dd8d57b19a7 --- /dev/null +++ b/sale_order_safe_commitment_date/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module allows to avoid that the salesmen set a delivery date before the minimum +expected date. diff --git a/sale_order_safe_commitment_date/readme/USAGE.md b/sale_order_safe_commitment_date/readme/USAGE.md new file mode 100644 index 00000000000..c3aad3f4fd5 --- /dev/null +++ b/sale_order_safe_commitment_date/readme/USAGE.md @@ -0,0 +1,12 @@ +To test the module: + +- Go to *Sales > Orders* and create a new quotation. +- Set whatever customer and order lines. +- Go to the *Other info* tab, section *Delivery* and set a *Delivery date* previous to + the expected date. +- A popup will raise warning about setting a delivery before the minimum date possible + to promise the delivery. +- Additionally, a warning alert will be shown on the top side of the order advising the + salesman to fix that issue. +- If the salesman ignores it, the delivery date will be set to the minimum expected date + when the order gets confirmed. diff --git a/sale_order_safe_commitment_date/static/description/icon.png b/sale_order_safe_commitment_date/static/description/icon.png new file mode 100644 index 00000000000..1dcc49c24f3 Binary files /dev/null and b/sale_order_safe_commitment_date/static/description/icon.png differ diff --git a/sale_order_safe_commitment_date/static/description/index.html b/sale_order_safe_commitment_date/static/description/index.html new file mode 100644 index 00000000000..48c4fd868d2 --- /dev/null +++ b/sale_order_safe_commitment_date/static/description/index.html @@ -0,0 +1,469 @@ + + + + + +Sale order safe commitment date + + + +
+

Sale order safe commitment date

+ + +

Alpha License: LGPL-3 OCA/sale-workflow Translate me on Weblate Try me on Runboat

+

This module allows to avoid that the salesmen set a delivery date before +the minimum expected date.

+
+

Important

+

This is an alpha version, the data model and design can change at any time without warning. +Only for development or testing purpose, do not use in production. +More details on development status

+
+

Table of contents

+ +
+

Use Cases / Context

+

When salesmen set a delivery date there’s nothing disallowing them to +set it before the expected date computed according to the order lead +times. That can cause great issues with deliveries schedules, as those +lead times are set to ensure that the orders are delivered on time.

+
+
+

Configuration

+

To set the sales cut-off hours:

+
    +
  1. Go to Sales > Configuration > Settings and then to the Shipping +section.
  2. +
  3. Set the Sales cut-off schedule: you can create a new one or select +an existing one.
  4. +
  5. In that calendar, set the cut-off hours for the given days.
  6. +
+
+
+

Usage

+

To test the module:

+
    +
  • Go to Sales > Orders and create a new quotation.
  • +
  • Set whatever customer and order lines.
  • +
  • Go to the Other info tab, section Delivery and set a Delivery +date previous to the expected date.
  • +
  • A popup will raise warning about setting a delivery before the minimum +date possible to promise the delivery.
  • +
  • Additionally, a warning alert will be shown on the top side of the +order advising the salesman to fix that issue.
  • +
  • If the salesman ignores it, the delivery date will be set to the +minimum expected date when the order gets confirmed.
  • +
+
+
+

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

+
    +
  • Moduon
  • +
+
+
+

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 maintainers:

+

chienandalu rafaelbn

+

This module is part of the OCA/sale-workflow project on GitHub.

+

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

+
+
+
+ + diff --git a/sale_order_safe_commitment_date/tests/__init__.py b/sale_order_safe_commitment_date/tests/__init__.py new file mode 100644 index 00000000000..a537c350453 --- /dev/null +++ b/sale_order_safe_commitment_date/tests/__init__.py @@ -0,0 +1 @@ +from . import test_sale_order_safe_commitment_date diff --git a/sale_order_safe_commitment_date/tests/test_sale_order_safe_commitment_date.py b/sale_order_safe_commitment_date/tests/test_sale_order_safe_commitment_date.py new file mode 100644 index 00000000000..6b0705f9c6d --- /dev/null +++ b/sale_order_safe_commitment_date/tests/test_sale_order_safe_commitment_date.py @@ -0,0 +1,40 @@ +# Copyright 2025 Moduon Team S.L. +# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl-3.0) +from freezegun import freeze_time + +from odoo.tests import Form, TransactionCase + + +@freeze_time("2018-01-11") +class TestSaleOrderSafeCommitmentDate(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + sale_form = Form(cls.env["sale.order"]) + sale_form.partner_id = cls.env["res.partner"].create({"name": "Mr. Odoo"}) + with sale_form.order_line.new() as line: + line.product_id = cls.env["product.product"].create({"name": "Test thingy"}) + cls.sale_order = sale_form.save() + + def test_unsafe_commitment_date(self): + """Time is freezed at 2018-01-11. As there aren't lead times, expected date + will be that date. Any date previous to that one is unsafe""" + self.assertFalse(self.sale_order.is_commitment_date_unsafe) + # Always in the past. Impossible to fulfill + self.sale_order.commitment_date = "2018-01-10" + self.assertTrue(self.sale_order.is_commitment_date_unsafe) + self.sale_order.action_confirm() + self.assertFalse(self.sale_order.is_commitment_date_unsafe) + self.assertEqual(self.sale_order.commitment_date, self.sale_order.expected_date) + + def test_safe_commitment_date(self): + """Time is freezed at 2018-01-11. There aren't lead times and the commitment + date is set after the expected date""" + self.assertFalse(self.sale_order.is_commitment_date_unsafe) + self.sale_order.commitment_date = "2018-01-20" + self.assertFalse(self.sale_order.is_commitment_date_unsafe) + self.sale_order.action_confirm() + self.assertFalse(self.sale_order.is_commitment_date_unsafe) + self.assertEqual( + self.sale_order.commitment_date.date().isoformat(), "2018-01-20" + ) diff --git a/sale_order_safe_commitment_date/views/res_config_settings_views.xml b/sale_order_safe_commitment_date/views/res_config_settings_views.xml new file mode 100644 index 00000000000..d3a84f4cad7 --- /dev/null +++ b/sale_order_safe_commitment_date/views/res_config_settings_views.xml @@ -0,0 +1,18 @@ + + + + res.config.settings + + + + + + + + + + + diff --git a/sale_order_safe_commitment_date/views/sale_order_views.xml b/sale_order_safe_commitment_date/views/sale_order_views.xml new file mode 100644 index 00000000000..162cdc54336 --- /dev/null +++ b/sale_order_safe_commitment_date/views/sale_order_views.xml @@ -0,0 +1,73 @@ + + + + sale.order + + + + + + + + + + +