Skip to content

Commit

Permalink
fixup! Add option to define how lead days should be consumed when com…
Browse files Browse the repository at this point in the history
…puting ``stock.warehouse.orderpoint.lead_days_date``
  • Loading branch information
SilvioC2C committed Apr 16, 2024
1 parent 1ba4d8a commit 1eaf0e4
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 19 deletions.
17 changes: 17 additions & 0 deletions stock_warehouse_calendar_orderpoint/models/res_company.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,20 @@ class ResCompany(models.Model):
string="Schedule the lead date on workday only",
help="Postpone the lead date to the first available workday",
)
orderpoint_on_workday_policy = fields.Selection(
[
("skip_to_first_workday", "Skip to first workday"),
("skip_all_non_workdays", "Skip non-workdays"),
],
string="Reordering on Workday Policy",
help="Policy to postpone the lead date to the first available workday:\n"
"* skip to first workday: compute the date using lead delay days as solar"
" days, then skip to the next workday if the result is not a workday"
" (eg: run action on Friday with 2 days lead delay -> the result is Sunday ->"
" skip to the first following workday, Monday)\n"
"* skip non-workdays: compute the order date consuming lead delay days only on"
" (eg: run action on Friday with 2 days lead delay -> skip Saturday and Sunday"
" -> start consuming lead days on Monday as first lead day -> the result is"
" Tuesday)\n",
default="skip_to_first_workday", # Retro-compatible default value
)
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@ class ResConfigSettings(models.TransientModel):
related="company_id.orderpoint_on_workday",
readonly=False,
)
orderpoint_on_workday_policy = fields.Selection(
related="company_id.orderpoint_on_workday_policy",
readonly=False,
)
20 changes: 20 additions & 0 deletions stock_warehouse_calendar_orderpoint/models/stock_warehouse.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,29 @@ class StockWarehouse(models.Model):
"This is based on the Working Hours calendar."
),
)
orderpoint_on_workday_policy = fields.Selection(
[
("skip_to_first_workday", "Skip to first workday"),
("skip_all_non_workdays", "Skip non-workdays"),
],
string="Reordering on Workday Policy",
default=lambda o: o._default_orderpoint_on_workday_policy(),
help="Policy to postpone the lead date to the first available workday:\n"
"* skip to first workday: compute the date using lead delay days as solar"
" days, then skip to the next workday if the result is not a workday"
" (eg: run action on Friday with 2 days lead delay -> the result is Sunday ->"
" skip to the first following workday, Monday)\n"
"* skip non-workdays: compute the order date consuming lead delay days only on"
" (eg: run action on Friday with 2 days lead delay -> skip Saturday and Sunday"
" -> start consuming lead days on Monday as first lead day -> the result is"
" Tuesday)\n",
)

def _default_orderpoint_calendar_id(self):
return self.env.company.orderpoint_calendar_id

def _default_orderpoint_on_workday(self):
return self.env.company.orderpoint_on_workday

def _default_orderpoint_on_workday_policy(self):
return self.env.company.orderpoint_on_workday_policy
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl)


from dateutil import relativedelta
from dateutil.relativedelta import relativedelta

from odoo import api, fields, models

Expand All @@ -15,27 +15,46 @@ class StockWarehouseOrderpoint(models.Model):
"product_id.seller_ids",
"product_id.seller_ids.delay",
"warehouse_id.orderpoint_calendar_id",
"warehouse_id.orderpoint_on_workday_policy",
)
def _compute_lead_days(self):
super()._compute_lead_days()
# Override to use the orderpoint calendar to compute the 'lead_days_date'
for orderpoint in self.with_context(bypass_delay_description=True):
op_calendar = orderpoint.warehouse_id.orderpoint_calendar_id
if not op_calendar:
continue
wh = orderpoint.warehouse_id
if not orderpoint.product_id or not orderpoint.location_id:
orderpoint.lead_days_date = False
continue
lead_days = orderpoint._get_lead_days()
# Get the next planned date to execute this orderpoint
start_date = orderpoint._get_next_reordering_date()
lead_days_date = start_date + relativedelta.relativedelta(days=lead_days)
# Postpone to the next available workday if needed
calendar = orderpoint.warehouse_id.calendar_id
if orderpoint.warehouse_id.orderpoint_on_workday and calendar:
# Get the lead days for this orderpoint
lead_days = orderpoint._get_lead_days()
# Get calendar, workday policy from warehouse
calendar = wh.calendar_id
policy = wh.orderpoint_on_workday and wh.orderpoint_on_workday_policy
if calendar and policy == "skip_to_first_workday":
# Consume all the lead days, then move up to the first workday
# according to the calendar
lead_days_date = calendar.plan_hours(
0, lead_days_date, compute_leaves=True
0, start_date + relativedelta(days=lead_days), compute_leaves=True
)
elif calendar and policy == "skip_all_non_workdays":
# Postpone to the next available workday if needed, consuming lead days
# only on workdays
lead_days_date = calendar.plan_hours(0, start_date, compute_leaves=True)
if lead_days_date.date() != start_date.date():
# We've consumed a lead day if the lead date is not the start date
lead_days -= 1
while lead_days > 0:
# Always get the next working day according to the calendar, and
# decrease the lead days at each iteration
lead_days_date = calendar.plan_hours(
0, lead_days_date + relativedelta(days=1), compute_leaves=True
)
lead_days -= 1
else:
# Simply postpone according to delays
lead_days_date = start_date + relativedelta(days=lead_days)
orderpoint.lead_days_date = lead_days_date

