diff --git a/sale_order_compute_commitment_date/README.rst b/sale_order_compute_commitment_date/README.rst new file mode 100644 index 00000000000..403ef262817 --- /dev/null +++ b/sale_order_compute_commitment_date/README.rst @@ -0,0 +1,106 @@ +================================== +Sale Order Compute Commitment Date +================================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:f77cbe11285d1aff1796eee9fac951f13819b1741c0660524121c96d6fb1f610 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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-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_compute_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_compute_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 automatically computes sale orders commitment date based on +the product template sale_delay and product attribute variant lead time +fields. + +**Table of contents** + +.. contents:: + :local: + +Configuration +============= + +To configure this module, you need to: + +- Create a product. +- Set its delay time on the "Inventory" tab. +- If you want product variants to increase the commitment date, check + the Extend Lead Time boolean. +- Now configure a variant for the product and set its lead time. +- Create a sales order and add the product. +- On the "Other Information" tab you will see the commitment date its + automatically computed. +- This module relies on the resource calendar to calculate dates, + including both working days and any configured leave days. +- Once the sale order is confirmed, it computes again the commitment + date. + +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 +------- + +* APSL - Nagarro + +Contributors +------------ + +- ``APSL-Nagarro ``\ \_: + + - Miquel Pascual miquel.pascual@nagarro.com + +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-mpascuall| image:: https://github.com/mpascuall.png?size=40px + :target: https://github.com/mpascuall + :alt: mpascuall + +Current `maintainer `__: + +|maintainer-mpascuall| + +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_compute_commitment_date/__init__.py b/sale_order_compute_commitment_date/__init__.py new file mode 100644 index 00000000000..306f9279d35 --- /dev/null +++ b/sale_order_compute_commitment_date/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2025 APSL Nagarro +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import models diff --git a/sale_order_compute_commitment_date/__manifest__.py b/sale_order_compute_commitment_date/__manifest__.py new file mode 100644 index 00000000000..9f6ffbf2523 --- /dev/null +++ b/sale_order_compute_commitment_date/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2025 APSL Nagarro +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +{ + "name": "Sale Order Compute Commitment Date", + "version": "18.0.1.0.0", + "category": "Sales Management", + "summary": "Automatically computes the commitment date based on product" + "lead times and attributes", + "author": "APSL - Nagarro, Odoo Community Association (OCA)", + "maintainers": ["mpascuall"], + "website": "https://github.com/OCA/sale-workflow", + "license": "LGPL-3", + "depends": ["base", "sale_management", "stock"], + "data": [ + "views/product_attribute_value.xml", + "views/product_template.xml", + ], + "installable": True, + "application": False, +} diff --git a/sale_order_compute_commitment_date/l10n/es.po b/sale_order_compute_commitment_date/l10n/es.po new file mode 100644 index 00000000000..2dfdbc50737 --- /dev/null +++ b/sale_order_compute_commitment_date/l10n/es.po @@ -0,0 +1,71 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * sale_order_compute_commitment_date +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 18.0+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-06-26 07:21+0000\n" +"PO-Revision-Date: 2025-06-26 07:21+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: sale_order_compute_commitment_date +#: model:ir.model,name:sale_order_compute_commitment_date.model_product_attribute_value +msgid "Attribute Value" +msgstr "Valor del Atributo" + +#. module: sale_order_compute_commitment_date +#: model:ir.model.fields,field_description:sale_order_compute_commitment_date.field_sale_order__commitment_date +msgid "Delivery Date" +msgstr "Fecha de entrega" + +#. module: sale_order_compute_commitment_date +#: model:ir.model.fields,field_description:sale_order_compute_commitment_date.field_product_product__extend_lead_time +#: model:ir.model.fields,field_description:sale_order_compute_commitment_date.field_product_template__extend_lead_time +msgid "Extend Lead Time" +msgstr "Tiempo de Entrega Extendido" + +#. module: sale_order_compute_commitment_date +#: model:ir.model.fields,help:sale_order_compute_commitment_date.field_product_product__extend_lead_time +#: model:ir.model.fields,help:sale_order_compute_commitment_date.field_product_template__extend_lead_time +msgid "" +"If checked, the attribute lead time will be taken into account to calculate " +"the final lead time." +msgstr "Si está marcado, el tiempo de entrega del atributo se tendrá " +"en cuenta para calcular el tiempo de entrega final." + +#. module: sale_order_compute_commitment_date +#: model:ir.model.fields,field_description:sale_order_compute_commitment_date.field_product_attribute_value__lead_time +msgid "Lead Time" +msgstr "Tiempo de Entrega" + +#. module: sale_order_compute_commitment_date +#: model:ir.model,name:sale_order_compute_commitment_date.model_product_template +msgid "Product" +msgstr "Producto" + +#. module: sale_order_compute_commitment_date +#: model:ir.model,name:sale_order_compute_commitment_date.model_sale_order +msgid "Sales Order" +msgstr "Pedido de venta" + +#. module: sale_order_compute_commitment_date +#: model:ir.model.fields,help:sale_order_compute_commitment_date.field_product_attribute_value__lead_time +msgid "The number of days this attribute adds to the product's lead time." +msgstr "El número de días que este atributo añade al tiempo de entrega del producto." + +#. module: sale_order_compute_commitment_date +#: model:ir.model.fields,help:sale_order_compute_commitment_date.field_sale_order__commitment_date +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 "" +"Ésta 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." diff --git a/sale_order_compute_commitment_date/models/__init__.py b/sale_order_compute_commitment_date/models/__init__.py new file mode 100644 index 00000000000..2e5a5a50844 --- /dev/null +++ b/sale_order_compute_commitment_date/models/__init__.py @@ -0,0 +1,6 @@ +# Copyright 2025 APSL Nagarro +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import product_template +from . import product_attribute_value +from . import sale_order diff --git a/sale_order_compute_commitment_date/models/product_attribute_value.py b/sale_order_compute_commitment_date/models/product_attribute_value.py new file mode 100644 index 00000000000..4550e392be6 --- /dev/null +++ b/sale_order_compute_commitment_date/models/product_attribute_value.py @@ -0,0 +1,12 @@ +# Copyright 2025 APSL Nagarro +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ProductAttributeValue(models.Model): + _inherit = "product.attribute.value" + + lead_time = fields.Integer( + help="The number of days this attribute adds to the product's lead time.", + ) diff --git a/sale_order_compute_commitment_date/models/product_template.py b/sale_order_compute_commitment_date/models/product_template.py new file mode 100644 index 00000000000..4564b3e9c7c --- /dev/null +++ b/sale_order_compute_commitment_date/models/product_template.py @@ -0,0 +1,13 @@ +# Copyright 2025 APSL Nagarro +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from odoo import fields, models + + +class ProductTemplate(models.Model): + _inherit = "product.template" + + extend_lead_time = fields.Boolean( + help="If checked, the attribute lead time will be " + "taken into account to calculate the final lead time.", + ) diff --git a/sale_order_compute_commitment_date/models/sale_order.py b/sale_order_compute_commitment_date/models/sale_order.py new file mode 100644 index 00000000000..37b96bb40bf --- /dev/null +++ b/sale_order_compute_commitment_date/models/sale_order.py @@ -0,0 +1,91 @@ +# Copyright 2025 APSL Nagarro +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from datetime import timedelta + +from pytz import timezone, utc + +from odoo import api, fields, models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + commitment_date = fields.Datetime( + "Delivery Date", + compute="_compute_commitment_date", + store=True, + 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.", + ) + + @api.depends( + "order_line.product_id", + "order_line.product_id.sale_delay", + "order_line.product_id.product_tmpl_id.extend_lead_time", + "order_line.product_id.product_template_attribute_value_ids.product_attribute_value_id.lead_time", + "date_order", + "state", + "company_id.resource_calendar_id.attendance_ids.dayofweek", + "company_id.resource_calendar_id.leave_ids", + ) + def _compute_commitment_date(self): + for order in self: + max_total_lead_time = 0 + + for line in order.order_line: + product_template = line.product_id.product_tmpl_id + + current_line_lead_time = ( + product_template.sale_delay + if product_template.sale_delay is not None + else 0 + ) + + if product_template.extend_lead_time: + for ptav in line.product_id.product_template_attribute_value_ids: + if ptav.product_attribute_value_id.lead_time: + current_line_lead_time += ( + ptav.product_attribute_value_id.lead_time + ) + + if current_line_lead_time > max_total_lead_time: + max_total_lead_time = current_line_lead_time + + base_date_for_calculation = order.date_order + + if base_date_for_calculation: + order.commitment_date = order._get_date_with_lead_time_from_calendar( + base_date_for_calculation, max_total_lead_time + ) + else: + order.commitment_date = False + + def _get_date_with_lead_time_from_calendar(self, base_datetime, lead_time_days): + self.ensure_one() + + calendar = self.company_id.resource_calendar_id + + if not calendar: + return base_datetime + timedelta(days=lead_time_days) + + hours_per_day = calendar.hours_per_day if calendar.hours_per_day else 8.0 + hours_to_add = lead_time_days * hours_per_day + + calendar_tz = timezone(calendar.tz) if calendar.tz else timezone("UTC") + + if not base_datetime.tzinfo: + base_datetime_aware = utc.localize(base_datetime) + else: + base_datetime_aware = base_datetime + + base_dt_in_calendar_tz = base_datetime_aware.astimezone(calendar_tz) + naive_base_dt = base_dt_in_calendar_tz.replace(tzinfo=None) + + final_datetime_aware = calendar.plan_hours( + hours_to_add, naive_base_dt, compute_leaves=True + ) + + return final_datetime_aware.astimezone(timezone("UTC")).replace(tzinfo=None) diff --git a/sale_order_compute_commitment_date/pyproject.toml b/sale_order_compute_commitment_date/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/sale_order_compute_commitment_date/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/sale_order_compute_commitment_date/readme/CONFIGURE.md b/sale_order_compute_commitment_date/readme/CONFIGURE.md new file mode 100644 index 00000000000..d66188407b2 --- /dev/null +++ b/sale_order_compute_commitment_date/readme/CONFIGURE.md @@ -0,0 +1,10 @@ +To configure this module, you need to: + +- Create a product. +- Set its delay time on the "Inventory" tab. +- If you want product variants to increase the commitment date, check the Extend Lead Time boolean. +- Now configure a variant for the product and set its lead time. +- Create a sales order and add the product. +- On the "Other Information" tab you will see the commitment date its automatically computed. +- This module relies on the resource calendar to calculate dates, including both working days and any configured leave days. +- Once the sale order is confirmed, it computes again the commitment date. \ No newline at end of file diff --git a/sale_order_compute_commitment_date/readme/CONTRIBUTORS.md b/sale_order_compute_commitment_date/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..128796742ef --- /dev/null +++ b/sale_order_compute_commitment_date/readme/CONTRIBUTORS.md @@ -0,0 +1,2 @@ +* `APSL-Nagarro `_: + * Miquel Pascual \ No newline at end of file diff --git a/sale_order_compute_commitment_date/readme/DESCRIPTION.md b/sale_order_compute_commitment_date/readme/DESCRIPTION.md new file mode 100644 index 00000000000..4ca3c4383d2 --- /dev/null +++ b/sale_order_compute_commitment_date/readme/DESCRIPTION.md @@ -0,0 +1,2 @@ +This module automatically computes sale orders commitment date based on the product template sale_delay +and product attribute variant lead time fields. \ No newline at end of file diff --git a/sale_order_compute_commitment_date/static/description/index.html b/sale_order_compute_commitment_date/static/description/index.html new file mode 100644 index 00000000000..196ef66f461 --- /dev/null +++ b/sale_order_compute_commitment_date/static/description/index.html @@ -0,0 +1,449 @@ + + + + + +Sale Order Compute Commitment Date + + + +
+

Sale Order Compute Commitment Date

+ + +

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

+

This module automatically computes sale orders commitment date based on +the product template sale_delay and product attribute variant lead time +fields.

+

Table of contents

+ +
+

Configuration

+

To configure this module, you need to:

+
    +
  • Create a product.
  • +
  • Set its delay time on the “Inventory” tab.
  • +
  • If you want product variants to increase the commitment date, check +the Extend Lead Time boolean.
  • +
  • Now configure a variant for the product and set its lead time.
  • +
  • Create a sales order and add the product.
  • +
  • On the “Other Information” tab you will see the commitment date its +automatically computed.
  • +
  • This module relies on the resource calendar to calculate dates, +including both working days and any configured leave days.
  • +
  • Once the sale order is confirmed, it computes again the commitment +date.
  • +
+
+
+

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

+
    +
  • APSL - Nagarro
  • +
+
+
+

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:

+

mpascuall

+

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_compute_commitment_date/tests/__init__.py b/sale_order_compute_commitment_date/tests/__init__.py new file mode 100644 index 00000000000..82d88678993 --- /dev/null +++ b/sale_order_compute_commitment_date/tests/__init__.py @@ -0,0 +1,4 @@ +# Copyright 2025 APSL Nagarro +# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). + +from . import test_sale_order_compute_commitment_date diff --git a/sale_order_compute_commitment_date/tests/test_sale_order_compute_commitment_date.py b/sale_order_compute_commitment_date/tests/test_sale_order_compute_commitment_date.py new file mode 100644 index 00000000000..357b71bd2bb --- /dev/null +++ b/sale_order_compute_commitment_date/tests/test_sale_order_compute_commitment_date.py @@ -0,0 +1,366 @@ +from datetime import datetime + +from odoo.tests.common import TransactionCase + + +class TestSaleCommitmentDate(TransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) + cls.SaleOrder = cls.env["sale.order"] + cls.SaleOrderLine = cls.env["sale.order.line"] + cls.ProductTemplate = cls.env["product.template"] + cls.ProductAttribute = cls.env["product.attribute"] + cls.ProductAttributeValue = cls.env["product.attribute.value"] + cls.ResourceCalendar = cls.env["resource.calendar"] + cls.ResourceCalendarLeaves = cls.env["resource.calendar.leaves"] + cls.Partner = cls.env["res.partner"] + cls.company = cls.env.ref("base.main_company") + cls.calendar = cls.ResourceCalendar.create( + { + "name": "Test Working Calendar (Mon-Fri)", + "company_id": cls.company.id, + "hours_per_day": 8.0, + "tz": "Europe/Madrid", + "attendance_ids": [ + ( + 0, + 0, + { + "name": "Monday Morning", + "dayofweek": "0", + "hour_from": 8, + "hour_to": 12, + }, + ), + ( + 0, + 0, + { + "name": "Monday Afternoon", + "dayofweek": "0", + "hour_from": 13, + "hour_to": 17, + }, + ), + ( + 0, + 0, + { + "name": "Tuesday Morning", + "dayofweek": "1", + "hour_from": 8, + "hour_to": 12, + }, + ), + ( + 0, + 0, + { + "name": "Tuesday Afternoon", + "dayofweek": "1", + "hour_from": 13, + "hour_to": 17, + }, + ), + ( + 0, + 0, + { + "name": "Wednesday Morning", + "dayofweek": "2", + "hour_from": 8, + "hour_to": 12, + }, + ), + ( + 0, + 0, + { + "name": "Wednesday Afternoon", + "dayofweek": "2", + "hour_from": 13, + "hour_to": 17, + }, + ), + ( + 0, + 0, + { + "name": "Thursday Morning", + "dayofweek": "3", + "hour_from": 8, + "hour_to": 12, + }, + ), + ( + 0, + 0, + { + "name": "Thursday Afternoon", + "dayofweek": "3", + "hour_from": 13, + "hour_to": 17, + }, + ), + ( + 0, + 0, + { + "name": "Friday Morning", + "dayofweek": "4", + "hour_from": 8, + "hour_to": 12, + }, + ), + ( + 0, + 0, + { + "name": "Friday Afternoon", + "dayofweek": "4", + "hour_from": 13, + "hour_to": 17, + }, + ), + ], + } + ) + cls.company.resource_calendar_id = cls.calendar + + cls.color_attribute = cls.ProductAttribute.create( + {"name": "Color", "display_type": "radio"} + ) + cls.red_value = cls.ProductAttributeValue.create( + { + "name": "Red", + "attribute_id": cls.color_attribute.id, + "lead_time": 2, + } + ) + cls.blue_value = cls.ProductAttributeValue.create( + { + "name": "Blue", + "attribute_id": cls.color_attribute.id, + "lead_time": 0, + } + ) + + cls.product_template_a = cls.ProductTemplate.create( + { + "name": "Product A", + "sale_delay": 5, + "type": "consu", + "attribute_line_ids": [ + ( + 0, + 0, + { + "attribute_id": cls.color_attribute.id, + "value_ids": [ + (6, 0, [cls.red_value.id, cls.blue_value.id]) + ], + }, + ) + ], + } + ) + + cls.product_a_red = cls.env["product.product"].search( + [ + ("product_tmpl_id", "=", cls.product_template_a.id), + ( + "product_template_attribute_value_ids.product_attribute_value_id", + "=", + cls.red_value.id, + ), + ], + limit=1, + ) + cls.product_a_blue = cls.env["product.product"].search( + [ + ("product_tmpl_id", "=", cls.product_template_a.id), + ( + "product_template_attribute_value_ids.product_attribute_value_id", + "=", + cls.blue_value.id, + ), + ], + limit=1, + ) + + cls.product_template_a.extend_lead_time = True + + cls.test_partner = cls.Partner.create({"name": "Test Partner for Sales"}) + + cls.test_creation_date = datetime(2025, 6, 25, 10, 0, 0) + + cls.order = cls.SaleOrder.create( + { + "partner_id": cls.test_partner.id, + "date_order": cls.test_creation_date, + } + ) + cls.SaleOrderLine.create( + { + "order_id": cls.order.id, + "product_id": cls.product_a_red.id, + "product_uom_qty": 1, + } + ) + + cls.order_2 = cls.SaleOrder.create( + { + "partner_id": cls.test_partner.id, + "date_order": cls.test_creation_date, + } + ) + cls.SaleOrderLine.create( + { + "order_id": cls.order_2.id, + "product_id": cls.product_a_red.id, + "product_uom_qty": 1, + } + ) + + def test_01_commitment_date_draft_order_calculation(self): + self.order._compute_commitment_date() + + expected_lead_time_days = 5 + 2 + + expected_commitment_date = self.order._get_date_with_lead_time_from_calendar( + self.test_creation_date, expected_lead_time_days + ) + + self.assertIsNotNone( + self.order.commitment_date, + "Commitment date should be calculated for a draft order.", + ) + self.assertEqual( + self.order.commitment_date.date(), + expected_commitment_date.date(), + "Commitment date for draft order should be calculated correctly based " + "on date_order.", + ) + self.assertEqual( + self.order.commitment_date.date(), + datetime(2025, 7, 4).date(), + "Commitment date for draft should be July 4, 2025.", + ) + + def test_02_commitment_date_confirmed_order_calculation(self): + self.ResourceCalendarLeaves.create( + { + "name": "Test Holiday", + "calendar_id": self.calendar.id, + "date_from": datetime(2025, 6, 30, 0, 0, 0), + "date_to": datetime(2025, 6, 30, 23, 59, 59), + } + ) + + self.assertIsNotNone( + self.order_2.commitment_date, + "Commitment date should be calculated in draft.", + ) + self.assertEqual( + self.order_2.commitment_date.date(), + datetime(2025, 7, 7).date(), + "Initial commitment date in draft should be July 7, 2025.", + ) + + self.order_2.action_confirm() + + expected_lead_time_days = 5 + 2 + + expected_commitment_date_final = ( + self.order_2._get_date_with_lead_time_from_calendar( + self.order_2.date_order, expected_lead_time_days + ) + ) + + self.assertEqual( + self.order_2.commitment_date.date(), + expected_commitment_date_final.date(), + "Commitment date for confirmed order should be " + "calculated correctly using date_order and holidays.", + ) + + def test_03_commitment_date_no_attribute_lead_time(self): + test_creation_date = datetime(2025, 6, 25, 10, 0, 0) + + order = self.SaleOrder.create( + { + "partner_id": self.test_partner.id, + "date_order": test_creation_date, + } + ) + self.SaleOrderLine.create( + { + "order_id": order.id, + "product_id": self.product_a_blue.id, + "product_uom_qty": 1, + } + ) + order._compute_commitment_date() + + expected_lead_time_days = 5 + + expected_commitment_date = order._get_date_with_lead_time_from_calendar( + test_creation_date, expected_lead_time_days + ) + + self.assertIsNotNone( + order.commitment_date, "Commitment date should be calculated." + ) + self.assertEqual( + order.commitment_date.date(), + expected_commitment_date.date(), + "Commitment date should be calculated without attribute lead time.", + ) + self.assertEqual( + order.commitment_date.date(), + datetime(2025, 7, 2).date(), + "Commitment date should be July 2, 2025.", + ) + + def test_04_commitment_date_extend_lead_time_false(self): + self.product_template_a.extend_lead_time = False + + test_creation_date = datetime(2025, 6, 25, 10, 0, 0) + + order = self.SaleOrder.create( + { + "partner_id": self.test_partner.id, + "date_order": test_creation_date, + } + ) + self.SaleOrderLine.create( + { + "order_id": order.id, + "product_id": self.product_a_red.id, + "product_uom_qty": 1, + } + ) + order._compute_commitment_date() + + expected_lead_time_days = 5 + + expected_commitment_date = order._get_date_with_lead_time_from_calendar( + test_creation_date, expected_lead_time_days + ) + + self.assertIsNotNone( + order.commitment_date, "Commitment date should be calculated." + ) + self.assertEqual( + order.commitment_date.date(), + expected_commitment_date.date(), + "Commitment date should not include attribute " + "lead time when extend_lead_time is False.", + ) + self.assertEqual( + order.commitment_date.date(), + datetime(2025, 7, 2).date(), + "Commitment date should be July 2, 2025 when extend_lead_time is False.", + ) diff --git a/sale_order_compute_commitment_date/views/product_attribute_value.xml b/sale_order_compute_commitment_date/views/product_attribute_value.xml new file mode 100644 index 00000000000..f975fa7f7bd --- /dev/null +++ b/sale_order_compute_commitment_date/views/product_attribute_value.xml @@ -0,0 +1,17 @@ + + + + + product.attribute.list.inherit.lead.time + product.attribute + + + + + + + + diff --git a/sale_order_compute_commitment_date/views/product_template.xml b/sale_order_compute_commitment_date/views/product_template.xml new file mode 100644 index 00000000000..99cebf5c635 --- /dev/null +++ b/sale_order_compute_commitment_date/views/product_template.xml @@ -0,0 +1,14 @@ + + + + + product.template.form.inherit.lead.time + product.template + + + + + + + +