From 02ea85a12fedc35a9fa5f7b18667bb25009fbd2a Mon Sep 17 00:00:00 2001 From: "Levi Siuzdak (sile)" Date: Mon, 8 Jan 2024 19:00:07 +0100 Subject: [PATCH] [FIX] web, hr_holidays: homogenize week numbering Versions -------- - 17.0 - 17.1 - master Steps ----- 1. Go to Settings / Manage Languages; 2. select your current language; 3. set First Day of Week to something other than Sunday; 4. go to Time Off app. Issue ----- Weeks in year overview still start on a Sunday. Cause ----- Commit 52dae7a2f00c41222cf4518617d050efa8d34359 hardcoded `firstDay` to Sunday for the `hr_holidays` module. This was a workaround to some issues with `fullcalendar`'s week number calculations. Solution -------- Remove the hardcoded `firstDay`, and add a custom week numbering function to be used on week, month, and year calendar views for consistent numbering that allows for different first days of the week. The function returns the ISO week number of the Monday nearest to the configured first day of the week, i.e. the following Monday when first day is set to Friday, Saturday or Sunday, the previous Monday if first day is set to Tuesday, Wednesday or Thursday. There were 3 main considerations for deciding a week numbering method: 1. no exisiting setting for users to decide on a method; 2. the ability to pick a first day of the week independent of locale; 3. the version of `luxon` used being unable to factor in locale. Addendum -------- This commit doesn't fix the issue with group-by week numbering in list view. These stem from `babel`'s inconsistent locale defaults and inability to take user-configured first day of the week into account. opw-3668175 closes odoo/odoo#148623 Signed-off-by: Bruno Boi (boi) --- .../calendar/year/calendar_year_renderer.js | 1 - .../calendar_common_renderer.js | 3 +- .../src/views/calendar/calendar_controller.js | 3 +- .../calendar_year/calendar_year_renderer.js | 3 +- addons/web/static/src/views/calendar/utils.js | 30 +++++++++++++++++++ .../views/calendar/calendar_view_tests.js | 6 ++-- 6 files changed, 39 insertions(+), 7 deletions(-) diff --git a/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.js b/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.js index cdfd919778e..75985607dd2 100644 --- a/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.js +++ b/addons/hr_holidays/static/src/views/calendar/year/calendar_year_renderer.js @@ -44,7 +44,6 @@ export class TimeOffCalendarYearRenderer extends CalendarYearRenderer { weekNumbers: true, weekNumbersWithinDays: false, weekLabel: _t("Week"), - firstDay: 0, }); } diff --git a/addons/web/static/src/views/calendar/calendar_common/calendar_common_renderer.js b/addons/web/static/src/views/calendar/calendar_common/calendar_common_renderer.js index 8e89fdd201c..aced367c18f 100644 --- a/addons/web/static/src/views/calendar/calendar_common/calendar_common_renderer.js +++ b/addons/web/static/src/views/calendar/calendar_common/calendar_common_renderer.js @@ -9,6 +9,7 @@ import { getColor } from "../colors"; import { useCalendarPopover, useClickHandler, useFullCalendar } from "../hooks"; import { CalendarCommonPopover } from "./calendar_common_popover"; import { browser } from "@web/core/browser/browser"; +import { getWeekNumber } from "../utils"; import { Component, onMounted, useEffect } from "@odoo/owl"; @@ -112,7 +113,7 @@ export class CalendarCommonRenderer extends Component { unselectAuto: false, weekLabel: this.props.model.scale === "month" && this.env.isSmall ? "" : _t("Week"), weekends: this.props.isWeekendVisible, - weekNumberCalculation: "ISO", + weekNumberCalculation: (date) => getWeekNumber(date, this.props.model.firstDayOfWeek), weekNumbers: true, weekNumbersWithinDays: !this.env.isSmall, windowResize: this.onWindowResizeDebounced, diff --git a/addons/web/static/src/views/calendar/calendar_controller.js b/addons/web/static/src/views/calendar/calendar_controller.js index fc14fdc0f38..107b291fcbe 100644 --- a/addons/web/static/src/views/calendar/calendar_controller.js +++ b/addons/web/static/src/views/calendar/calendar_controller.js @@ -19,6 +19,7 @@ import { useSearchBarToggler } from "@web/search/search_bar/search_bar_toggler"; import { ViewScaleSelector } from "@web/views/view_components/view_scale_selector"; import { CogMenu } from "@web/search/cog_menu/cog_menu"; import { browser } from "@web/core/browser/browser"; +import { getWeekNumber } from "./utils"; import { Component, useState } from "@odoo/owl"; @@ -137,7 +138,7 @@ export class CalendarController extends Component { } get currentWeek() { - return this.date.toFormat("W"); + return getWeekNumber(this.model.rangeStart); } get rendererProps() { diff --git a/addons/web/static/src/views/calendar/calendar_year/calendar_year_renderer.js b/addons/web/static/src/views/calendar/calendar_year/calendar_year_renderer.js index 7673953d6d5..7ba34717b2f 100644 --- a/addons/web/static/src/views/calendar/calendar_year/calendar_year_renderer.js +++ b/addons/web/static/src/views/calendar/calendar_year/calendar_year_renderer.js @@ -5,6 +5,7 @@ import { useDebounced } from "@web/core/utils/timing"; import { getColor } from "../colors"; import { useCalendarPopover, useFullCalendar } from "../hooks"; import { CalendarYearPopover } from "./calendar_year_popover"; +import { getWeekNumber } from "../utils"; import { Component, useEffect, useRef } from "@odoo/owl"; @@ -58,7 +59,7 @@ export class CalendarYearRenderer extends Component { timeZone: luxon.Settings.defaultZone.name, titleFormat: { month: "long", year: "numeric" }, unselectAuto: false, - weekNumberCalculation: "ISO", + weekNumberCalculation: (date) => getWeekNumber(date, this.props.model.firstDayOfWeek), weekNumbers: false, windowResize: this.onWindowResizeDebounced, }; diff --git a/addons/web/static/src/views/calendar/utils.js b/addons/web/static/src/views/calendar/utils.js index 7378ddf104b..a22b3c7a53c 100644 --- a/addons/web/static/src/views/calendar/utils.js +++ b/addons/web/static/src/views/calendar/utils.js @@ -12,3 +12,33 @@ export function getFormattedDateSpan(start, end) { : start.toFormat("DDD") + " - " + end.toFormat("DDD"); } } + +/** + * Get the week number of a given date. + * Returns the ISO week number of the Monday nearest to the first day of the week. + * + * @param {Date | luxon.DateTime} date + * @param {number} first day of the week (optional) + * @returns {number} week number + */ +export function getWeekNumber(date, firstDay) { + if (!date.isLuxonDateTime) { + date = luxon.DateTime.fromJSDate(date); + } + if (Number.isInteger(firstDay)) { // go to start of week + date = date.minus({ days: (date.weekday + 7 - firstDay) % 7 }); + } else { + firstDay = date.weekday; + } + // go to nearest Monday, up to 3 days back- or forwards + date = firstDay > 1 && firstDay < 5 // if firstDay after Mon & before Fri + ? date.minus({ days: (date.weekday + 6) % 7 }) // then go back 1-3 days + : date.plus({ days: (8 - date.weekday) % 7 }); // else go forwards 0-3 days + date = date.plus({ days: 6 }); // go to last weekday of ISO week + const jan4 = luxon.DateTime.local(date.year, 1, 4); + // count from previous year if week falls before Jan 4 + const diffDays = date < jan4 + ? date.diff(jan4.minus({ years: 1 }), 'day').days + : date.diff(jan4, 'day').days; + return Math.trunc(diffDays / 7) + 1; +} diff --git a/addons/web/static/tests/views/calendar/calendar_view_tests.js b/addons/web/static/tests/views/calendar/calendar_view_tests.js index 3076a7c764b..8534dc20385 100644 --- a/addons/web/static/tests/views/calendar/calendar_view_tests.js +++ b/addons/web/static/tests/views/calendar/calendar_view_tests.js @@ -1241,8 +1241,8 @@ QUnit.module("Views", ({ beforeEach }) => { }); QUnit.test(`week numbering`, async (assert) => { - // The week is now calculated by FullCalendar (ISO week). If it's start a sunday it - // returns the week of the monday. + // Using ISO week calculation, get the ISO week number of + // the Monday nearest to the start of the week. patchWithCleanup(localization, { weekStart: 7 }); @@ -4214,7 +4214,7 @@ QUnit.module("Views", ({ beforeEach }) => { ); assert.strictEqual( target.querySelector(".fc-head .fc-week-number").textContent, - "Week 36", + "Week 37", "The number of the week should be correct" ); });