diff --git a/stock_warehouse_calendar_orderpoint/models/res_company.py b/stock_warehouse_calendar_orderpoint/models/res_company.py index d66d8d1e52ae..d702d162c066 100644 --- a/stock_warehouse_calendar_orderpoint/models/res_company.py +++ b/stock_warehouse_calendar_orderpoint/models/res_company.py @@ -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 + ) diff --git a/stock_warehouse_calendar_orderpoint/models/res_config_settings.py b/stock_warehouse_calendar_orderpoint/models/res_config_settings.py index b4e88572e6c2..6a6fc716e4ec 100644 --- a/stock_warehouse_calendar_orderpoint/models/res_config_settings.py +++ b/stock_warehouse_calendar_orderpoint/models/res_config_settings.py @@ -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, + ) diff --git a/stock_warehouse_calendar_orderpoint/models/stock_warehouse.py b/stock_warehouse_calendar_orderpoint/models/stock_warehouse.py index 98aa9f0211ad..5f5c3a66f8e1 100644 --- a/stock_warehouse_calendar_orderpoint/models/stock_warehouse.py +++ b/stock_warehouse_calendar_orderpoint/models/stock_warehouse.py @@ -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 diff --git a/stock_warehouse_calendar_orderpoint/models/stock_warehouse_orderpoint.py b/stock_warehouse_calendar_orderpoint/models/stock_warehouse_orderpoint.py index 108869b9768e..9d85a8fc8523 100644 --- a/stock_warehouse_calendar_orderpoint/models/stock_warehouse_orderpoint.py +++ b/stock_warehouse_calendar_orderpoint/models/stock_warehouse_orderpoint.py @@ -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 @@ -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): @@ -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): diff --git a/stock_warehouse_calendar_orderpoint/tests/common.py b/stock_warehouse_calendar_orderpoint/tests/common.py index 77a6110334c5..236a462abc74 100644 --- a/stock_warehouse_calendar_orderpoint/tests/common.py +++ b/stock_warehouse_calendar_orderpoint/tests/common.py @@ -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 diff --git a/stock_warehouse_calendar_orderpoint/tests/test_calendar_orderpoint.py b/stock_warehouse_calendar_orderpoint/tests/test_calendar_orderpoint.py index 1aa0cafd2592..4dbbfb3af238 100644 --- a/stock_warehouse_calendar_orderpoint/tests/test_calendar_orderpoint.py +++ b/stock_warehouse_calendar_orderpoint/tests/test_calendar_orderpoint.py @@ -17,7 +17,7 @@ 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 @@ -25,7 +25,7 @@ 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( @@ -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") diff --git a/stock_warehouse_calendar_orderpoint/views/res_config_settings.xml b/stock_warehouse_calendar_orderpoint/views/res_config_settings.xml index 2853168f2be0..1f84db65e986 100644 --- a/stock_warehouse_calendar_orderpoint/views/res_config_settings.xml +++ b/stock_warehouse_calendar_orderpoint/views/res_config_settings.xml @@ -39,6 +39,18 @@ > +