From 9a7911f613e6a09d5706e917c1acca878ea57d7a Mon Sep 17 00:00:00 2001 From: Mmequignon Date: Thu, 12 Oct 2023 15:07:19 +0200 Subject: [PATCH] sale_delivery_date: Only deliver on open days --- sale_delivery_date/models/sale_order_line.py | 41 ++++++++++++-- sale_delivery_date/tests/test_integration.py | 56 ++++++++++++++++++++ 2 files changed, 93 insertions(+), 4 deletions(-) diff --git a/sale_delivery_date/models/sale_order_line.py b/sale_delivery_date/models/sale_order_line.py index 85c76bc2ebe..8a0b211ce25 100644 --- a/sale_delivery_date/models/sale_order_line.py +++ b/sale_delivery_date/models/sale_order_line.py @@ -13,6 +13,18 @@ _logger = logging.getLogger(__name__) +# When postponing a delivery date based on calendar leaves, it could happen +# that we have to look far in the future for a valid day. +# I.E. when customer's time window is wednesday, and all wednesdays are leaves +# on the warehouse calendar. +# In such case, it would create an infinite loop, because no matter how +# far we look in the future, we will never find an open time window for this customer. +# In order to avoid this, we use LOOP_THRESHOLD as a hard limit about how +# far in the future we are willing to look for a valid delivery date. +# If no valid delivery_date date has been found within this date range, +# then the next day (with less restrictive constraints) will be used. +LOOP_THRESHOLD = 20 + class SaleOrderLine(models.Model): """This override adds delays to the date_deadline and the date_planned. @@ -259,11 +271,32 @@ def _delivery_date_from_expedition_date( ) else: earliest_delivery_date_naive = expedition_date - # /TODO extract - expected_delivery_date = self._apply_customer_window( - earliest_delivery_date_naive, partner + return self._get_next_open_customer_window(partner, calendar, from_date=earliest_delivery_date_naive) + + def _get_next_open_customer_window(self, partner, calendar, from_date=None): + if from_date is None: + from_date = datetime.today() + # Try to find an opened customer window within LOOP_THRESHOLD + for days in range(LOOP_THRESHOLD): + window_date = self._apply_customer_window( + from_date + timedelta(days=days), partner + ) + open_date = self._postpone_to_working_day( + datetime.combine(window_date, time.min), + calendar=calendar, + ) + if window_date.date() == open_date.date(): + # We found an opened delivery window + return window_date + # Fallback to the next customer window + + # TODO should we log something? + window_date = self._apply_customer_window(from_date, partner) + _logger.warning( + f"Unable to find a valid delivery date for line {self.name}. " + f"Falling back to {str(window_date.date())}." ) - return expected_delivery_date + return window_date @api.model def _expedition_date_from_delivery_date( diff --git a/sale_delivery_date/tests/test_integration.py b/sale_delivery_date/tests/test_integration.py index c5cdd172eeb..6ee24fed3b7 100644 --- a/sale_delivery_date/tests/test_integration.py +++ b/sale_delivery_date/tests/test_integration.py @@ -259,3 +259,59 @@ def test_friday_order_with_monday_leave(self): picking = order.picking_ids self.assertEqual(str(picking.scheduled_date), "2023-08-22 08:00:00") self.assertEqual(str(picking.expected_delivery_date.date()), "2023-08-23") + + @freeze_time("2023-10-09 08:00:00") + def test_delivery_window_on_public_holiday(self): + order = self.order_warehouse_cutoff + weekday_numbers = tuple(range(5)) + time_ranges = [(8.0, 12.0), (13.0, 17.5)] + self._set_calendar_attendances(self.calendar, weekday_numbers, time_ranges) + # Set customer to receive goods on Wednesday, from 12 to 17 + weekday_numbers = (2,) # Wednesday + time_window_ranges = [(12.00, 17.00), ] + self._set_partner_time_window( + self.customer_warehouse_cutoff, weekday_numbers, time_window_ranges + ) + # add a public holiday the 11th of October + days = ["2023-10-11", ] + self._add_calendar_leaves(self.calendar, days) + order.action_confirm() + picking = order.picking_ids + self.assertEqual(str(picking.scheduled_date.date()), "2023-10-17") + self.assertEqual(str(picking.date_deadline.date()), "2023-10-18") + + @freeze_time("2023-10-09 08:00:00") + def test_no_open_delivery_window_within_twenty_days(self): + order = self.order_warehouse_cutoff + weekday_numbers = tuple(range(5)) + time_ranges = [(8.0, 12.0), (13.0, 17.5)] + self._set_calendar_attendances(self.calendar, weekday_numbers, time_ranges) + # Set customer to receive goods on Wednesday, from 12 to 17 + weekday_numbers = (2,) # Wednesday + time_window_ranges = [(12.00, 17.00), ] + self._set_partner_time_window( + self.customer_warehouse_cutoff, weekday_numbers, time_window_ranges + ) + # add a public holiday all fridays + days = [ + "2023-10-11", + "2023-10-18", + "2023-10-25", + "2023-11-01", + ] + self._add_calendar_leaves(self.calendar, days) + logger_name = "odoo.addons.sale_delivery_date.models.sale_order_line" + with self.assertLogs(logger_name, level="WARNING") as watcher: + line = order.order_line + order.action_confirm() + expected_message = ( + f"WARNING:{logger_name}:" + f"Unable to find a valid delivery date for line {line.name}. " + f"Falling back to 2023-10-11." + ) + self.assertEqual(watcher.output[0], expected_message) + picking = order.picking_ids + # since we weren't able to find an open friday within 20 days, + # we fell back on the first one, being the 11th of october + self.assertEqual(str(picking.scheduled_date.date()), "2023-10-10") + self.assertEqual(str(picking.date_deadline.date()), "2023-10-11")