From be06add73fa74a6da2995027a6e14e3e38ef55c4 Mon Sep 17 00:00:00 2001 From: Markus Boremski Date: Thu, 27 Aug 2026 11:02:04 +0000 Subject: [PATCH 1/2] Read the working days from Redmine's own configuration Fixes #30 The plugin kept its own set of weekday checkboxes although Redmine already has `Setting.non_working_week_days` under Administration -> Settings -> Issue tracking. Two places to answer the same question means they can disagree, and then the workload calculation contradicts the rest of Redmine without anyone being told. `WlDateTools.working_days` now derives from the core setting, which uses the same cwday numbering the plugin already used. The seven `general_workday_*` plugin settings and their fieldset are gone; the settings page points to the core setting instead. BREAKING for installations whose plugin settings differed from Redmine's: their working days change to whatever Redmine says. Everyone who left both at their defaults (Monday to Friday) sees no difference. The stored `general_workday_*` values are left untouched in the settings hash. They are simply no longer read, so nothing is lost if this has to be reverted. Tests: the four suites that configured the plugin settings now set `Setting.non_working_week_days`. Three of them were spelling out Monday to Friday, which is Redmine's default anyway. Added a case for an empty setting, where every day is a working day. The `workload_settings_general_workdays_*` day name keys stay in the locales; `app/views/wl_user_datas/_workday.html.erb` still refers to them. That partial is an unfinished per-user feature and is not rendered anywhere, so it was left alone. --- README.md | 7 +- app/views/settings/_workload_settings.erb | 95 ++--------------------- config/locales/de.yml | 1 + config/locales/en.yml | 1 + config/locales/it.yml | 1 + init.rb | 7 -- lib/redmine_workload/wl_date_tools.rb | 14 +--- test/unit/user_workload_test.rb | 12 +-- test/unit/wl_date_tools_test.rb | 37 ++++----- test/unit/wl_national_holiday_test.rb | 8 +- test/unit/wl_user_vacation_test.rb | 8 +- 11 files changed, 37 insertions(+), 154 deletions(-) diff --git a/README.md b/README.md index 016ccd1..c546c53 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ The encoding options are the same as on the page itself and might depend on your ### workday settings -Workday settings are fixed now (see [#27](https://github.com/xmera-circle/redmine_workload/issues/27)) but lead to restrictions for PostgreSQL user. +Workday settings were fixed in this version (see [#27](https://github.com/xmera-circle/redmine_workload/issues/27)) but led to restrictions for PostgreSQL user. Since 4.1.0 the working days come from Redmine's own configuration, see below. :warning: **PostgreSQL requires Ruby 3.1 or newer.** Redmine 6.1 requires Ruby 3.2 or newer anyway, so this is no longer a separate constraint for the 4.x line. @@ -109,7 +109,10 @@ Please refer to [redmine.org -> Plugins](https://www.redmine.org/projects/redmin There are three places where this plugin might be configured: 1. In the plugin settings, available in the administration area under `plugins`. -You can configure working days, thresholds here and set global holidays. +You can configure thresholds here and set global holidays. The working days +are **not** configured here: they are read from Redmine's own +`Administration -> Settings -> Issue tracking -> Non-working days`, so that the +workload calculation and the rest of Redmine agree on which days are worked. 2. In the roles section of the administration area, the plugin adds new permissions as described below. There is no need to configure this plugin on project level. diff --git a/app/views/settings/_workload_settings.erb b/app/views/settings/_workload_settings.erb index 950c060..5e5821b 100644 --- a/app/views/settings/_workload_settings.erb +++ b/app/views/settings/_workload_settings.erb @@ -1,96 +1,13 @@ <% # This file provides configuration options for the workload plugin. %> -
- <%= l(:workload_settings_general_workdays) %> - -

<%= l(:workload_settings_general_workdays_explanation) %>

- -

- - - - > -

-

- - - - > -

-

- - - - > -

-

- - - - > -

-

- - - - > -

-

- - - - > -

-

- - - - > -

+
+ <%= l(:workload_settings_general_workdays) %> + +

<%= l(:workload_settings_general_workdays_from_core) %>

+

<%= link_to l(:setting_non_working_week_days), settings_path(tab: 'issues') %>

+
<%= l(:workload_settings_hours) %> diff --git a/config/locales/de.yml b/config/locales/de.yml index 5a28ec0..c90b07d 100755 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -46,6 +46,7 @@ de: workload_overdue_issues_hours: "Stunden aus überfälligen Tickets:" workload_settings_general_workdays: "Wöchentliche Arbeitstage" workload_settings_general_workdays_explanation: "Diese Tage werden bei der Berechnung der Arbeitsbelastung als Arbeitstage angenommen:" + workload_settings_general_workdays_from_core: "Die Arbeitstage stammen aus Redmines eigener Konfiguration, damit beide übereinstimmen. Dort ändern:" workload_settings_general_workdays_monday: "Montag" workload_settings_general_workdays_tuesday: "Dienstag" workload_settings_general_workdays_wednesday: "Mittwoch" diff --git a/config/locales/en.yml b/config/locales/en.yml index b111ff9..c298e3c 100755 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -46,6 +46,7 @@ en: workload_overdue_issues_hours: "Hours from overdue issues:" workload_settings_general_workdays: "Weekly working days" workload_settings_general_workdays_explanation: "The following days will be considered as working days when computing the workload:" + workload_settings_general_workdays_from_core: "Working days are taken from Redmine's own configuration so that both agree. Change them there:" workload_settings_general_workdays_monday: "Monday:" workload_settings_general_workdays_tuesday: "Tuesday:" workload_settings_general_workdays_wednesday: "Wednesday:" diff --git a/config/locales/it.yml b/config/locales/it.yml index fa49e09..0799d04 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -45,6 +45,7 @@ it: workload_settings_general_workdays: "Giorni lavorativi settimanali" workload_settings_general_workdays_explanation: "I seguenti giorni settimanali saranno considerati come lavorativi:" + workload_settings_general_workdays_from_core: "I giorni lavorativi provengono dalla configurazione di Redmine, in modo che coincidano. Modificali qui:" workload_settings_general_workdays_monday: "Lunedì:" workload_settings_general_workdays_tuesday: "Martedì:" workload_settings_general_workdays_wednesday: "Mercoledì:" diff --git a/init.rb b/init.rb index 6958936..8c54b08 100755 --- a/init.rb +++ b/init.rb @@ -24,13 +24,6 @@ settings partial: 'settings/workload_settings', default: { - 'general_workday_monday' => 'checked', - 'general_workday_tuesday' => 'checked', - 'general_workday_wednesday' => 'checked', - 'general_workday_thursday' => 'checked', - 'general_workday_friday' => 'checked', - 'general_workday_saturday' => '', - 'general_workday_sunday' => '', 'threshold_lowload_min' => 0.1, 'threshold_normalload_min' => 7, 'threshold_highload_min' => 8.5, diff --git a/lib/redmine_workload/wl_date_tools.rb b/lib/redmine_workload/wl_date_tools.rb index 4d2ccb4..c1eb4b0 100644 --- a/lib/redmine_workload/wl_date_tools.rb +++ b/lib/redmine_workload/wl_date_tools.rb @@ -37,18 +37,10 @@ def self.months_in_time_span(time_span) # Returns a list of all regular working weekdays. # 1 is monday, 7 is sunday (same as in Date::cwday) + # + # Taken from Redmine's own 'Non-working days' setting so that both agree. def self.working_days - result = Set.new - - result.add(1) if settings['general_workday_monday'] != '' - result.add(2) if settings['general_workday_tuesday'] != '' - result.add(3) if settings['general_workday_wednesday'] != '' - result.add(4) if settings['general_workday_thursday'] != '' - result.add(5) if settings['general_workday_friday'] != '' - result.add(6) if settings['general_workday_saturday'] != '' - result.add(7) if settings['general_workday_sunday'] != '' - - result + Set.new((1..7).to_a - Array(Setting.non_working_week_days).map(&:to_i)) end def self.working_days_in_time_span(time_span, assignee, no_cache: false) diff --git a/test/unit/user_workload_test.rb b/test/unit/user_workload_test.rb index ec294cb..e11c565 100644 --- a/test/unit/user_workload_test.rb +++ b/test/unit/user_workload_test.rb @@ -788,16 +788,10 @@ def teardown private - # Set Saturday, Sunday and Wednesday to be a holiday, all others to be a - # working day. + # Set Saturday, Sunday and Wednesday to be a non-working day, all others to + # be a working day. def define_saturday_sunday_and_wednesday_as_holiday - settings['general_workday_monday'] = 'checked' - settings['general_workday_tuesday'] = 'checked' - settings['general_workday_wednesday'] = '' - settings['general_workday_thursday'] = 'checked' - settings['general_workday_friday'] = 'checked' - settings['general_workday_saturday'] = '' - settings['general_workday_sunday'] = '' + Setting.non_working_week_days = %w[3 6 7] end def assert_issue_times_hash_equals(expected, actual) diff --git a/test/unit/wl_date_tools_test.rb b/test/unit/wl_date_tools_test.rb index 31eebdd..3c6cada 100644 --- a/test/unit/wl_date_tools_test.rb +++ b/test/unit/wl_date_tools_test.rb @@ -12,15 +12,15 @@ def setup test 'working_days_in_time_span works if start and end day are equal and no holiday.' do # Set friday to be a working day. - Setting['plugin_redmine_workload']['general_workday_friday'] = 'checked' + Setting.non_working_week_days = %w[6 7] date = Date.new(2005, 12, 30) # A friday assert_equal Set.new([date]), RedmineWorkload::WlDateTools.working_days_in_time_span(date..date, @user, no_cache: true) end test 'working_days_in_time_span works if start and end day are equal and a holiday.' do - # Set friday to be a holiday. - Setting['plugin_redmine_workload']['general_workday_friday'] = '' + # Set friday to be a non-working day. + Setting.non_working_week_days = %w[5 6 7] date = Date.new(2005, 12, 30) # A friday assert_equal Set.new, RedmineWorkload::WlDateTools.working_days_in_time_span(date..date, @user, no_cache: true) @@ -33,9 +33,8 @@ def setup end test 'working_days_in_time_span works if both days follow each other and are holidays.' do - # Set wednesday and thursday to be a holiday. - Setting['plugin_redmine_workload']['general_workday_wednesday'] = '' - Setting['plugin_redmine_workload']['general_workday_thursday'] = '' + # Set wednesday and thursday to be non-working days. + Setting.non_working_week_days = %w[3 4] start_date = Date.new(2005, 12, 28) # A wednesday end_date = Date.new(2005, 12, 29) # A thursday @@ -43,14 +42,8 @@ def setup end test 'working_days_in_time_span works if only weekends and mondays are holidays and startday is thursday, endday is tuesday.' do - # Set saturday, sunday and monday to be a holiday, all others to be a working day. - Setting['plugin_redmine_workload']['general_workday_monday'] = '' - Setting['plugin_redmine_workload']['general_workday_tuesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_wednesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_thursday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_friday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_saturday'] = '' - Setting['plugin_redmine_workload']['general_workday_sunday'] = '' + # Set saturday, sunday and monday to be non-working days. + Setting.non_working_week_days = %w[1 6 7] start_date = Date.new(2005, 12, 29) # A thursday end_date = Date.new(2006, 1, 3) # A tuesday @@ -65,18 +58,18 @@ def setup end test 'working_days returns the working days.' do - # Set saturday, sunday and monday to be a holiday, all others to be a working day. - Setting['plugin_redmine_workload']['general_workday_monday'] = '' - Setting['plugin_redmine_workload']['general_workday_tuesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_wednesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_thursday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_friday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_saturday'] = '' - Setting['plugin_redmine_workload']['general_workday_sunday'] = '' + # Set saturday, sunday and monday to be non-working days. + Setting.non_working_week_days = %w[1 6 7] assert_equal Set.new([2, 3, 4, 5]), RedmineWorkload::WlDateTools.working_days end + test 'working_days returns all days when Redmine has no non-working days.' do + Setting.non_working_week_days = [] + + assert_equal Set.new([1, 2, 3, 4, 5, 6, 7]), RedmineWorkload::WlDateTools.working_days + end + test 'getMonthsBetween returns [] if last day after first day' do first_day = Date.new(2012, 3, 29) last_day = Date.new(2012, 3, 28) diff --git a/test/unit/wl_national_holiday_test.rb b/test/unit/wl_national_holiday_test.rb index 5c33b8a..1a7fc54 100644 --- a/test/unit/wl_national_holiday_test.rb +++ b/test/unit/wl_national_holiday_test.rb @@ -9,13 +9,7 @@ class WlNationalHolidayTest < ActiveSupport::TestCase setup do # reset default settings - Setting['plugin_redmine_workload']['general_workday_monday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_tuesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_wednesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_thursday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_friday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_saturday'] = '' - Setting['plugin_redmine_workload']['general_workday_sunday'] = '' + Setting.non_working_week_days = %w[6 7] end test 'single holiday' do diff --git a/test/unit/wl_user_vacation_test.rb b/test/unit/wl_user_vacation_test.rb index 2d347e1..5045a1a 100644 --- a/test/unit/wl_user_vacation_test.rb +++ b/test/unit/wl_user_vacation_test.rb @@ -9,13 +9,7 @@ class WlUserVacationTest < ActiveSupport::TestCase setup do # reset default settings - Setting['plugin_redmine_workload']['general_workday_monday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_tuesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_wednesday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_thursday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_friday'] = 'checked' - Setting['plugin_redmine_workload']['general_workday_saturday'] = '' - Setting['plugin_redmine_workload']['general_workday_sunday'] = '' + Setting.non_working_week_days = %w[6 7] end test 'Vacation for user' do From 849fcfb4e9fdb4ba60e53079a971f18ff5db16f2 Mon Sep 17 00:00:00 2001 From: Markus Boremski Date: Thu, 27 Aug 2026 13:33:33 +0000 Subject: [PATCH 2/2] Say that the working days are cached for up to 12 hours `WlDateTools.working_days_in_time_span` caches per user and time span with `expires_in: 12.hours`. With the plugin's own checkboxes that was already true but rarely noticed; now that the days come from Redmine's own settings, more people will change them and wonder why the workload view does not follow. Rather than rebuilding the caching, name it where it bites: a warning on the plugin settings page next to the link, and a note in the README with the two ways out (restart, or clear the cache). The note also mentions that holidays and vacations go through the same cache. --- README.md | 6 ++++++ app/views/settings/_workload_settings.erb | 2 ++ config/locales/de.yml | 1 + config/locales/en.yml | 1 + config/locales/it.yml | 1 + 5 files changed, 11 insertions(+) diff --git a/README.md b/README.md index c546c53..68833bb 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,12 @@ are **not** configured here: they are read from Redmine's own `Administration -> Settings -> Issue tracking -> Non-working days`, so that the workload calculation and the rest of Redmine agree on which days are worked. +:warning: Changing the working days does not show up in the workload view right +away. `WlDateTools.working_days_in_time_span` caches its result per user and +time span for up to 12 hours. Restart Redmine or clear its cache +(`rake tmp:cache:clear`, or remove `tmp/cache`) to apply the change +immediately. The same applies to global holidays and vacations. + 2. In the roles section of the administration area, the plugin adds new permissions as described below. There is no need to configure this plugin on project level. diff --git a/app/views/settings/_workload_settings.erb b/app/views/settings/_workload_settings.erb index 5e5821b..225bc29 100644 --- a/app/views/settings/_workload_settings.erb +++ b/app/views/settings/_workload_settings.erb @@ -6,6 +6,8 @@

<%= l(:workload_settings_general_workdays_from_core) %>

<%= link_to l(:setting_non_working_week_days), settings_path(tab: 'issues') %>

+ +

<%= sprite_icon('warning', l(:workload_settings_general_workdays_cache_hint)) %>

diff --git a/config/locales/de.yml b/config/locales/de.yml index c90b07d..65cc8a0 100755 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -47,6 +47,7 @@ de: workload_settings_general_workdays: "Wöchentliche Arbeitstage" workload_settings_general_workdays_explanation: "Diese Tage werden bei der Berechnung der Arbeitsbelastung als Arbeitstage angenommen:" workload_settings_general_workdays_from_core: "Die Arbeitstage stammen aus Redmines eigener Konfiguration, damit beide übereinstimmen. Dort ändern:" + workload_settings_general_workdays_cache_hint: "Eine Änderung wirkt nicht sofort: die Berechnung wird je Anwender und Zeitraum bis zu 12 Stunden zwischengespeichert. Für eine sofortige Wirkung Redmine neu starten oder den Cache leeren." workload_settings_general_workdays_monday: "Montag" workload_settings_general_workdays_tuesday: "Dienstag" workload_settings_general_workdays_wednesday: "Mittwoch" diff --git a/config/locales/en.yml b/config/locales/en.yml index c298e3c..933881e 100755 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -47,6 +47,7 @@ en: workload_settings_general_workdays: "Weekly working days" workload_settings_general_workdays_explanation: "The following days will be considered as working days when computing the workload:" workload_settings_general_workdays_from_core: "Working days are taken from Redmine's own configuration so that both agree. Change them there:" + workload_settings_general_workdays_cache_hint: "Changing them does not show up right away: the calculation is cached per user and time span for up to 12 hours. Restart Redmine or clear its cache to apply the change immediately." workload_settings_general_workdays_monday: "Monday:" workload_settings_general_workdays_tuesday: "Tuesday:" workload_settings_general_workdays_wednesday: "Wednesday:" diff --git a/config/locales/it.yml b/config/locales/it.yml index 0799d04..d799f94 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -46,6 +46,7 @@ it: workload_settings_general_workdays: "Giorni lavorativi settimanali" workload_settings_general_workdays_explanation: "I seguenti giorni settimanali saranno considerati come lavorativi:" workload_settings_general_workdays_from_core: "I giorni lavorativi provengono dalla configurazione di Redmine, in modo che coincidano. Modificali qui:" + workload_settings_general_workdays_cache_hint: "Una modifica non ha effetto immediato: il calcolo viene memorizzato nella cache per utente e periodo fino a 12 ore. Per applicarla subito, riavvia Redmine o svuota la cache." workload_settings_general_workdays_monday: "Lunedì:" workload_settings_general_workdays_tuesday: "Martedì:" workload_settings_general_workdays_wednesday: "Mercoledì:"