diff --git a/.rubocop.yml b/.rubocop.yml
index 2c80d271f6..53cd5233dd 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -79,7 +79,6 @@ Lint/Void:
Lint/AmbiguousBlockAssociation:
AllowedMethods: [change]
-
Metrics/ClassLength:
Enabled: false
@@ -282,6 +281,9 @@ Style/EvenOdd:
Style/FormatString:
Enabled: false
+Style/FormatStringToken:
+ AllowedMethods: [redirect]
+
Style/GlobalVars:
Enabled: false
diff --git a/app/contracts/settings/working_days_params_contract.rb b/app/contracts/settings/working_days_params_contract.rb
new file mode 100644
index 0000000000..635e08a795
--- /dev/null
+++ b/app/contracts/settings/working_days_params_contract.rb
@@ -0,0 +1,47 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2022 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+module Settings
+ class WorkingDaysParamsContract < ::ParamsContract
+ include RequiresAdminGuard
+
+ validate :working_days_are_present
+
+ protected
+
+ def working_days_are_present
+ if working_days.empty?
+ errors.add :base, :working_days_are_missing
+ end
+ end
+
+ def working_days
+ params[:working_days]
+ end
+ end
+end
diff --git a/app/controllers/admin/settings/working_days_settings_controller.rb b/app/controllers/admin/settings/working_days_settings_controller.rb
new file mode 100644
index 0000000000..4a9842ced3
--- /dev/null
+++ b/app/controllers/admin/settings/working_days_settings_controller.rb
@@ -0,0 +1,25 @@
+module Admin::Settings
+ class WorkingDaysSettingsController < ::Admin::SettingsController
+ current_menu_item [:show] do
+ :working_days
+ end
+
+ def default_breadcrumb
+ t(:label_working_days)
+ end
+
+ def show_local_breadcrumb
+ true
+ end
+
+ def settings_params
+ settings = super
+ settings[:working_days] = settings[:working_days].compact_blank.map(&:to_i).uniq
+ settings
+ end
+
+ def contract_options
+ { params_contract: Settings::WorkingDaysParamsContract }
+ end
+ end
+end
diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb
index 3b06d5d389..744648b0a7 100644
--- a/app/controllers/admin/settings_controller.rb
+++ b/app/controllers/admin/settings_controller.rb
@@ -51,7 +51,7 @@ module Admin
return unless params[:settings]
call = ::Settings::UpdateService
- .new(user: current_user)
+ .new(user: current_user, contract_options:)
.call(settings_params)
call.on_success { flash[:notice] = t(:notice_successful_update) }
@@ -93,5 +93,9 @@ module Admin
def settings_params
permitted_params.settings.to_h
end
+
+ def contract_options
+ {}
+ end
end
end
diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb
index fe8d699ef8..9dcab181ee 100644
--- a/app/helpers/settings_helper.rb
+++ b/app/helpers/settings_helper.rb
@@ -76,8 +76,9 @@ module SettingsHelper
end
def setting_multiselect(setting, choices, options = {})
+ direction = options.delete(:direction) || :vertical
setting_label(setting, options) +
- content_tag(:span, class: 'form--field-container -vertical') do
+ content_tag(:span, class: "form--field-container -#{direction}") do
hidden = with_empty_unless_writable(setting) do
hidden_field_tag("settings[#{setting}][]", '')
end
diff --git a/app/models/day.rb b/app/models/day.rb
index 8dea0bd74b..c74a5bc3da 100644
--- a/app/models/day.rb
+++ b/app/models/day.rb
@@ -29,12 +29,6 @@
class Day < ApplicationRecord
include Tableless
- belongs_to :week_day,
- inverse_of: false,
- class_name: 'WeekDay',
- foreign_key: :day_of_week,
- primary_key: :day
-
has_many :non_working_days,
inverse_of: false,
class_name: 'NonWorkingDay',
@@ -53,7 +47,6 @@ class Day < ApplicationRecord
from = today.at_beginning_of_month
to = today.next_month.at_end_of_month
from_range(from:, to:)
- .includes(:week_day)
.includes(:non_working_days)
.order("days.id")
end
@@ -68,16 +61,21 @@ class Day < ApplicationRecord
to_char(dd, 'YYYYMMDD')::integer id,
date_trunc('day', dd)::date date,
extract(isodow from dd) day_of_week,
- (COALESCE(week_days.working, TRUE) AND non_working_days.id IS NULL)::bool working
+ (COALESCE(POSITION(extract(isodow from dd)::text IN settings.value) > 0, TRUE)
+ AND non_working_days.id IS NULL)::bool working
FROM
generate_series( '#{from}'::timestamp,
'#{to}'::timestamp,
'1 day'::interval) dd
- LEFT JOIN week_days
- ON extract(isodow from dd) = week_days.day
+ LEFT JOIN settings
+ ON settings.name = 'working_days'
LEFT JOIN non_working_days
ON dd = non_working_days.date
) days
SQL
end
+
+ def week_day
+ WeekDay.new(day: day_of_week)
+ end
end
diff --git a/app/models/week_day.rb b/app/models/week_day.rb
index ebc0b43a60..03b4296efa 100644
--- a/app/models/week_day.rb
+++ b/app/models/week_day.rb
@@ -1,6 +1,32 @@
-class WeekDay < ApplicationRecord
+class WeekDay
+ DAY_RANGE = Array(1..7)
+
+ attr_accessor :day
+
+ class << self
+ def find_by!(day:)
+ raise ActiveRecord::RecordNotFound, "Couldn't find WeekDay with day #{day}" unless day.in?(DAY_RANGE)
+
+ new(day:)
+ end
+
+ def all
+ DAY_RANGE.map do |day|
+ new(day:)
+ end
+ end
+ end
+
+ def initialize(day:)
+ self.day = day
+ end
+
def name
day_names = I18n.t('date.day_names')
day_names[day % 7]
end
+
+ def working
+ Setting.working_days.empty? || day.in?(Setting.working_days)
+ end
end
diff --git a/app/services/settings/update_service.rb b/app/services/settings/update_service.rb
index b317791b5a..8821cb0a4b 100644
--- a/app/services/settings/update_service.rb
+++ b/app/services/settings/update_service.rb
@@ -27,11 +27,23 @@
#++
class Settings::UpdateService < ::BaseServices::BaseContracted
- def initialize(user:)
+ def initialize(user:, contract_options: {})
super user:,
+ contract_options:,
contract_class: Settings::UpdateContract
end
+ def validate_params(params)
+ if contract_options[:params_contract]
+ contract = contract_options[:params_contract].new(model, user, params:)
+ ServiceResult.new success: contract.valid?,
+ errors: contract.errors,
+ result: model
+ else
+ super
+ end
+ end
+
def after_validate(params, call)
params.each do |name, value|
Setting[name] = derive_value(value)
@@ -44,11 +56,9 @@ class Settings::UpdateService < ::BaseServices::BaseContracted
def derive_value(value)
case value
- when Array
- # remove blank values in array settings
- value.delete_if(&:blank?)
- when Hash
- value.delete_if { |_, v| v.blank? }
+ when Array, Hash
+ # remove blank values in array, hash settings
+ value.compact_blank!
else
value.strip
end
diff --git a/app/services/work_packages/shared/working_days.rb b/app/services/work_packages/shared/working_days.rb
index 2d66b90d52..0d117083b3 100644
--- a/app/services/work_packages/shared/working_days.rb
+++ b/app/services/work_packages/shared/working_days.rb
@@ -146,13 +146,15 @@ module WorkPackages
# To accomodate both versions 0-6, 1-7, an array of 8 elements is created
# where array[0] = array[7] = value for Sunday
#
- # Because the database table for WeekDay could be empty or incomplete
- # (like in tests), the initial array is built with all days considered
- # working (value is `true`)
+ # Since Setting.working_days can be empty, the initial array is
+ # built with all days considered working (value is `true`)
+
@working_week_days = [true] * 8
- WeekDay.pluck(:day, :working).each do |day, working|
- @working_week_days[day] = working
+
+ WeekDay.all.each do |week_day|
+ @working_week_days[week_day.day] = week_day.working
end
+
@working_week_days[0] = @working_week_days[7] # value for Sunday is present at index 0 AND index 7
@working_week_days
end
diff --git a/app/views/admin/settings/working_days_settings/show.html.erb b/app/views/admin/settings/working_days_settings/show.html.erb
new file mode 100644
index 0000000000..49d3a62133
--- /dev/null
+++ b/app/views/admin/settings/working_days_settings/show.html.erb
@@ -0,0 +1,55 @@
+<%#-- copyright
+OpenProject is an open source project management software.
+Copyright (C) 2012-2022 the OpenProject GmbH
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License version 3.
+
+OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+Copyright (C) 2006-2013 Jean-Philippe Lang
+Copyright (C) 2010-2013 the ChiliProject Team
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2
+of the License, or (at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+See COPYRIGHT and LICENSE files for more details.
+
+++#%>
+
+<% content_for :header_tags do %>
+
+<% end %>
+
+<%= toolbar title: t(:label_working_days) %>
+
+
+
+
<%= t("working_days.warning") %>
+
+
+
+<%= styled_form_tag(admin_settings_working_days_path, method: :patch) do %>
+
+ <%= styled_button_tag t(:button_save), class: '-highlight -with-icon icon-checkmark' %>
+<% end %>
diff --git a/config/constants/settings/definitions.rb b/config/constants/settings/definitions.rb
index 938f093621..a649153815 100644
--- a/config/constants/settings/definitions.rb
+++ b/config/constants/settings/definitions.rb
@@ -986,6 +986,11 @@ Settings::Definition.define do
add :work_package_startdate_is_adddate,
default: false
+ add :working_days,
+ format: :array,
+ allowed: Array(1..7),
+ default: Array(1..5) # Sat, Sun being non-working days
+
add :youtube_channel,
default: 'https://www.youtube.com/c/OpenProjectCommunity',
writable: false
diff --git a/config/initializers/menus.rb b/config/initializers/menus.rb
index 61d2e36cf3..3eb4b6e9bf 100644
--- a/config/initializers/menus.rb
+++ b/config/initializers/menus.rb
@@ -276,6 +276,12 @@ Redmine::MenuManager.map :admin_menu do |menu|
if: Proc.new { User.current.admin? },
icon: 'icon2 icon-enumerations'
+ menu.push :working_days,
+ { controller: '/admin/settings/working_days_settings', action: :show },
+ if: Proc.new { User.current.admin? },
+ caption: :label_working_days,
+ icon: 'icon2 icon-calendar'
+
menu.push :settings,
{ controller: '/admin/settings/general_settings', action: :show },
if: Proc.new { User.current.admin? },
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 665e82d665..1bf11c6cd8 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -793,6 +793,10 @@ en:
attributes:
permissions:
dependency_missing: "need to also include '%{dependency}' as '%{permission}' is selected."
+ setting:
+ attributes:
+ base:
+ working_days_are_missing: 'At least one working day needs to be specified.'
time_entry:
attributes:
hours:
@@ -2007,6 +2011,7 @@ en:
label_workflow: "Workflow"
label_workflow_plural: "Workflows"
label_workflow_summary: "Summary"
+ label_working_days: "Working days"
label_x_closed_work_packages_abbr:
one: "1 closed"
other: "%{count} closed"
@@ -2668,7 +2673,6 @@ en:
status: "Entire row by Status"
type: "Entire row by Type"
priority: "Entire row by Priority"
-
text_formatting:
markdown: 'Markdown'
plain: 'Plain text'
@@ -3099,6 +3103,12 @@ en:
info: "Deleting the work package is an irreversible action."
title: "Delete the work package"
+ working_days:
+ info: >
+ Define days considered part of the work week
+ Days that are not selected are skipped when scheduling work packages (and not included in the day count). These can be overriden at a work-package level.
+ warning: >
+ Changing which days of the week are considered working days can affect the start and finish days of all work packages in all projects in this instance.
nothing_to_preview: "Nothing to preview"
api_v3:
diff --git a/config/routes.rb b/config/routes.rb
index 6d94943e17..044f8ea5d2 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -71,7 +71,7 @@ OpenProject::Application.routes.draw do
# forward requests to the proxy
if FrontendAssetHelper.assets_proxied?
match '/assets/frontend/*appendix',
- to: redirect(FrontendAssetHelper.cli_proxy + "/assets/frontend/%{appendix}", status: 307),
+ to: redirect("#{FrontendAssetHelper.cli_proxy}/assets/frontend/%{appendix}", status: 307),
format: false,
via: :all
end
@@ -143,10 +143,7 @@ OpenProject::Application.routes.draw do
resources :custom_fields, except: :show do
member do
- match "options/:option_id",
- to: "custom_fields#delete_option",
- via: :delete,
- as: :delete_option_of
+ delete "options/:option_id", to: "custom_fields#delete_option", as: :delete_option_of
post :reorder_alphabetical
end
@@ -192,7 +189,7 @@ OpenProject::Application.routes.draw do
end
member do
- get "settings", to: redirect('projects/%{id}/settings/general/') # rubocop:disable Style/FormatStringToken
+ get "settings", to: redirect('projects/%{id}/settings/general/')
get :copy
@@ -278,7 +275,7 @@ OpenProject::Application.routes.draw do
resources :members, only: %i[index create update destroy], shallow: true do
collection do
- match :autocomplete_for_member, via: %i[get]
+ get :autocomplete_for_member
end
end
@@ -306,7 +303,7 @@ OpenProject::Application.routes.draw do
%w{diff annotate changes entry browse}.each do |action|
get "(/revisions/:rev)/#{action}(/*repo_path)",
format: 'html',
- action: action,
+ action:,
constraints: { rev: /[\w0-9.\-_]+/, repo_path: /.*/ },
as: "#{action}_revision"
end
@@ -401,6 +398,7 @@ OpenProject::Application.routes.draw do
resource :mail_notifications, controller: '/admin/settings/mail_notifications_settings', only: %i[show update]
resource :api, controller: '/admin/settings/api_settings', only: %i[show update]
resource :work_packages, controller: '/admin/settings/work_packages_settings', only: %i[show update]
+ resource :working_days, controller: '/admin/settings/working_days_settings', only: %i[show update]
resource :users, controller: '/admin/settings/users_settings', only: %i[show update]
# Redirect /settings to general settings
@@ -468,7 +466,7 @@ OpenProject::Application.routes.draw do
member do
get '/edit(/:tab)' => 'users#edit', as: 'edit'
- match '/change_status/:change_action' => 'users#change_status_info', via: :get, as: 'change_status_info'
+ get '/change_status/:change_action' => 'users#change_status_info', as: 'change_status_info'
post :change_status
post :resend_invitation
get :deletion_info
@@ -535,9 +533,8 @@ OpenProject::Application.routes.draw do
# alternate routes for the current user
scope 'my' do
- match '/deletion_info' => 'users#deletion_info', via: :get, as: 'delete_my_account_info'
- match '/oauth/revoke_application/:application_id' => 'oauth/grants#revoke_application', via: :post,
- as: 'revoke_my_oauth_application'
+ get '/deletion_info' => 'users#deletion_info', as: 'delete_my_account_info'
+ post '/oauth/revoke_application/:application_id' => 'oauth/grants#revoke_application', as: 'revoke_my_oauth_application'
end
scope controller: 'my' do
diff --git a/db/migrate/20220909153412_drop_week_days.rb b/db/migrate/20220909153412_drop_week_days.rb
new file mode 100644
index 0000000000..e7602fd149
--- /dev/null
+++ b/db/migrate/20220909153412_drop_week_days.rb
@@ -0,0 +1,21 @@
+class DropWeekDays < ActiveRecord::Migration[7.0]
+ def up
+ drop_table :week_days
+ end
+
+ def down
+ create_table :week_days do |t|
+ t.integer :day, null: false
+ t.boolean :working, null: false, default: true
+
+ t.timestamps
+ end
+
+ execute <<-SQL.squish
+ ALTER TABLE week_days
+ ADD CONSTRAINT unique_day_number UNIQUE (day);
+ ALTER TABLE week_days
+ ADD CHECK (day >= 1 AND day <=7);
+ SQL
+ end
+end
diff --git a/lib/api/v3/days/week_api.rb b/lib/api/v3/days/week_api.rb
index 777dad4f91..2e4e14b383 100644
--- a/lib/api/v3/days/week_api.rb
+++ b/lib/api/v3/days/week_api.rb
@@ -31,10 +31,14 @@ module API::V3::Days
helpers ::API::Utilities::UrlPropsParsingHelper
resources :week do
- get &::API::V3::Utilities::Endpoints::Index.new(model: WeekDay,
- render_representer: WeekDayCollectionRepresenter,
- self_path: -> { api_v3_paths.days_week })
- .mount
+ get do
+ self_link = api_v3_paths.days_week
+ week_days = WeekDay.all
+ WeekDayCollectionRepresenter.new(week_days,
+ self_link:,
+ current_user:)
+ end
+
route_param :day, type: Integer, desc: 'WeekDay ID' do
after_validation do
@week_day = WeekDay.find_by!(day: declared_params[:day])
diff --git a/lib/api/v3/days/week_day_representer.rb b/lib/api/v3/days/week_day_representer.rb
index a1ec01d0ef..f6119b3981 100644
--- a/lib/api/v3/days/week_day_representer.rb
+++ b/lib/api/v3/days/week_day_representer.rb
@@ -39,5 +39,9 @@ module API::V3::Days
def _type
'WeekDay'
end
+
+ def json_key_part_represented
+ [represented.day, Setting.working_days]
+ end
end
end
diff --git a/modules/calendar/spec/features/calendar_dates_spec.rb b/modules/calendar/spec/features/calendar_dates_spec.rb
index bd635fb3ab..e217ce6b31 100644
--- a/modules/calendar/spec/features/calendar_dates_spec.rb
+++ b/modules/calendar/spec/features/calendar_dates_spec.rb
@@ -50,7 +50,7 @@ describe 'Calendar non working days', type: :feature, js: true do
end
context 'with week days defined' do
- let!(:week_days) { create :week_days }
+ let(:week_days) { week_with_saturday_and_sunday_as_weekend }
it 'renders sat and sun as non working' do
expect(page).to have_selector('.fc-day-sat.fc-non-working-day', minimum: 1, wait: 10)
@@ -76,13 +76,7 @@ describe 'Calendar non working days', type: :feature, js: true do
end
context 'with all days marked as weekend' do
- let!(:week_days) do
- days = create(:week_with_saturday_and_sunday_as_weekend)
-
- WeekDay.update_all(working: false)
-
- days
- end
+ let(:week_days) { week_with_no_working_days }
it 'renders all as non working' do
expect(page).to have_selector('.fc-day-sat.fc-non-working-day', minimum: 1, wait: 10)
diff --git a/modules/team_planner/spec/features/team_planner_dates_spec.rb b/modules/team_planner/spec/features/team_planner_dates_spec.rb
index 88ead683b6..a1508180b3 100644
--- a/modules/team_planner/spec/features/team_planner_dates_spec.rb
+++ b/modules/team_planner/spec/features/team_planner_dates_spec.rb
@@ -37,7 +37,7 @@ describe 'Team planner working days', type: :feature, js: true do
include_context 'with team planner full access'
context 'with week days defined' do
- let!(:week_days) { create :week_days }
+ let!(:week_days) { week_with_saturday_and_sunday_as_weekend }
it 'renders sat and sun as non working' do
team_planner.visit!
@@ -72,13 +72,7 @@ describe 'Team planner working days', type: :feature, js: true do
end
context 'with all days marked as weekend' do
- let!(:week_days) do
- days = create(:week_with_saturday_and_sunday_as_weekend)
-
- WeekDay.update_all(working: false)
-
- days
- end
+ let!(:week_days) { week_with_no_working_days }
it 'renders all as non working' do
team_planner.visit!
diff --git a/spec/contracts/settings/working_days_params_contract_spec.rb b/spec/contracts/settings/working_days_params_contract_spec.rb
new file mode 100644
index 0000000000..585bcb6466
--- /dev/null
+++ b/spec/contracts/settings/working_days_params_contract_spec.rb
@@ -0,0 +1,48 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2022 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require 'spec_helper'
+require 'contracts/shared/model_contract_shared_context'
+
+describe Settings::WorkingDaysParamsContract do
+ include_context 'ModelContract shared context'
+ let(:setting) { Setting }
+ let(:current_user) { build_stubbed(:admin) }
+ let(:params) { { working_days: [1] } }
+ let(:contract) do
+ described_class.new(setting, current_user, params:)
+ end
+
+ it_behaves_like 'contract is valid for active admins and invalid for regular users'
+
+ context 'without working days' do
+ let(:params) { { working_days: [] } }
+
+ include_examples 'contract is invalid', base: :working_days_are_missing
+ end
+end
diff --git a/spec/contracts/work_packages/base_contract_spec.rb b/spec/contracts/work_packages/base_contract_spec.rb
index 939c900f70..b90b76f005 100644
--- a/spec/contracts/work_packages/base_contract_spec.rb
+++ b/spec/contracts/work_packages/base_contract_spec.rb
@@ -600,7 +600,7 @@ describe WorkPackages::BaseContract do
context 'when setting duration and dates while covering non-working days' do
before do
- create(:week_with_saturday_and_sunday_as_weekend)
+ week_with_saturday_and_sunday_as_weekend
work_package.ignore_non_working_days = false
work_package.duration = 6
work_package.start_date = "2022-08-22"
@@ -623,7 +623,7 @@ describe WorkPackages::BaseContract do
context 'when setting duration and dates while covering non-working days and duration is too small' do
before do
- create(:week_with_saturday_and_sunday_as_weekend)
+ week_with_saturday_and_sunday_as_weekend
work_package.ignore_non_working_days = false
work_package.duration = 1
work_package.start_date = "2022-08-22"
@@ -646,7 +646,7 @@ describe WorkPackages::BaseContract do
context 'when setting duration and dates while covering non-working days and duration is too big' do
before do
- create(:week_with_saturday_and_sunday_as_weekend)
+ week_with_saturday_and_sunday_as_weekend
work_package.ignore_non_working_days = false
work_package.duration = 99
work_package.start_date = "2022-08-22"
diff --git a/spec/factories/week_day_factory.rb b/spec/factories/week_day_factory.rb
index 875eb78de8..a909209cba 100644
--- a/spec/factories/week_day_factory.rb
+++ b/spec/factories/week_day_factory.rb
@@ -28,45 +28,14 @@
FactoryBot.define do
factory :week_day do
- sequence :day, [1, 2, 3, 4, 5, 6, 7].cycle
- working { day < 6 }
-
- # hack to reuse the day if it already exists in database
- to_create do |instance|
- instance.attributes = WeekDay.find_or_create_by(instance.attributes.slice("day", "working")).attributes
- instance.instance_variable_set('@new_record', false)
- end
-
- trait :tuesday do
- day { 2 }
- end
- end
-
- # Factory to create all 7 week days at once, Saturday and Sunday being weekend days
- factory :week_with_saturday_and_sunday_as_weekend, aliases: [:week_days], parent: :week do
- working_days { %w[monday tuesday wednesday thursday friday] }
- end
-
- # Factory to create all 7 week days at once
- #
- # use +working: ['monday', 'tuesday', ...]+ to define which days of the week
- # will be working days. By default, all days are working days.
- factory :week, class: 'Array' do
- transient do
- working_days { %w[monday tuesday wednesday thursday friday saturday sunday] }
- end
-
# Skip the create callback to be able to use non-AR models. Otherwise FactoryBot will
# try to call #save! on any created object.
skip_create
+ sequence :day, [1, 2, 3, 4, 5, 6, 7].cycle
+
initialize_with do
- %w[monday tuesday wednesday thursday friday saturday sunday]
- .map.with_index do |day_name, i|
- day = i + 1
- working = working_days.include?(day_name)
- create(:week_day, day:, working:)
- end
+ new(day:)
end
end
end
diff --git a/spec/features/admin/working_days_spec.rb b/spec/features/admin/working_days_spec.rb
new file mode 100644
index 0000000000..4e12616ad7
--- /dev/null
+++ b/spec/features/admin/working_days_spec.rb
@@ -0,0 +1,86 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2022 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+require 'spec_helper'
+
+describe 'Working Days', type: :feature do
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
+ shared_let(:admin) { create :admin }
+
+ before do
+ login_as(admin)
+ visit admin_settings_working_days_path
+ end
+
+ it 'contains all defined days from the settings' do
+ WeekDay.all.each do |day|
+ expect(page).to have_selector('label', text: day.name)
+ if day.working
+ expect(page).to have_checked_field day.name
+ end
+ end
+ end
+
+ it 'updates the values and saves the settings' do
+ expect(Setting.working_days).to eq([1, 2, 3, 4, 5])
+ uncheck 'Monday'
+ uncheck 'Friday'
+ click_on 'Save'
+
+ expect(page).to have_selector('.flash.notice', text: 'Successful update.')
+ expect(page).to have_unchecked_field 'Monday'
+ expect(page).to have_unchecked_field 'Friday'
+ expect(page).to have_unchecked_field 'Saturday'
+ expect(page).to have_unchecked_field 'Sunday'
+ expect(page).to have_checked_field 'Tuesday'
+ expect(page).to have_checked_field 'Wednesday'
+ expect(page).to have_checked_field 'Thursday'
+ expect(Setting.working_days).to eq([2, 3, 4])
+ end
+
+ it 'shows error when no working days are set' do
+ uncheck 'Monday'
+ uncheck 'Tuesday'
+ uncheck 'Wednesday'
+ uncheck 'Thursday'
+ uncheck 'Friday'
+
+ click_on 'Save'
+
+ expect(page).to have_selector('.flash.error', text: 'At least one working day needs to be specified.')
+ # Restore the checkboxes to their valid state
+ expect(page).to have_checked_field 'Monday'
+ expect(page).to have_checked_field 'Tuesday'
+ expect(page).to have_checked_field 'Wednesday'
+ expect(page).to have_checked_field 'Thursday'
+ expect(page).to have_checked_field 'Friday'
+ expect(page).to have_unchecked_field 'Saturday'
+ expect(page).to have_unchecked_field 'Sunday'
+ expect(Setting.working_days).to eq([1, 2, 3, 4, 5])
+ end
+end
diff --git a/spec/features/menu_items/admin_menu_item_spec.rb b/spec/features/menu_items/admin_menu_item_spec.rb
index 3e45e69c45..e63ae075f1 100644
--- a/spec/features/menu_items/admin_menu_item_spec.rb
+++ b/spec/features/menu_items/admin_menu_item_spec.rb
@@ -44,8 +44,8 @@ describe 'Admin menu items', js: true do
visit admin_index_path
expect(page).to have_selector('[data-qa-selector="menu-blocks--container"]')
- expect(page).to have_selector('[data-qa-selector="menu-block"]', count: 19)
- expect(page).to have_selector('[data-qa-selector="op-menu--item-action"]', count: 20) # All plus 'overview'
+ expect(page).to have_selector('[data-qa-selector="menu-block"]', count: 20)
+ expect(page).to have_selector('[data-qa-selector="op-menu--item-action"]', count: 21) # All plus 'overview'
end
end
@@ -57,10 +57,10 @@ describe 'Admin menu items', js: true do
visit admin_index_path
expect(page).to have_selector('[data-qa-selector="menu-blocks--container"]')
- expect(page).to have_selector('[data-qa-selector="menu-block"]', count: 18)
+ expect(page).to have_selector('[data-qa-selector="menu-block"]', count: 19)
expect(page).not_to have_selector('[data-qa-selector="menu-block"]', text: I18n.t('timelines.admin_menu.colors'))
- expect(page).to have_selector('[data-qa-selector="op-menu--item-action"]', count: 19) # All plus 'overview'
+ expect(page).to have_selector('[data-qa-selector="op-menu--item-action"]', count: 20) # All plus 'overview'
expect(page).not_to have_selector('[data-qa-selector="op-menu--item-action"]', text: I18n.t('timelines.admin_menu.colors'))
end
end
diff --git a/spec/features/work_packages/datepicker/datepicker_logic_spec.rb b/spec/features/work_packages/datepicker/datepicker_logic_spec.rb
index 1901d7a8e8..3b68e1e270 100644
--- a/spec/features/work_packages/datepicker/datepicker_logic_spec.rb
+++ b/spec/features/work_packages/datepicker/datepicker_logic_spec.rb
@@ -42,7 +42,7 @@ describe 'Datepicker modal logic test cases (WP #43539)',
shared_let(:milestone_wp) { create :work_package, project:, type: type_milestone }
# assume sat+sun are non working days
- shared_let(:weekdays) { create :week_days }
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
let(:work_packages_page) { Pages::FullWorkPackage.new(work_package, project) }
let(:wp_table) { Pages::WorkPackagesTable.new(project) }
diff --git a/spec/features/work_packages/details/workdays_spec.rb b/spec/features/work_packages/details/workdays_spec.rb
index 8bda951ac1..a5bcddaf1e 100644
--- a/spec/features/work_packages/details/workdays_spec.rb
+++ b/spec/features/work_packages/details/workdays_spec.rb
@@ -44,7 +44,6 @@ describe 'Work packages datepicker workdays',
let(:combined_date) { work_packages_page.edit_field(:combinedDate) }
before do
- week_days
login_as(user)
work_packages_page.visit!
@@ -55,7 +54,7 @@ describe 'Work packages datepicker workdays',
end
context 'with default work days' do
- let(:week_days) { create :week_days }
+ shared_let(:working_days) { week_with_saturday_and_sunday_as_weekend }
it 'shows them as disabled' do
expect(page).to have_selector('.dayContainer', count: 2)
diff --git a/spec/features/work_packages/timeline/timeline_dates_spec.rb b/spec/features/work_packages/timeline/timeline_dates_spec.rb
index 7d33502d70..803c130476 100644
--- a/spec/features/work_packages/timeline/timeline_dates_spec.rb
+++ b/spec/features/work_packages/timeline/timeline_dates_spec.rb
@@ -65,7 +65,6 @@ RSpec.describe 'Work package timeline date formatting',
subject: 'Work Package ignoring non working days'
end
- let(:week_days) { nil }
let(:wp_timeline) { Pages::WorkPackagesTimeline.new(project) }
let!(:query_tl) do
query = build(:query, user: current_user, project:)
@@ -88,7 +87,6 @@ RSpec.describe 'Work package timeline date formatting',
end
before do
- week_days
login_as current_user
wp_timeline.visit_query query_tl
@@ -125,7 +123,8 @@ RSpec.describe 'Work package timeline date formatting',
context 'with weekdays defined' do
let(:current_user) { create :admin, language: 'en' }
- let(:week_days) { create :week_days }
+
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
it 'shows them as disabled' do
expect_date_week work_package.start_date.iso8601, '01'
@@ -160,8 +159,8 @@ RSpec.describe 'Work package timeline date formatting',
end
describe 'setting dates' do
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
let(:current_user) { create :admin }
- let(:week_days) { create :week_days }
let(:row) { wp_timeline.timeline_row work_package_with_non_working_days.id }
shared_examples "sets dates, duration and displays bar" do
diff --git a/spec/lib/api/v3/days/day_collection_representer_spec.rb b/spec/lib/api/v3/days/day_collection_representer_spec.rb
index 38bface9f4..22773be52c 100644
--- a/spec/lib/api/v3/days/day_collection_representer_spec.rb
+++ b/spec/lib/api/v3/days/day_collection_representer_spec.rb
@@ -29,7 +29,6 @@
require 'spec_helper'
describe ::API::V3::Days::DayCollectionRepresenter do
- let!(:week_days) { create(:week_with_saturday_and_sunday_as_weekend) }
let(:days) do
[
build(:day, date: Date.new(2022, 12, 27)),
diff --git a/spec/lib/api/v3/days/day_representer_spec.rb b/spec/lib/api/v3/days/day_representer_spec.rb
index d29f78d0d4..df381d4391 100644
--- a/spec/lib/api/v3/days/day_representer_spec.rb
+++ b/spec/lib/api/v3/days/day_representer_spec.rb
@@ -41,7 +41,7 @@ describe ::API::V3::Days::DayRepresenter do
subject(:generated) { representer.to_json }
before do
- create(:week_day, :tuesday, working:)
+ set_week_days('tuesday', working:)
end
it 'has _type: Day' do
diff --git a/spec/lib/api/v3/days/week_day_collection_representer_spec.rb b/spec/lib/api/v3/days/week_day_collection_representer_spec.rb
index af716f91db..d215623599 100644
--- a/spec/lib/api/v3/days/week_day_collection_representer_spec.rb
+++ b/spec/lib/api/v3/days/week_day_collection_representer_spec.rb
@@ -29,7 +29,7 @@
require 'spec_helper'
describe ::API::V3::Days::WeekDayCollectionRepresenter do
- let(:week_days) { build(:week_days) }
+ let(:week_days) { WeekDay.all }
let(:representer) do
described_class.new(week_days,
self_link: '/api/v3/days/week',
diff --git a/spec/lib/api/v3/days/week_day_representer_spec.rb b/spec/lib/api/v3/days/week_day_representer_spec.rb
index 82e955f606..5aeb4448f7 100644
--- a/spec/lib/api/v3/days/week_day_representer_spec.rb
+++ b/spec/lib/api/v3/days/week_day_representer_spec.rb
@@ -29,7 +29,7 @@
require 'spec_helper'
describe ::API::V3::Days::WeekDayRepresenter do
- let(:week_day) { build_stubbed(:week_day, day: 1) }
+ let(:week_day) { build(:week_day, day: 1) }
let(:representer) { described_class.new(week_day, current_user: instance_double(User, name: 'current_user')) }
describe '#to_json' do
@@ -99,8 +99,8 @@ describe ::API::V3::Days::WeekDayRepresenter do
end
end
- it 'changes when the week_day is updated' do
- week_day.updated_at = 20.seconds.from_now
+ it 'changes when the Setting is updated' do
+ set_week_days('tuesday')
expect(representer.json_cache_key)
.not_to eql former_cache_key
diff --git a/spec/models/day_spec.rb b/spec/models/day_spec.rb
index b180528bfb..2eaad53273 100644
--- a/spec/models/day_spec.rb
+++ b/spec/models/day_spec.rb
@@ -23,8 +23,8 @@ describe Day, type: :model do
)
end
- it 'eager loads week_day relation' do
- expect(days).to(be_all { |d| d.association(:week_day).loaded? })
+ it 'loads week_day method' do
+ expect(days).to(be_all { |d| d.week_day.present? })
end
it 'eager loads non_working_days relation' do
@@ -43,16 +43,16 @@ describe Day, type: :model do
expect(days.first.day_of_week % 7).to eq(today.at_beginning_of_month.wday) # wday is from 0-6
end
- it 'does not have a name' do
- expect(days.first.name).to be_nil
+ it 'loads the name attribute' do
+ expect(days.first.name).to eq(today.at_beginning_of_month.strftime("%A"))
end
end
context 'for collection with multiple non-working days' do
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
let(:non_working_dates) { [date_range.begin, date_range.begin + 1.day] }
before do
- create(:week_with_saturday_and_sunday_as_weekend)
non_working_dates.each { |date| create(:non_working_day, date:) }
end
@@ -78,10 +78,6 @@ describe Day, type: :model do
end
context 'with the weekday present' do
- before do
- create(:week_day, day: 6)
- end
-
it 'loads the name attribute' do
expect(subject.name).to eq('Saturday')
end
@@ -89,9 +85,7 @@ describe Day, type: :model do
describe '#working' do
context 'when the week day is non-working' do
- before do
- create(:week_day, day: 6, working: false)
- end
+ shared_let(:working_days) { week_with_no_working_days }
it 'is false' do
expect(subject.working).to be_falsy
@@ -109,9 +103,7 @@ describe Day, type: :model do
end
context 'when the week day is working' do
- before do
- create(:week_day, day: 6, working: true)
- end
+ shared_let(:working_days) { reset_working_week_days('saturday') }
it 'is true' do
expect(subject.working).to be_truthy
diff --git a/spec/models/setting_spec.rb b/spec/models/setting_spec.rb
index f1d6bfedae..be95b643ae 100644
--- a/spec/models/setting_spec.rb
+++ b/spec/models/setting_spec.rb
@@ -31,6 +31,7 @@ require 'spec_helper'
describe Setting, type: :model do
before do
described_class.clear_cache
+ described_class.destroy_all
end
after do
diff --git a/spec/models/week_day_spec.rb b/spec/models/week_day_spec.rb
index a239501265..1c2187441f 100644
--- a/spec/models/week_day_spec.rb
+++ b/spec/models/week_day_spec.rb
@@ -3,11 +3,11 @@ require 'rails_helper'
RSpec.describe WeekDay, type: :model do
describe '#name' do
it 'returns the translated week day name' do
- expect(described_class.create(day: 1).name).to eq('Monday')
- expect(described_class.create(day: 7).name).to eq('Sunday')
+ expect(described_class.new(day: 1).name).to eq('Monday')
+ expect(described_class.new(day: 7).name).to eq('Sunday')
I18n.with_locale(:de) do
- expect(described_class.create(day: 3).name).to eq('Mittwoch')
- expect(described_class.create(day: 4).name).to eq('Donnerstag')
+ expect(described_class.new(day: 3).name).to eq('Mittwoch')
+ expect(described_class.new(day: 4).name).to eq('Donnerstag')
end
end
end
diff --git a/spec/requests/api/v3/days/day_spec.rb b/spec/requests/api/v3/days/day_spec.rb
index 4b03a682d7..ed4deb247b 100644
--- a/spec/requests/api/v3/days/day_spec.rb
+++ b/spec/requests/api/v3/days/day_spec.rb
@@ -32,13 +32,13 @@ describe ::API::V3::Days::DaysAPI,
type: :request do
include API::V3::Utilities::PathHelper
+ shared_let(:working_days) { week_with_saturday_and_sunday_as_weekend }
let(:parsed_response) { JSON.parse(last_response.body) }
let(:filters) { [] }
current_user { user }
before do
- create(:week_with_saturday_and_sunday_as_weekend)
get api_v3_paths.path_for :days, filters:
end
diff --git a/spec/requests/api/v3/days/week_show_resource_spec.rb b/spec/requests/api/v3/days/week_show_resource_spec.rb
index dffb12dc1a..b9deb5a1a5 100644
--- a/spec/requests/api/v3/days/week_show_resource_spec.rb
+++ b/spec/requests/api/v3/days/week_show_resource_spec.rb
@@ -33,8 +33,7 @@ describe ::API::V3::Days::WeekAPI,
type: :request do
include API::V3::Utilities::PathHelper
- let(:week_day) { create(:week_day, day: 1) }
- let(:path) { api_v3_paths.days_week_day(week_day.day) }
+ let(:path) { api_v3_paths.days_week_day(1) }
current_user { user }
subject { last_response.body }
diff --git a/spec/requests/api/v3/days/week_spec.rb b/spec/requests/api/v3/days/week_spec.rb
index 6ac5b14515..38b80d61ce 100644
--- a/spec/requests/api/v3/days/week_spec.rb
+++ b/spec/requests/api/v3/days/week_spec.rb
@@ -38,7 +38,6 @@ describe ::API::V3::Days::WeekAPI,
current_user { user }
before do
- create(:week_with_saturday_and_sunday_as_weekend)
get api_v3_paths.days_week
end
diff --git a/spec/services/work_packages/set_attributes_service_spec.rb b/spec/services/work_packages/set_attributes_service_spec.rb
index 7e8f0fe6bc..c408e07d6c 100644
--- a/spec/services/work_packages/set_attributes_service_spec.rb
+++ b/spec/services/work_packages/set_attributes_service_spec.rb
@@ -1039,7 +1039,7 @@ describe WorkPackages::SetAttributesService,
end
context 'with non-working days' do
- shared_let(:week_days) { create(:week_with_saturday_and_sunday_as_weekend) }
+ shared_let(:working_days) { week_with_saturday_and_sunday_as_weekend }
let(:monday) { Time.zone.today.beginning_of_week }
let(:tuesday) { monday + 1.day }
let(:wednesday) { monday + 2.days }
@@ -1643,12 +1643,12 @@ describe WorkPackages::SetAttributesService,
end
context 'when the soonest start date is a non-working day' do
+ shared_let(:working_days) { week_with_saturday_and_sunday_as_weekend }
let(:saturday) { Time.zone.today.beginning_of_week.next_occurring(:saturday) }
let(:next_monday) { saturday.next_occurring(:monday) }
let(:soonest_start) { saturday }
before do
- create(:week_with_saturday_and_sunday_as_weekend)
work_package.ignore_non_working_days = false
end
diff --git a/spec/services/work_packages/set_schedule_service_working_days_spec.rb b/spec/services/work_packages/set_schedule_service_working_days_spec.rb
index 87e9250e7b..8d42075d5b 100644
--- a/spec/services/work_packages/set_schedule_service_working_days_spec.rb
+++ b/spec/services/work_packages/set_schedule_service_working_days_spec.rb
@@ -31,7 +31,7 @@ require 'spec_helper'
describe WorkPackages::SetScheduleService, 'working days' do
create_shared_association_defaults_for_work_package_factory
- shared_let(:week_days) { create(:week_with_saturday_and_sunday_as_weekend) }
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
let(:instance) do
described_class.new(user:, work_package:)
@@ -1023,10 +1023,10 @@ describe WorkPackages::SetScheduleService, 'working days' do
end
def set_non_working_week_days(*days)
- days.each do |day|
- wday = %w[xxx monday tuesday wednesday thursday friday saturday sunday].index(day.downcase)
- WeekDay.find_by!(day: wday).update(working: false)
+ non_working_days = days.map do |day|
+ %w[xxx monday tuesday wednesday thursday friday saturday sunday].index(day.downcase)
end
+ Setting.working_days -= non_working_days
end
context 'when moving forward due to days and predecessor due date now being non-working days' do
diff --git a/spec/services/work_packages/shared/shared_examples_days.rb b/spec/services/work_packages/shared/shared_examples_days.rb
index 2328121258..2ccd31d652 100644
--- a/spec/services/work_packages/shared/shared_examples_days.rb
+++ b/spec/services/work_packages/shared/shared_examples_days.rb
@@ -29,7 +29,7 @@
Date::DATE_FORMATS[:wday_date] = '%a %-d %b %Y' # Fri 5 Aug 2022
RSpec.shared_context 'with weekend days Saturday and Sunday' do
- shared_let(:week_days) { create(:week_with_saturday_and_sunday_as_weekend) }
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
end
RSpec.shared_context 'with non working days Christmas 2022 and new year 2023' do
@@ -41,7 +41,7 @@ RSpec.shared_context 'with no working days' do
include_context 'with weekend days Saturday and Sunday'
before do
- WeekDay.update_all(working: false)
+ week_with_no_working_days
end
end
diff --git a/spec/services/work_packages/shared/working_days_spec.rb b/spec/services/work_packages/shared/working_days_spec.rb
index ef49a86969..c3c99b7db8 100644
--- a/spec/services/work_packages/shared/working_days_spec.rb
+++ b/spec/services/work_packages/shared/working_days_spec.rb
@@ -181,7 +181,8 @@ RSpec.describe WorkPackages::Shared::WorkingDays do
describe '#add_days' do
it 'when positive, adds the number of working days to the date, ignoring non-working days' do
- create(:week_day, day: 5, working: false)
+ # Friday is a non working week day
+ set_non_working_week_days('friday')
create(:non_working_day, date: wednesday_2022_08_03)
# Wednesday is skipped (non working day)
@@ -195,7 +196,8 @@ RSpec.describe WorkPackages::Shared::WorkingDays do
end
it 'when negative, removes the number of working days to the date, ignoring non-working days' do
- create(:week_day, day: 5, working: false)
+ # Friday is a non working week day
+ set_non_working_week_days('friday')
create(:non_working_day, date: sunday_2022_07_31)
# Sunday is skipped (non working day)
diff --git a/spec/support/settings.rb b/spec/support/settings.rb
new file mode 100644
index 0000000000..8552938928
--- /dev/null
+++ b/spec/support/settings.rb
@@ -0,0 +1,78 @@
+#-- copyright
+# OpenProject is an open source project management software.
+# Copyright (C) 2012-2022 the OpenProject GmbH
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License version 3.
+#
+# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
+# Copyright (C) 2006-2013 Jean-Philippe Lang
+# Copyright (C) 2010-2013 the ChiliProject Team
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# See COPYRIGHT and LICENSE files for more details.
+#++
+
+def week_with_saturday_and_sunday_as_weekend
+ Setting.working_days = Array(1..5)
+end
+
+def week_with_all_days_working
+ Setting.working_days = Array(1..7)
+end
+
+def week_with_no_working_days
+ # This a hack to make all days non-working,
+ # because we don't allow that by definition
+ Setting.working_days = [false]
+end
+
+def set_non_working_week_days(*days)
+ week_days = get_week_days(*days)
+ Setting.working_days -= week_days
+end
+
+def set_working_week_days(*days)
+ week_days = get_week_days(*days)
+ Setting.working_days += week_days
+end
+
+def set_week_days(*days, working: true)
+ if working
+ set_working_week_days(*days)
+ else
+ set_non_working_week_days(*days)
+ end
+end
+
+def reset_working_week_days(*days)
+ week_days = get_week_days(*days)
+ Setting.working_days = week_days
+end
+
+def get_week_days(*days)
+ days.map do |day|
+ %w[xxx monday tuesday wednesday thursday friday saturday sunday].index(day.downcase)
+ end
+end
+
+RSpec.configure do |config|
+ config.before(:suite) do
+ # The test suite assumes the default of all days working.
+ # Since the Setting default is with Sat-Sun non-working, we update it before the tests.
+ week_with_all_days_working
+ end
+end
diff --git a/spec/support_spec/schedule_helpers/chart_representer_spec.rb b/spec/support_spec/schedule_helpers/chart_representer_spec.rb
index 0c858211db..a5dc73dd53 100644
--- a/spec/support_spec/schedule_helpers/chart_representer_spec.rb
+++ b/spec/support_spec/schedule_helpers/chart_representer_spec.rb
@@ -39,7 +39,7 @@ describe ScheduleHelpers::ChartRepresenter do
let(:sunday) { Date.new(2022, 6, 26) }
describe '#normalized_to_s' do
- let!(:week_days) { create(:week_with_saturday_and_sunday_as_weekend) }
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
context 'when both charts have different work packages items and/or order' do
def to_first_columns(charts)
diff --git a/spec/support_spec/schedule_helpers/chart_spec.rb b/spec/support_spec/schedule_helpers/chart_spec.rb
index 0043e44326..3b66ca4b73 100644
--- a/spec/support_spec/schedule_helpers/chart_spec.rb
+++ b/spec/support_spec/schedule_helpers/chart_spec.rb
@@ -157,7 +157,7 @@ describe ScheduleHelpers::Chart do
end
describe '#to_s' do
- let!(:week_days) { create(:week_with_saturday_and_sunday_as_weekend) }
+ shared_let(:week_days) { week_with_saturday_and_sunday_as_weekend }
context 'with a chart built from ascii representation' do
let(:chart) do
diff --git a/spec/workers/work_packages/apply_working_days_change_job_spec.rb b/spec/workers/work_packages/apply_working_days_change_job_spec.rb
index b749ba6f6e..df255d5f08 100644
--- a/spec/workers/work_packages/apply_working_days_change_job_spec.rb
+++ b/spec/workers/work_packages/apply_working_days_change_job_spec.rb
@@ -33,22 +33,7 @@ RSpec.describe WorkPackages::ApplyWorkingDaysChangeJob do
shared_let(:user) { create(:user) }
- let!(:week) { create(:week_with_saturday_and_sunday_as_weekend) }
-
- def set_non_working_week_days(*days)
- set_week_days(*days, working: false)
- end
-
- def set_working_week_days(*days)
- set_week_days(*days, working: true)
- end
-
- def set_week_days(*days, working:)
- days.each do |day|
- wday = %w[xxx monday tuesday wednesday thursday friday saturday sunday].index(day.downcase)
- WeekDay.find_by!(day: wday).update(working:)
- end
- end
+ let!(:week) { week_with_saturday_and_sunday_as_weekend }
context 'when a work package includes a date that is now a non-working day' do
let_schedule(<<~CHART)
@@ -138,7 +123,7 @@ RSpec.describe WorkPackages::ApplyWorkingDaysChangeJob do
end
context 'when a follower has a predecessor with dates covering a day that is now a working day' do
- let!(:week) { create(:week, working_days: ['monday', 'tuesday', 'thursday', 'friday']) }
+ let!(:week) { reset_working_week_days('monday', 'tuesday', 'thursday', 'friday') }
let_schedule(<<~CHART)
days | MTWTFSS |
@@ -161,7 +146,7 @@ RSpec.describe WorkPackages::ApplyWorkingDaysChangeJob do
end
xcontext 'when a follower has a predecessor with a non-working day between them that is now a working day' do
- let!(:week) { create(:week, working_days: ['monday', 'tuesday', 'thursday', 'friday']) }
+ let!(:week) { reset_working_week_days('monday', 'tuesday', 'thursday', 'friday') }
let_schedule(<<~CHART)
days | MTWTFSS |
@@ -245,7 +230,7 @@ RSpec.describe WorkPackages::ApplyWorkingDaysChangeJob do
end
xcontext 'when having multiple work packages following each other, and having days becoming working days' do
- let!(:week) { create(:week, working_days: ['monday', 'thursday']) }
+ let!(:week) { reset_working_week_days('monday', 'thursday') }
let_schedule(<<~CHART)
days | MTWTFSSmtwtfssmtwtfss |