def _get_lead_days(self):
Expand All @@ -45,12 +64,11 @@ def _get_lead_days(self):

def _get_next_reordering_date(self):
self.ensure_one()
now = fields.Datetime.now()
calendar = self.warehouse_id.orderpoint_calendar_id
if calendar:
# TODO: should we take into account days off or the reordering
# calendar with 'compute_leaves=True' here?
return calendar.plan_hours(0, fields.Datetime.now())
return False
# TODO: should we take into account days off or the reordering calendar with
# 'compute_leaves=True' here?
return calendar and calendar.plan_hours(0, now) or now

@api.depends("rule_ids", "product_id.seller_ids", "product_id.seller_ids.delay")
def _compute_json_popover(self):
Expand Down
5 changes: 3 additions & 2 deletions stock_warehouse_calendar_orderpoint/tests/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@ def setUpClass(cls):
"stock_warehouse_calendar_orderpoint.resource_calendar_orderpoint_demo"
)
cls.wh.orderpoint_on_workday = True
cls.wh.orderpoint_on_workday_policy = "skip_to_first_workday"
cls.wh.calendar_id = cls.env.ref("resource.resource_calendar_std")
# 1 day delay for supplier
cls.seller.delay = 1
# Company PO lead time
cls.wh.company_id.po_lead = 1
# Rule lead time
cls.orderpoint.rule_ids.delay = 2
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ def setUp(self):

@freeze_time("2022-05-30 12:00") # Monday
def test_calendar_orderpoint_monday(self):
# Monday -> Wednesday + 2 days (seller delay + PO lead time) => Friday
# Monday -> Wednesday + 2 days rule delay => Friday
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-03")

@freeze_time("2022-06-01 12:00") # Wednesday during working hours
def test_calendar_orderpoint_wednesday_during_working_hours(self):
self.op_model.invalidate_cache(["lead_days_date"]) # Recompute field
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-03")

@freeze_time("2022-06-01 20:00") # Wednesday outside working hours
@freeze_time("2022-06-01 20:00") # Wednesday after working hours
def test_calendar_orderpoint_wednesday_outside_working_hours(self):
self.op_model.invalidate_cache(["lead_days_date"]) # Recompute field
self.assertEqual(
Expand All @@ -49,3 +49,15 @@ def test_calendar_orderpoint_thursday(self):
def test_calendar_orderpoint_next_thursday(self):
self.op_model.invalidate_cache(["lead_days_date"])
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-17")

@freeze_time("2022-06-01 12:00") # Wednesday during working hours
def test_calendar_orderpoint_policy(self):
self.orderpoint.rule_ids.delay = 4
# 4 days are counted from Wednesday to Sunday => end on Monday
self.wh.orderpoint_on_workday_policy = "skip_to_first_workday"
self.op_model.invalidate_cache(["lead_days_date"])
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-06")
# 4 days are counted from Wednesday, skipping the weekend => end on Tuesday
self.wh.orderpoint_on_workday_policy = "skip_all_non_workdays"
self.op_model.invalidate_cache(["lead_days_date"])
self.assertEqual(str(self.orderpoint.lead_days_date), "2022-06-07")
12 changes: 12 additions & 0 deletions stock_warehouse_calendar_orderpoint/views/res_config_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,18 @@
>
<label for="orderpoint_on_workday" />
</div>
<div
class="o_setting_left_pane"
attrs="{'invisible': [('orderpoint_on_workday', '=', False)]}"
>
<field name="orderpoint_on_workday_policy" />
</div>
<div
class="o_setting_right_pane"
attrs="{'invisible': [('orderpoint_on_workday', '=', False)]}"
>
<label for="orderpoint_on_workday_policy" />
</div>
</div>
</div>
</field>
Expand Down
4 changes: 4 additions & 0 deletions stock_warehouse_calendar_orderpoint/views/stock_warehouse.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
name="orderpoint_on_workday"
attrs="{'invisible': [('orderpoint_calendar_id', '=', False)]}"
/>
<field
name="orderpoint_on_workday_policy"
attrs="{'invisible': [('orderpoint_calendar_id', '=', False)], 'required': [('orderpoint_on_workday', '=', True)]}"
/>
</group>
<field name="calendar_id" position="attributes">
<attribute
Expand Down

0 comments on commit 1eaf0e4

Please sign in to comment.