From 1c73f31001e65df33cc30abebf9bd02888402a0f Mon Sep 17 00:00:00 2001 From: Nicolas Delbovier Date: Tue, 31 Mar 2026 09:14:21 +0200 Subject: [PATCH 1/2] [FIX] shopfloor_reception: fix UTC boundaries for "Today" filters The `_get_today_start_end_datetime` function returned naive datetimes without accounting for the user's local timezone. Since Odoo stores datetimes in UTC, this caused incorrect filtering on the "Pickings to Process Today" screen for users in non-UTC timezones. Now, the function correctly calculates the start and end of the local day and converts those boundaries to UTC . --- shopfloor_reception/services/reception.py | 41 ++++++++++++------- .../tests/test_select_document.py | 19 ++++++--- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/shopfloor_reception/services/reception.py b/shopfloor_reception/services/reception.py index 349de78804a..c52f778b948 100644 --- a/shopfloor_reception/services/reception.py +++ b/shopfloor_reception/services/reception.py @@ -4,6 +4,8 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +from datetime import datetime, time + import pytz from decorator import contextmanager @@ -86,31 +88,40 @@ def _move_line_by_lot(self, lot): def _scheduled_date_today_domain(self): domain = [] - today_start, today_end = self._get_today_start_end_datetime() + today_start, today_end = self._get_today_start_end_datetime_utc() domain.append(("scheduled_date", ">=", today_start)) domain.append(("scheduled_date", "<=", today_end)) return domain - def _get_today_start_end_datetime(self, naive=True): + def _get_today_start_end_datetime_utc(self): + """ + Returns the start and end of the current day for the warehouse/company + timezone, converted to UTC naive datetimes. + """ # TODO: Put warehouse tz retrieval in shopfloor module? company = self.env.company warehouse = self.picking_types.warehouse_id - tz = ( + + tz_name = ( warehouse.partner_id.tz if (len(warehouse) == 1 and warehouse.partner_id.tz) else company.partner_id.tz or "UTC" ) - today = fields.Datetime.today() - today_start = today_start_localized = fields.Datetime.start_of(today, "day") - today_end = today_end_localized = fields.Datetime.end_of(today, "day") - if not naive: - today_start_localized = ( - pytz.timezone(tz).localize(today_start).astimezone(pytz.utc) - ) - today_end_localized = ( - pytz.timezone(tz).localize(today_end).astimezone(pytz.utc) - ) - return (today_start_localized, today_end_localized) + tz = pytz.timezone(tz_name) + + now_local = pytz.utc.localize(datetime.now()).astimezone(tz) + + local_start = datetime.combine( + now_local.date(), time.min, tzinfo=now_local.tzinfo + ) + local_end = datetime.combine( + now_local.date(), time.max, tzinfo=now_local.tzinfo + ) + + utc_start = local_start.astimezone(pytz.utc).replace(tzinfo=None) + utc_end = local_end.astimezone(pytz.utc).replace(tzinfo=None) + + return utc_start, utc_end @property def filter_today_scheduled_pickings(self): @@ -384,7 +395,7 @@ def _scan_document__by_picking(self, pickings, barcode): # could return more than one picking. # If there's only one picking due today, we go to the next screen. # Otherwise, we ask the user to scan a package instead. - today_start, today_end = self._get_today_start_end_datetime() + today_start, today_end = self._get_today_start_end_datetime_utc() picking_filter_result_due_today = picking_filter_result.filtered( lambda p: today_start <= p.scheduled_date < today_end ) diff --git a/shopfloor_reception/tests/test_select_document.py b/shopfloor_reception/tests/test_select_document.py index 7de580e35b4..d9a2aadb669 100644 --- a/shopfloor_reception/tests/test_select_document.py +++ b/shopfloor_reception/tests/test_select_document.py @@ -1,6 +1,8 @@ # Copyright 2022 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) # pylint: disable=missing-return +from datetime import datetime, timedelta + from freezegun import freeze_time from odoo import fields @@ -9,7 +11,6 @@ _TODAY = "2022-12-07" _TOMORROW = "2022-12-08" -_TODAY_ELEVEN = "2022-12-07 23:30:00" class TestSelectDocument(CommonCase): @@ -82,15 +83,21 @@ def test_scan_picking_origin_multiple_pickings_one_today(self): data=self._data_for_select_move(picking_today), ) - @freeze_time(_TODAY_ELEVEN, tz_offset=0) + # Since we use "Europe/Brussels" tz and _TODAY is in winter + # the shift is UTC+1 + @freeze_time(_TODAY, tz_offset=-1) def test_scan_picking_origin_multiple_pickings_one_today_tz(self): # freezed today is UTC time, set warehouse user to Brussels - self.wh.partner_id.sudo().tz = "Europe/Brussels" + tz_name = "Europe/Brussels" + self.wh.partner_id.sudo().tz = tz_name # Create a picking with the UTC hour - picking_today = self._create_picking(scheduled_date=_TODAY_ELEVEN) - - picking_tomorrow = self._create_picking(scheduled_date=_TOMORROW) + picking_today = self._create_picking( + scheduled_date=datetime.now() + timedelta(hours=23, minutes=30) + ) + picking_tomorrow = self._create_picking( + scheduled_date=datetime.now() + timedelta(days=1), + ) pickings = picking_today | picking_tomorrow pickings = pickings.sorted(lambda p: (p.scheduled_date, p.id), reverse=False) pickings.write({"origin": "Somewhere together"}) From 5b8d9eebc3b7a02487a10d5ce895fc7a85756a72 Mon Sep 17 00:00:00 2001 From: OCA-git-bot Date: Thu, 2 Apr 2026 10:32:31 +0000 Subject: [PATCH 2/2] [BOT] post-merge updates --- README.md | 2 +- shopfloor_reception/README.rst | 2 +- shopfloor_reception/__manifest__.py | 2 +- shopfloor_reception/static/description/index.html | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 908dc64d6b7..386dd19d096 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ addon | version | maintainers | summary [shopfloor_mobile](shopfloor_mobile/) | 16.0.1.4.0 | simahawk | Mobile frontend for WMS Shopfloor app [shopfloor_mobile_base](shopfloor_mobile_base/) | 16.0.1.1.0 | simahawk | Mobile frontend for WMS Shopfloor app [shopfloor_mobile_base_auth_api_key](shopfloor_mobile_base_auth_api_key/) | 16.0.1.0.0 | | Provides authentication via API key to Shopfloor base mobile app -[shopfloor_reception](shopfloor_reception/) | 16.0.1.6.5 | mmequignon JuMiSanAr | Reception scenario for shopfloor +[shopfloor_reception](shopfloor_reception/) | 16.0.1.6.6 | mmequignon JuMiSanAr | Reception scenario for shopfloor [shopfloor_reception_mobile](shopfloor_reception_mobile/) | 16.0.1.1.2 | JuMiSanAr | Scenario for receiving products [shopfloor_reception_refund_return](shopfloor_reception_refund_return/) | 16.0.1.0.0 | mmequignon | Mark created return as to refund [shopfloor_rest_log](shopfloor_rest_log/) | 16.0.1.0.0 | simahawk | Integrate rest_log into Shopfloor app diff --git a/shopfloor_reception/README.rst b/shopfloor_reception/README.rst index 6e0fdc30774..d6f1ddb0f96 100644 --- a/shopfloor_reception/README.rst +++ b/shopfloor_reception/README.rst @@ -11,7 +11,7 @@ Shopfloor Reception !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:40ba81ab584552fe151e2a4d6021706cb34380a9bdc9ed7f08963394a45030b9 + !! source digest: sha256:f8d87b75abf2f07115fe503fe887e2266295e7bc6e9e86e9818ff85ed88bff85 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/shopfloor_reception/__manifest__.py b/shopfloor_reception/__manifest__.py index 2346cf0deca..3cd79d28e16 100644 --- a/shopfloor_reception/__manifest__.py +++ b/shopfloor_reception/__manifest__.py @@ -1,7 +1,7 @@ { "name": "Shopfloor Reception", "summary": "Reception scenario for shopfloor", - "version": "16.0.1.6.5", + "version": "16.0.1.6.6", "development_status": "Beta", "category": "Inventory", "website": "https://github.com/OCA/wms", diff --git a/shopfloor_reception/static/description/index.html b/shopfloor_reception/static/description/index.html index a1af0acb72a..6a9251b2515 100644 --- a/shopfloor_reception/static/description/index.html +++ b/shopfloor_reception/static/description/index.html @@ -372,7 +372,7 @@

Shopfloor Reception

!! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:40ba81ab584552fe151e2a4d6021706cb34380a9bdc9ed7f08963394a45030b9 +!! source digest: sha256:f8d87b75abf2f07115fe503fe887e2266295e7bc6e9e86e9818ff85ed88bff85 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

Beta License: AGPL-3 OCA/wms Translate me on Weblate Try me on Runboat

Shopfloor implementation of the reception scenario.