Skip to content

Commit fb01303

Browse files
[ADD] lead_time_profile
1 parent a1a91fb commit fb01303

20 files changed

+1274
-0
lines changed

lead_time_profile/README.rst

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
=================
2+
Lead Time Profile
3+
=================
4+
5+
..
6+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
7+
!! This file is generated by oca-gen-addon-readme !!
8+
!! changes will be overwritten. !!
9+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
10+
!! source digest: sha256:8a7de4e1a21d634e67796c011daf7f9870570bcf399ef64b4df23c0e8ce273cb
11+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
12+
13+
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
14+
:target: https://odoo-community.org/page/development-status
15+
:alt: Beta
16+
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
17+
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
18+
:alt: License: AGPL-3
19+
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--workflow-lightgray.png?logo=github
20+
:target: https://github.com/OCA/stock-logistics-workflow/tree/16.0/lead_time_profile
21+
:alt: OCA/stock-logistics-workflow
22+
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
23+
:target: https://translation.odoo-community.org/projects/stock-logistics-workflow-16-0/stock-logistics-workflow-16-0-lead_time_profile
24+
:alt: Translate me on Weblate
25+
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
26+
:target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-workflow&target_branch=16.0
27+
:alt: Try me on Runboat
28+
29+
|badge1| |badge2| |badge3| |badge4| |badge5|
30+
31+
This module provides a generic way to manage lead times using lead time
32+
profiles. It selects the most suitable profile based on predefined rules and calculates
33+
the lead time accordingly.
34+
35+
The module is designed to be used as a base module. You can see an example implementation
36+
in the sale_lead_time module (part of the sale-workflow repository).
37+
38+
**Table of contents**
39+
40+
.. contents::
41+
:local:
42+
43+
Configuration
44+
=============
45+
46+
To configure this module, you must first properly set up your lead time profiles:
47+
48+
1. Go to *Inventory > Configuration > Settings*. Find the section Lead Time
49+
Settings, and update the factors of warehouse, country, state and partner according
50+
to your specific requirements. Setting 0.0 means that matches of the corresponding
51+
field will not be counted in score calculation of the lead time profile.
52+
2. Navigate to *Inventory > Configuration > Lead Time Profiles*, and create records
53+
based on your operational lead time requirements.
54+
55+
Example of how most matched lead time profile is determined for sales order:
56+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
57+
58+
When there are lead time profile records like this, and you have an order to deliver
59+
from Main WH to Cust A:
60+
61+
+---------+-------+---------+-----------+------------+------------------+
62+
| Country | State | Partner | Warehouse | Model | Lead Time (Days) |
63+
+=========+=======+=========+===========+============+==================+
64+
| Japan | Tokyo | Cust A | | sale.order | 3.0 |
65+
+---------+-------+---------+-----------+------------+------------------+
66+
| Japan | Tokyo | | Main WH | sale.order | 5.0 |
67+
+---------+-------+---------+-----------+------------+------------------+
68+
| Japan | Tokyo | | 2nd WH | sale.order | 7.0 |
69+
+---------+-------+---------+-----------+------------+------------------+
70+
71+
If the factors are configured to be like this:
72+
73+
- Country: 1.0
74+
- State: 1.0
75+
- Partner: 1.0
76+
- Warehouse: 2.0
77+
78+
The scores of each profile will be calculated as follows:
79+
80+
+---------+-------+---------+-----------+------------------+------------+-------------------------------+
81+
| Country | State | Partner | Warehouse | Lead Time (Days) | Model | Score Calculation |
82+
+=========+=======+=========+===========+==================+============================================+
83+
| Japan | Tokyo | Cust A | | 3.0 | sale.order | 1.0 + 1.0 + 1.0 + 0.0 = 3.0 |
84+
+---------+-------+---------+-----------+------------------+--------------------------------------------+
85+
| Japan | Tokyo | | Main WH | 5.0 | sale.order | 1.0 + 1.0 + 0.0 + 2.0 = 4.0 |
86+
+---------+-------+---------+-----------+------------------+--------------------------------------------+
87+
| Japan | Tokyo | | 2nd WH | 7.0 | sale.order | N/A for warehouse mismatch |
88+
+---------+-------+---------+-----------+------------------+--------------------------------------------+
89+
90+
The profile with the highest score is considered the best match.
91+
92+
In a tie-breaking situation, the lead time profile with the lowest lead time will be chosen.
93+
94+
Bug Tracker
95+
===========
96+
97+
Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-workflow/issues>`_.
98+
In case of trouble, please check there if your issue has already been reported.
99+
If you spotted it first, help us to smash it by providing a detailed and welcomed
100+
`feedback <https://github.com/OCA/stock-logistics-workflow/issues/new?body=module:%20lead_time_profile%0Aversion:%2016.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
101+
102+
Do not contact contributors directly about support or help with technical issues.
103+
104+
Credits
105+
=======
106+
107+
Authors
108+
~~~~~~~
109+
110+
* Quartile
111+
112+
Contributors
113+
~~~~~~~~~~~~
114+
115+
* `Quartile <https://www.quartile.co>`__:
116+
117+
* Yoshi Tashiro
118+
* Aung Ko Ko Lin
119+
120+
Maintainers
121+
~~~~~~~~~~~
122+
123+
This module is maintained by the OCA.
124+
125+
.. image:: https://odoo-community.org/logo.png
126+
:alt: Odoo Community Association
127+
:target: https://odoo-community.org
128+
129+
OCA, or the Odoo Community Association, is a nonprofit organization whose
130+
mission is to support the collaborative development of Odoo features and
131+
promote its widespread use.
132+
133+
This module is part of the `OCA/stock-logistics-workflow <https://github.com/OCA/stock-logistics-workflow/tree/16.0/lead_time_profile>`_ project on GitHub.
134+
135+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

lead_time_profile/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from . import models

lead_time_profile/__manifest__.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2025 Quartile (https://www.quartile.co)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3+
{
4+
"name": "Lead Time Profile",
5+
"version": "16.0.1.0.0",
6+
"author": "Quartile, Odoo Community Association (OCA)",
7+
"website": "https://github.com/OCA/stock-logistics-workflow",
8+
"license": "AGPL-3",
9+
"depends": ["stock"],
10+
"data": [
11+
"security/lead_time_profile_security.xml",
12+
"security/ir.model.access.csv",
13+
"views/lead_time_profile_views.xml",
14+
"views/res_config_settings_views.xml",
15+
],
16+
"installable": True,
17+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from . import lead_time_profile
2+
from . import res_company
3+
from . import res_config_settings
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# Copyright 2025 Quartile (https://www.quartile.co)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3+
4+
from odoo import api, fields, models
5+
6+
7+
class LeadTimeProfile(models.Model):
8+
_name = "lead.time.profile"
9+
_description = "Lead Time Profile"
10+
_order = "country_id, state_id, partner_id, warehouse_id"
11+
12+
warehouse_id = fields.Many2one(
13+
"stock.warehouse", help="Matched against the warehouse of the sales order."
14+
)
15+
partner_id = fields.Many2one(
16+
"res.partner", help="Matched against the delivery address of the sales order."
17+
)
18+
state_id = fields.Many2one(
19+
"res.country.state",
20+
domain="[('country_id', '=?', country_id)]",
21+
help="Matched against the state of the delivery address of the sales order.",
22+
)
23+
country_id = fields.Many2one(
24+
"res.country",
25+
help="Matched against the country of the delivery address of the sales order.",
26+
)
27+
company_id = fields.Many2one(
28+
"res.company", required=True, default=lambda self: self.env.company
29+
)
30+
model_id = fields.Many2one("ir.model", required=True, ondelete="cascade")
31+
model_name = fields.Char(related="model_id.model", store=True)
32+
lead_time = fields.Float(string="Lead Time (Days)", required=True)
33+
34+
@api.onchange("partner_id")
35+
def _onchange_partner_id(self):
36+
for rec in self:
37+
if rec.partner_id:
38+
rec.state_id = rec.partner_id.state_id
39+
40+
@api.onchange("state_id")
41+
def _onchange_state_id(self):
42+
for rec in self:
43+
if rec.partner_id.state_id != rec.state_id:
44+
rec.partner_id = False
45+
rec.country_id = rec.state_id.country_id
46+
47+
@api.onchange("country_id")
48+
def _onchange_country_id(self):
49+
for rec in self:
50+
if rec.state_id.country_id != rec.country_id:
51+
rec.state_id = False
52+
if rec.partner_id.country_id != rec.country_id:
53+
rec.partner_id = False
54+
55+
def _get_score(self, **kwargs):
56+
"""Return a matching score for this lead time profile.
57+
58+
The method scores each relevant match (warehouse/country/state/partner)
59+
based on factors defined in the company. For example, if the partner matches,
60+
the score is increased by the lead_time_profile_partner_factor. If any mismatch
61+
is found, it immediately returns -1.
62+
63+
:param kwargs: Dictionary containing 'warehouse' and 'partner'.
64+
:return: A float representing the total match score if no mismatch is found,
65+
or -1 if any mismatch is found.
66+
"""
67+
self.ensure_one()
68+
score = 0
69+
partner = kwargs.get("partner")
70+
warehouse = kwargs.get("warehouse")
71+
company = self.company_id
72+
if self.partner_id:
73+
if partner == self.partner_id:
74+
score += company.lead_time_profile_partner_factor
75+
else:
76+
return -1
77+
if self.state_id:
78+
if partner.state_id == self.state_id:
79+
score += company.lead_time_profile_state_factor
80+
else:
81+
return -1
82+
if self.country_id:
83+
if partner.country_id == self.country_id:
84+
score += company.lead_time_profile_country_factor
85+
else:
86+
return -1
87+
if self.warehouse_id:
88+
if warehouse == self.warehouse_id:
89+
score += company.lead_time_profile_warehouse_factor
90+
else:
91+
return -1
92+
return score
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Copyright 2025 Quartile (https://www.quartile.co)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3+
4+
from odoo import fields, models
5+
6+
7+
class ResCompany(models.Model):
8+
_inherit = "res.company"
9+
10+
lead_time_profile_warehouse_factor = fields.Float(default=1.0)
11+
lead_time_profile_country_factor = fields.Float(default=1.0)
12+
lead_time_profile_state_factor = fields.Float(default=1.0)
13+
lead_time_profile_partner_factor = fields.Float(default=1.0)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Copyright 2025 Quartile (https://www.quartile.co)
2+
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
3+
4+
from odoo import fields, models
5+
6+
7+
class ResConfigSettings(models.TransientModel):
8+
_inherit = "res.config.settings"
9+
10+
lead_time_profile_warehouse_factor = fields.Float(
11+
related="company_id.lead_time_profile_warehouse_factor", readonly=False
12+
)
13+
lead_time_profile_country_factor = fields.Float(
14+
related="company_id.lead_time_profile_country_factor", readonly=False
15+
)
16+
lead_time_profile_state_factor = fields.Float(
17+
related="company_id.lead_time_profile_state_factor", readonly=False
18+
)
19+
lead_time_profile_partner_factor = fields.Float(
20+
related="company_id.lead_time_profile_partner_factor", readonly=False
21+
)
22+
23+
def open_lead_time_profile_list(self):
24+
self.ensure_one()
25+
return {
26+
"type": "ir.actions.act_window",
27+
"name": "Lead Time Profiles",
28+
"res_model": "lead.time.profile",
29+
"view_mode": "tree",
30+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
To configure this module, you must first properly set up your lead time profiles:
2+
3+
1. Go to *Inventory > Configuration > Settings*. Find the section Lead Time
4+
Settings, and update the factors of warehouse, country, state and partner according
5+
to your specific requirements. Setting 0.0 means that matches of the corresponding
6+
field will not be counted in score calculation of the lead time profile.
7+
2. Navigate to *Inventory > Configuration > Lead Time Profiles*, and create records
8+
based on your operational lead time requirements.
9+
10+
Example of how most matched lead time profile is determined for sales order:
11+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
12+
13+
When there are lead time profile records like this, and you have an order to deliver
14+
from Main WH to Cust A:
15+
16+
+---------+-------+---------+-----------+------------+------------------+
17+
| Country | State | Partner | Warehouse | Model | Lead Time (Days) |
18+
+=========+=======+=========+===========+============+==================+
19+
| Japan | Tokyo | Cust A | | sale.order | 3.0 |
20+
+---------+-------+---------+-----------+------------+------------------+
21+
| Japan | Tokyo | | Main WH | sale.order | 5.0 |
22+
+---------+-------+---------+-----------+------------+------------------+
23+
| Japan | Tokyo | | 2nd WH | sale.order | 7.0 |
24+
+---------+-------+---------+-----------+------------+------------------+
25+
26+
If the factors are configured to be like this:
27+
28+
- Country: 1.0
29+
- State: 1.0
30+
- Partner: 1.0
31+
- Warehouse: 2.0
32+
33+
The scores of each profile will be calculated as follows:
34+
35+
+---------+-------+---------+-----------+------------------+------------+-------------------------------+
36+
| Country | State | Partner | Warehouse | Lead Time (Days) | Model | Score Calculation |
37+
+=========+=======+=========+===========+==================+============================================+
38+
| Japan | Tokyo | Cust A | | 3.0 | sale.order | 1.0 + 1.0 + 1.0 + 0.0 = 3.0 |
39+
+---------+-------+---------+-----------+------------------+--------------------------------------------+
40+
| Japan | Tokyo | | Main WH | 5.0 | sale.order | 1.0 + 1.0 + 0.0 + 2.0 = 4.0 |
41+
+---------+-------+---------+-----------+------------------+--------------------------------------------+
42+
| Japan | Tokyo | | 2nd WH | 7.0 | sale.order | N/A for warehouse mismatch |
43+
+---------+-------+---------+-----------+------------------+--------------------------------------------+
44+
45+
The profile with the highest score is considered the best match.
46+
47+
In a tie-breaking situation, the lead time profile with the lowest lead time will be chosen.
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
* `Quartile <https://www.quartile.co>`__:
2+
3+
* Yoshi Tashiro
4+
* Aung Ko Ko Lin
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
This module provides a generic way to manage lead times using lead time
2+
profiles. It selects the most suitable profile based on predefined rules and calculates
3+
the lead time accordingly.
4+
5+
The module is designed to be used as a base module. You can see an example implementation
6+
in the sale_lead_time module (part of the sale-workflow repository).

0 commit comments

Comments
 (0)