From d8232e8e9cf1dfb688abb4363c7357689b526144 Mon Sep 17 00:00:00 2001 From: Yoshi Tashiro Date: Sun, 19 Jan 2025 15:08:00 +0000 Subject: [PATCH] fixup! --- sale_lead_time_profile/README.rst | 90 ++++++++++++- sale_lead_time_profile/__manifest__.py | 3 +- sale_lead_time_profile/models/__init__.py | 2 + .../models/lead_time_profile.py | 79 ++++++++--- sale_lead_time_profile/models/res_company.py | 13 ++ .../models/res_config_settings.py | 24 ++++ sale_lead_time_profile/models/sale_order.py | 60 +++++++-- sale_lead_time_profile/readme/CONFIGURE.rst | 48 ++++++- sale_lead_time_profile/readme/DESCRIPTION.rst | 6 +- sale_lead_time_profile/readme/USAGE.rst | 32 +++++ .../security/ir.model.access.csv | 4 +- .../security/lead_time_profile_security.xml | 8 ++ .../tests/test_sale_lead_time_profile.py | 126 +++++++++++------- .../views/lead_time_profile_views.xml | 11 +- .../views/res_config_settings_view.xml | 78 +++++++++++ 15 files changed, 496 insertions(+), 88 deletions(-) create mode 100644 sale_lead_time_profile/models/res_company.py create mode 100644 sale_lead_time_profile/models/res_config_settings.py create mode 100644 sale_lead_time_profile/readme/USAGE.rst create mode 100644 sale_lead_time_profile/security/lead_time_profile_security.xml create mode 100644 sale_lead_time_profile/views/res_config_settings_view.xml diff --git a/sale_lead_time_profile/README.rst b/sale_lead_time_profile/README.rst index 94e8096a6c9..3235e0e1bf0 100644 --- a/sale_lead_time_profile/README.rst +++ b/sale_lead_time_profile/README.rst @@ -28,8 +28,10 @@ Sale Lead Time Profile |badge1| |badge2| |badge3| |badge4| |badge5| -This module enhances the sales order process by adding a delivery_lead_time that is determined based on the most closely matching lead time profile. -This time is then incorporated into the customer_lead of each sale order line, optimizing delivery scheduling based on specific lead time configurations. +This module enhances the sales order process by adding a delivery_lead_time that is +determined based on the most closely matching lead time profile. +This time is then incorporated into the customer_lead of each sale order line, +optimizing delivery scheduling based on specific lead time configurations. **Table of contents** @@ -41,9 +43,87 @@ Configuration To configure this module, you must first properly set up your lead time profiles: -1. Navigate to Inventory > Configuration > Lead Time Profiles. -2. Create the records according to your specific requirements. - The system will use the most matching record to determine the delivery lead time for each sale order. +1. Go to *Inventory > Configuration > Settings*. Find the section Delivery Lead Time + Settings, and update the factors of warehouse, country, state and partner according + to your specific requirements. Setting 0.0 means that matches of the corresponding + field will not be counted in score calculation of the lead time profile. +2. Navigate to *Inventory > Configuration > Lead Time Profiles*, and create records + according to the reality of delivery logistics. + +Example of how most matched lead time profile is determined: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When there are lead time profile records like this, and you have an order to deliver +from Main WH to Cust A: + ++---------+-------+---------+-----------+------------------+ +| Country | State | Partner | Warehouse | Lead Time (Days) | ++=========+=======+=========+===========+==================+ +| Japan | Tokyo | Cust A | | 3.0 | ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Main WH | 5.0 | ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Second WH | 7.0 | ++---------+-------+---------+-----------+------------------+ + +If the factors are configured to be like this: + +- Country: 1.0 +- State: 1.0 +- Partner: 1.0 +- Warehouse: 2.0 + +The scores of each profile will be calculated as follows: + ++---------+-------+---------+-----------+------------------+ +| Country | State | Partner | Warehouse | Lead Time (Days) | ++=========+=======+=========+===========+==================+ +| Japan | Tokyo | Cust A | | 3.0 | --> 1.0 + 1.0 + 1.0 + 0.0 = 3.0 ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Main WH | 5.0 | --> 1.0 + 1.0 + 0.0 + 2.0 = 4.0 ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Second WH | 7.0 | --> N/A for warehouse mismatch ++---------+-------+---------+-----------+------------------+ + +As a result, 5 days of the Delivery Lead Time will be proposed for the sales order. + +In a tie-breaking situation, the lead time profile with the lowest lead time will be chosen. + +Usage +===== + +The system proposes the **Delivery Lead Time** in sales orders (Other Info tab) based on +the most closely matching lead time profile, determined by the warehouse and delivery +address. + +The **Delivery Lead Time** can be manually updated as necessary. + +The **Delivery Lead Time** is incorporated into the lead time of each sales order line +by adding days to the lead time proposed based on the standard logic, which governs +delivery scheduling. + +Example: +~~~~~~~~ + +Assume the **Sales Security Lead Time** is set to 2.0 days. + +In vanilla Odoo, for a sales order with the following details: + +- **Order Date**: 2025-01-19 18:00:00 +- **Order Line Lead Time**: 5.0 days + +The delivery order dates would be: + +- **Scheduled Date**: 2025-01-22 18:00:00 +- **Deadline**: 2025-01-24 18:00:00 + +If this module is installed and the **Delivery Lead Time** proposed is 3.0 days, the +values will be updated as follows: + +- **Order Line Lead Time**: 8.0 days +- **Deadline (Delivery)**: 2025-01-27 18:00:00 + +The **Scheduled Date** will remain unchanged. Bug Tracker =========== diff --git a/sale_lead_time_profile/__manifest__.py b/sale_lead_time_profile/__manifest__.py index 9fddd1b9b85..a3daab770c2 100644 --- a/sale_lead_time_profile/__manifest__.py +++ b/sale_lead_time_profile/__manifest__.py @@ -1,6 +1,5 @@ # Copyright 2025 Quartile # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). - { "name": "Sale Lead Time Profile", "version": "16.0.1.0.0", @@ -9,8 +8,10 @@ "license": "AGPL-3", "depends": ["sale_stock"], "data": [ + "security/lead_time_profile_security.xml", "security/ir.model.access.csv", "views/lead_time_profile_views.xml", + "views/res_config_settings_view.xml", "views/sale_order_views.xml", ], "installable": True, diff --git a/sale_lead_time_profile/models/__init__.py b/sale_lead_time_profile/models/__init__.py index f307f607274..b7e7c9c6f39 100644 --- a/sale_lead_time_profile/models/__init__.py +++ b/sale_lead_time_profile/models/__init__.py @@ -1,3 +1,5 @@ from . import lead_time_profile +from . import res_company +from . import res_config_settings from . import sale_order from . import sale_order_line diff --git a/sale_lead_time_profile/models/lead_time_profile.py b/sale_lead_time_profile/models/lead_time_profile.py index cd371a87598..56ca21fc80d 100644 --- a/sale_lead_time_profile/models/lead_time_profile.py +++ b/sale_lead_time_profile/models/lead_time_profile.py @@ -1,7 +1,7 @@ # Copyright 2025 Quartile # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from odoo import fields, models +from odoo import api, fields, models class LeadTimeProfile(models.Model): @@ -9,37 +9,82 @@ class LeadTimeProfile(models.Model): _description = "Lead Time Profile" _order = "country_id, state_id, partner_id, warehouse_id" - warehouse_id = fields.Many2one("stock.warehouse") - partner_id = fields.Many2one("res.partner", string="Delivery Address") + warehouse_id = fields.Many2one( + "stock.warehouse", help="Matched against the warehouse of the sales order." + ) + partner_id = fields.Many2one( + "res.partner", help="Matched against the delivery address of the sales order." + ) state_id = fields.Many2one( - "res.country.state", domain="[('country_id', '=?', country_id)]" + "res.country.state", + domain="[('country_id', '=?', country_id)]", + help="Matched against the state of the delivery address of the sales order.", ) country_id = fields.Many2one( - related="state_id.country_id", readonly=False, store=True, required=True + "res.country", + help="Matched against the country of the delivery address of the sales order.", ) - lead_time = fields.Float(required=True) + company_id = fields.Many2one( + "res.company", required=True, default=lambda self: self.env.company + ) + lead_time = fields.Float(string="Lead Time (Days)", required=True) + + @api.onchange("partner_id") + def _onchagnge_partner_id(self): + for rec in self: + if rec.partner_id: + rec.state_id = rec.partner_id.state_id + + @api.onchange("state_id") + def _onchagnge_state_id(self): + for rec in self: + if rec.partner_id.state_id != rec.state_id: + rec.partner_id = False + rec.country_id = rec.state_id.country_id + + @api.onchange("country_id") + def _onchagnge_country_id(self): + for rec in self: + if rec.state_id.country_id != rec.country_id: + rec.state_id = False + if rec.partner_id.country_id != rec.country_id: + rec.partner_id = False + + def _get_score(self, **kwargs): + """Return a matching score for this lead time profile. + + The method scores each relevant match (warehouse/country/state/partner) + based on factors defined in the company. For example, if the partner matches, + the score is increased by the factor_partner. If any mismatch is found, it + immediately returns -1. - def _get_score(self, warehouse, partner): + :param kwargs: Dictionary containing 'warehouse' and 'partner'. + :return: A float representing the total match score if no mismatch is found, + or -1 if any mismatch is found. + """ self.ensure_one() score = 0 - if self.warehouse_id: - if warehouse == self.warehouse_id: - score += 1 - else: - return -1 + partner = kwargs.get("partner") + warehouse = kwargs.get("warehouse") + company = self.company_id if self.partner_id: if partner == self.partner_id: - score += 3 + score += company.factor_partner else: return -1 - elif self.state_id: + if self.state_id: if partner.state_id == self.state_id: - score += 2 + score += company.factor_state else: return -1 - elif self.country_id: + if self.country_id: if partner.country_id == self.country_id: - score += 1 + score += company.factor_country + else: + return -1 + if self.warehouse_id: + if warehouse == self.warehouse_id: + score += company.factor_warehouse else: return -1 return score diff --git a/sale_lead_time_profile/models/res_company.py b/sale_lead_time_profile/models/res_company.py new file mode 100644 index 00000000000..f44205c4d75 --- /dev/null +++ b/sale_lead_time_profile/models/res_company.py @@ -0,0 +1,13 @@ +# Copyright 2025 Quartile +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ResCompany(models.Model): + _inherit = "res.company" + + factor_warehouse = fields.Float(default=1.0) + factor_country = fields.Float(default=1.0) + factor_state = fields.Float(default=1.0) + factor_partner = fields.Float(default=1.0) diff --git a/sale_lead_time_profile/models/res_config_settings.py b/sale_lead_time_profile/models/res_config_settings.py new file mode 100644 index 00000000000..5bf56d9d726 --- /dev/null +++ b/sale_lead_time_profile/models/res_config_settings.py @@ -0,0 +1,24 @@ +# Copyright 2025 Quartile +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import fields, models + + +class ResConfigSettings(models.TransientModel): + _inherit = "res.config.settings" + + factor_warehouse = fields.Float( + related="company_id.factor_warehouse", readonly=False + ) + factor_country = fields.Float(related="company_id.factor_country", readonly=False) + factor_state = fields.Float(related="company_id.factor_state", readonly=False) + factor_partner = fields.Float(related="company_id.factor_partner", readonly=False) + + def open_lead_time_profile_list(self): + self.ensure_one() + return { + "type": "ir.actions.act_window", + "name": "Lead Time Profiles", + "res_model": "lead.time.profile", + "view_mode": "tree", + } diff --git a/sale_lead_time_profile/models/sale_order.py b/sale_lead_time_profile/models/sale_order.py index a96cc5435fe..8c0e9a1780f 100644 --- a/sale_lead_time_profile/models/sale_order.py +++ b/sale_lead_time_profile/models/sale_order.py @@ -2,6 +2,7 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from odoo import api, fields, models +from odoo.osv import expression class SaleOrder(models.Model): @@ -14,26 +15,63 @@ class SaleOrder(models.Model): help="Number of days expected for delivery from the warehouse to the delivery address.", ) - @api.depends("warehouse_id", "partner_shipping_id") + def _get_partner_address_domain(self): + self.ensure_one() + partner = self.partner_shipping_id + partner_domain = [("partner_id", "=", partner.id)] + state_domain = [ + ("partner_id", "=", False), + ("state_id", "=", partner.state_id.id), + ] + country_domain = [ + ("partner_id", "=", False), + ("state_id", "=", False), + ("country_id", "=", partner.country_id.id), + ] + no_address_domain = [ + ("partner_id", "=", False), + ("state_id", "=", False), + ("country_id", "=", False), + ] + return expression.OR( + [partner_domain, state_domain, country_domain, no_address_domain] + ) + + def _get_warehouse_domain(self): + self.ensure_one() + return [ + "|", + ("warehouse_id", "=", self.warehouse_id.id), + ("warehouse_id", "=", False), + ] + + @api.depends("partner_shipping_id", "warehouse_id") def _compute_delivery_lead_time(self): for rec in self: rec.delivery_lead_time = 0.0 if not rec.partner_shipping_id: continue - profiles = self.env["lead.time.profile"].search( - [ - "|", - ("warehouse_id", "=", rec.warehouse_id.id), - ("warehouse_id", "=", False), - ] - ) + partner_address_domain = rec._get_partner_address_domain() + warehouse_domain = rec._get_warehouse_domain() + domain = expression.AND([partner_address_domain, warehouse_domain]) + profiles = self.env["lead.time.profile"].search(domain) if not profiles: continue profile_scores = { - profile: profile._get_score(rec.warehouse_id, rec.partner_shipping_id) + profile: profile._get_score( + **{ + "partner": rec.partner_shipping_id, + "warehouse": rec.warehouse_id, + } + ) for profile in profiles } - best_profile = max(profile_scores, key=profile_scores.get, default=None) + # In case of a tie, pick the profile with the lowest lead time. + best_profile = max( + profiles, + key=lambda profile: (profile_scores[profile], -profile.lead_time), + default=None, + ) rec.delivery_lead_time = ( - best_profile.lead_time if profile_scores[best_profile] > 0 else 0.00 + best_profile.lead_time if profile_scores[best_profile] >= 0.0 else 0.0 ) diff --git a/sale_lead_time_profile/readme/CONFIGURE.rst b/sale_lead_time_profile/readme/CONFIGURE.rst index edbadeffc1b..af844689d29 100644 --- a/sale_lead_time_profile/readme/CONFIGURE.rst +++ b/sale_lead_time_profile/readme/CONFIGURE.rst @@ -1,5 +1,47 @@ To configure this module, you must first properly set up your lead time profiles: -1. Navigate to Inventory > Configuration > Lead Time Profiles. -2. Create the records according to your specific requirements. - The system will use the most matching record to determine the delivery lead time for each sale order. +1. Go to *Inventory > Configuration > Settings*. Find the section Delivery Lead Time + Settings, and update the factors of warehouse, country, state and partner according + to your specific requirements. Setting 0.0 means that matches of the corresponding + field will not be counted in score calculation of the lead time profile. +2. Navigate to *Inventory > Configuration > Lead Time Profiles*, and create records + according to the reality of delivery logistics. + +Example of how most matched lead time profile is determined: +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When there are lead time profile records like this, and you have an order to deliver +from Main WH to Cust A: + ++---------+-------+---------+-----------+------------------+ +| Country | State | Partner | Warehouse | Lead Time (Days) | ++=========+=======+=========+===========+==================+ +| Japan | Tokyo | Cust A | | 3.0 | ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Main WH | 5.0 | ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Second WH | 7.0 | ++---------+-------+---------+-----------+------------------+ + +If the factors are configured to be like this: + +- Country: 1.0 +- State: 1.0 +- Partner: 1.0 +- Warehouse: 2.0 + +The scores of each profile will be calculated as follows: + ++---------+-------+---------+-----------+------------------+ +| Country | State | Partner | Warehouse | Lead Time (Days) | ++=========+=======+=========+===========+==================+ +| Japan | Tokyo | Cust A | | 3.0 | --> 1.0 + 1.0 + 1.0 + 0.0 = 3.0 ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Main WH | 5.0 | --> 1.0 + 1.0 + 0.0 + 2.0 = 4.0 ++---------+-------+---------+-----------+------------------+ +| Japan | Tokyo | | Second WH | 7.0 | --> N/A for warehouse mismatch ++---------+-------+---------+-----------+------------------+ + +As a result, 5 days of the Delivery Lead Time will be proposed for the sales order. + +In a tie-breaking situation, the lead time profile with the lowest lead time will be chosen. diff --git a/sale_lead_time_profile/readme/DESCRIPTION.rst b/sale_lead_time_profile/readme/DESCRIPTION.rst index 0a9b52d2010..165fdb98e40 100644 --- a/sale_lead_time_profile/readme/DESCRIPTION.rst +++ b/sale_lead_time_profile/readme/DESCRIPTION.rst @@ -1,2 +1,4 @@ -This module enhances the sales order process by adding a delivery_lead_time that is determined based on the most closely matching lead time profile. -This time is then incorporated into the customer_lead of each sale order line, optimizing delivery scheduling based on specific lead time configurations. +This module enhances the sales order process by adding a delivery_lead_time that is +determined based on the most closely matching lead time profile. +This time is then incorporated into the customer_lead of each sale order line, +optimizing delivery scheduling based on specific lead time configurations. diff --git a/sale_lead_time_profile/readme/USAGE.rst b/sale_lead_time_profile/readme/USAGE.rst new file mode 100644 index 00000000000..e915fc84fce --- /dev/null +++ b/sale_lead_time_profile/readme/USAGE.rst @@ -0,0 +1,32 @@ +The system proposes the **Delivery Lead Time** in sales orders (Other Info tab) based on +the most closely matching lead time profile, determined by the warehouse and delivery +address. + +The **Delivery Lead Time** can be manually updated as necessary. + +The **Delivery Lead Time** is incorporated into the lead time of each sales order line +by adding days to the lead time proposed based on the standard logic, which governs +delivery scheduling. + +Example: +~~~~~~~~ + +Assume the **Sales Security Lead Time** is set to 2.0 days. + +In vanilla Odoo, for a sales order with the following details: + +- **Order Date**: 2025-01-19 18:00:00 +- **Order Line Lead Time**: 5.0 days + +The delivery order dates would be: + +- **Scheduled Date**: 2025-01-22 18:00:00 +- **Deadline**: 2025-01-24 18:00:00 + +If this module is installed and the **Delivery Lead Time** proposed is 3.0 days, the +values will be updated as follows: + +- **Order Line Lead Time**: 8.0 days +- **Deadline (Delivery)**: 2025-01-27 18:00:00 + +The **Scheduled Date** will remain unchanged. diff --git a/sale_lead_time_profile/security/ir.model.access.csv b/sale_lead_time_profile/security/ir.model.access.csv index 62bf3281c57..fe5cd23c134 100644 --- a/sale_lead_time_profile/security/ir.model.access.csv +++ b/sale_lead_time_profile/security/ir.model.access.csv @@ -1,2 +1,4 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_lead_time_profile,lead.time.profile,model_lead_time_profile,sales_team.group_sale_manager,1,1,1,1 +access_lead_time_profile_user,lead.time.profile.user,model_lead_time_profile,base.group_user,1,0,0,0 +access_lead_time_profile_sale_manager,lead.time.profile.sale.manager,model_lead_time_profile,sales_team.group_sale_manager,1,1,1,1 +access_lead_time_profile_stock_manager,lead.time.profile.stock.manager,model_lead_time_profile,stock.group_stock_manager,1,1,1,1 diff --git a/sale_lead_time_profile/security/lead_time_profile_security.xml b/sale_lead_time_profile/security/lead_time_profile_security.xml new file mode 100644 index 00000000000..8fed2449d17 --- /dev/null +++ b/sale_lead_time_profile/security/lead_time_profile_security.xml @@ -0,0 +1,8 @@ + + + + Lead Time Profile multi-company + + [('company_id', 'in', company_ids)] + + diff --git a/sale_lead_time_profile/tests/test_sale_lead_time_profile.py b/sale_lead_time_profile/tests/test_sale_lead_time_profile.py index 7c75f7deefd..859fa849cee 100644 --- a/sale_lead_time_profile/tests/test_sale_lead_time_profile.py +++ b/sale_lead_time_profile/tests/test_sale_lead_time_profile.py @@ -3,6 +3,7 @@ from datetime import timedelta +from odoo import Command from odoo.tests.common import TransactionCase @@ -10,77 +11,112 @@ class TestSaleLeadTimeProfile(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() - cls.partner = cls.env["res.partner"].create( - { - "name": "Partner", - "country_id": cls.env.ref("base.us").id, - } + country_jp = cls.env.ref("base.jp") + cls.country_us = cls.env.ref("base.us") + state_tokyo = cls.env.ref("base.state_jp_jp-13") + warehouse_obj = cls.env["stock.warehouse"] + cls.warehouse_main = warehouse_obj.create({"name": "Main WH", "code": "MW"}) + cls.warehouse_2nd = warehouse_obj.create({"name": "2ndn WH", "code": "2W"}) + cls.partner_1 = cls.env["res.partner"].create( + {"name": "Cust A", "country_id": country_jp.id, "state_id": state_tokyo.id} + ) + cls.partner_2 = cls.env["res.partner"].create( + {"name": "Cust B", "country_id": country_jp.id, "state_id": state_tokyo.id} ) - cls.warehouse = cls.env["stock.warehouse"].create( - {"name": "Main Warehouse", "code": "MW"} + cls.partner_3 = cls.env["res.partner"].create( + {"name": "Cust C", "country_id": cls.country_us.id} ) - cls.lead_time_profile_1 = cls.env["lead.time.profile"].create( + cls.product = cls.env["product.product"].create( + {"name": "product", "type": "product", "sale_delay": 10} + ) + # Prepare lead time profiles like the following: + # +---------+-------+---------+-----------+------------------+ + # | Country | State | Partner | Warehouse | Lead Time (Days) | + # +=========+=======+=========+===========+==================+ + # | Japan | Tokyo | Cust A | | 3.0 | + # +---------+-------+---------+-----------+------------------+ + # | Japan | Tokyo | | Main WH | 5.0 | + # +---------+-------+---------+-----------+------------------+ + # | Japan | Tokyo | | 2nd WH | 7.0 | + # +---------+-------+---------+-----------+------------------+ + # | | | | | 20.0 | + # +---------+-------+---------+-----------+------------------+ + profile_obj = cls.env["lead.time.profile"] + profile_obj.create( { - "country_id": cls.env.ref("base.us").id, + "country_id": country_jp.id, + "state_id": state_tokyo.id, + "partner_id": cls.partner_1.id, "lead_time": 3.0, } ) - cls.lead_time_profile_2 = cls.env["lead.time.profile"].create( + profile_obj.create( { - "warehouse_id": cls.warehouse.id, - "country_id": cls.env.ref("base.us").id, + "country_id": country_jp.id, + "state_id": state_tokyo.id, + "warehouse_id": cls.warehouse_main.id, "lead_time": 5.0, } ) - cls.product = cls.env["product.product"].create( + profile_obj.create( { - "name": "Test Product", - "type": "product", + "country_id": country_jp.id, + "state_id": state_tokyo.id, + "warehouse_id": cls.warehouse_2nd.id, + "lead_time": 7.0, } ) + profile_obj.create({"lead_time": 20.0}) - def create_and_confirm_sale_order(self): - sale_order = self.env["sale.order"].create( + def create_sale_order(self, partner, warehouse): + return self.env["sale.order"].create( { - "partner_id": self.partner.id, - "warehouse_id": self.warehouse.id, + "partner_id": partner.id, + "warehouse_id": warehouse.id, "order_line": [ - ( - 0, - 0, + Command.create( { "name": self.product.name, "product_id": self.product.id, - "product_uom_qty": 2, - "product_uom": self.product.uom_id.id, - "price_unit": self.product.list_price, + "product_uom_qty": 1, + # "product_uom": self.product.uom_id.id, + # "price_unit": self.product.list_price, }, ) ], } ) - sale_order.action_confirm() - return sale_order - def test_sale_order_lead_time_profile(self): - sale_order = self.create_and_confirm_sale_order() - self.assertTrue(sale_order.picking_ids) - self.assertEqual(sale_order.delivery_lead_time, 5.0) - self.assertEqual(sale_order.order_line.customer_lead, 5.0) - picking = sale_order.picking_ids - self.assertEqual( - picking.scheduled_date.date() + timedelta(days=5.0), - picking.date_deadline.date(), - ) - - # assign partner_id to profile - self.lead_time_profile_1.partner_id = self.partner.id - sale_order = self.create_and_confirm_sale_order() - self.assertTrue(sale_order.picking_ids) - self.assertEqual(sale_order.delivery_lead_time, 3.0) - self.assertEqual(sale_order.order_line.customer_lead, 3.0) - picking = sale_order.picking_ids + def test_sale_order_to_picking(self): + company = self.env.company + self.assertEqual(company.factor_country, 1.0) + self.assertEqual(company.factor_state, 1.0) + self.assertEqual(company.factor_partner, 1.0) + self.assertEqual(company.factor_warehouse, 1.0) + order = self.create_sale_order(self.partner_1, self.warehouse_main) + # Profile with shorter lead time is picked in a tie-breaking situation + self.assertEqual(order.delivery_lead_time, 3.0) + # sale_delay of the product (10 days) + delivery_lead_time (3 days) + self.assertEqual(order.order_line.customer_lead, 13.0) + order.action_confirm() + # Confirm that the delivery lead time is correctly accounted for in the + # dates of picking + picking = order.picking_ids self.assertEqual( picking.scheduled_date.date() + timedelta(days=3.0), picking.date_deadline.date(), ) + + def test_profile_field_factors(self): + company = self.env.company + company.factor_warehouse = 2.0 + order = self.create_sale_order(self.partner_1, self.warehouse_main) + self.assertEqual(order.delivery_lead_time, 5.0) + + def test_misc_order_patterns(self): + order = self.create_sale_order(self.partner_2, self.warehouse_main) + self.assertEqual(order.delivery_lead_time, 5.0) + order.warehouse_id = self.warehouse_2nd + self.assertEqual(order.delivery_lead_time, 7.0) + order.partner_shipping_id = self.partner_3 + self.assertEqual(order.delivery_lead_time, 20.0) diff --git a/sale_lead_time_profile/views/lead_time_profile_views.xml b/sale_lead_time_profile/views/lead_time_profile_views.xml index 5c332edabbe..adba9399a57 100644 --- a/sale_lead_time_profile/views/lead_time_profile_views.xml +++ b/sale_lead_time_profile/views/lead_time_profile_views.xml @@ -5,10 +5,10 @@ lead.time.profile - + lead.time.profile - - + + + diff --git a/sale_lead_time_profile/views/res_config_settings_view.xml b/sale_lead_time_profile/views/res_config_settings_view.xml new file mode 100644 index 00000000000..ad43e85564d --- /dev/null +++ b/sale_lead_time_profile/views/res_config_settings_view.xml @@ -0,0 +1,78 @@ + + + + res_config_settings_view_form - sale_lead_time_profile + res.config.settings + + + +

Delivery Lead Time Settings

+
+
+
+
+ Factors of Lead Time Profile Elements + +
+ Factors considered in calculating the score of profiles to identify the best matching one. +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + +