45001-component-to-show-the-list-of-non-working-days-of-year
commit
8aa7ec0250
@ -0,0 +1,31 @@ |
||||
#-- 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. |
||||
#++ |
||||
|
||||
class Journal::ProjectJournal < Journal::BaseJournal |
||||
self.table_name = 'project_journals' |
||||
end |
@ -0,0 +1,40 @@ |
||||
#-- 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 Projects::Journalized |
||||
extend ActiveSupport::Concern |
||||
|
||||
included do |
||||
acts_as_journalized |
||||
end |
||||
|
||||
# override acts_as_journalized method |
||||
def activity_type |
||||
'project_attributes' |
||||
end |
||||
end |
@ -0,0 +1,44 @@ |
||||
class RemoveActivityTypeFromJournalsTable < ActiveRecord::Migration[7.0] |
||||
def up |
||||
remove_column :journals, :activity_type |
||||
end |
||||
|
||||
def down |
||||
add_column :journals, :activity_type, :string |
||||
|
||||
execute <<-SQL.squish |
||||
UPDATE |
||||
journals |
||||
SET |
||||
activity_type = |
||||
CASE |
||||
WHEN journable_type = 'Attachment' THEN 'attachments' |
||||
WHEN journable_type = 'Budget' THEN 'budgets' |
||||
WHEN journable_type = 'Changeset' THEN 'changesets' |
||||
WHEN journable_type = 'Document' THEN 'documents' |
||||
WHEN journable_type = 'Meeting' THEN 'meetings' |
||||
WHEN journable_type = 'Message' THEN 'messages' |
||||
WHEN journable_type = 'News' THEN 'news' |
||||
WHEN journable_type = 'TimeEntry' THEN 'time_entries' |
||||
WHEN journable_type = 'WikiContent' THEN 'wiki_edits' |
||||
WHEN journable_type = 'WorkPackage' THEN 'work_packages' |
||||
END |
||||
WHERE journable_type != 'MeetingContent' |
||||
SQL |
||||
|
||||
execute <<-SQL.squish |
||||
UPDATE |
||||
journals |
||||
SET |
||||
activity_type = |
||||
CASE |
||||
WHEN meeting_contents.type = 'MeetingMinutes' THEN 'meeting_minutes' |
||||
WHEN meeting_contents.type = 'MeetingAgenda' THEN 'meeting_agenda' |
||||
ELSE 'meetings' |
||||
END |
||||
FROM meeting_contents |
||||
WHERE journable_type = 'MeetingContent' |
||||
AND journable_id = meeting_contents.id |
||||
SQL |
||||
end |
||||
end |
@ -0,0 +1,15 @@ |
||||
class CreateProjectJournals < ActiveRecord::Migration[7.0] |
||||
# rubocop:disable Rails/CreateTableWithTimestamps(RuboCop) |
||||
def change |
||||
create_table :project_journals do |t| |
||||
t.string :name, null: false |
||||
t.text :description |
||||
t.boolean :public, null: false |
||||
t.bigint :parent_id |
||||
t.string :identifier, null: false |
||||
t.boolean :active, null: false |
||||
t.boolean :templated, null: false |
||||
end |
||||
end |
||||
# rubocop:enable Rails/CreateTableWithTimestamps(RuboCop) |
||||
end |
@ -0,0 +1,16 @@ |
||||
class AddNonNullConstraintOnProjectsIdentifier < ActiveRecord::Migration[7.0] |
||||
def change |
||||
reversible do |dir| |
||||
dir.up do |
||||
# should not be needed as all identifiers should be present |
||||
execute <<~SQL.squish |
||||
UPDATE projects |
||||
SET identifier = 'project-' || id |
||||
WHERE identifier IS NULL |
||||
SQL |
||||
end |
||||
end |
||||
|
||||
change_column_null :projects, :identifier, false |
||||
end |
||||
end |
@ -0,0 +1,97 @@ |
||||
class FillProjectJournalsWithExistingData < ActiveRecord::Migration[7.0] |
||||
def up |
||||
create_journal_entries_for_projects |
||||
end |
||||
|
||||
def down |
||||
delete_journal_entries_for_projects |
||||
end |
||||
|
||||
private |
||||
|
||||
def create_journal_entries_for_projects |
||||
sql = <<~SQL.squish |
||||
WITH project_journals_insertion AS ( |
||||
INSERT INTO project_journals( |
||||
name, |
||||
description, |
||||
public, |
||||
parent_id, |
||||
identifier, |
||||
active, |
||||
templated |
||||
) |
||||
SELECT name, |
||||
description, |
||||
public, |
||||
parent_id, |
||||
identifier, |
||||
active, |
||||
templated |
||||
FROM projects |
||||
RETURNING id, |
||||
identifier |
||||
), |
||||
journals_insertion AS ( |
||||
INSERT into journals ( |
||||
journable_id, |
||||
journable_type, |
||||
user_id, |
||||
created_at, |
||||
updated_at, |
||||
version, |
||||
data_type, |
||||
data_id |
||||
) |
||||
SELECT projects.id, |
||||
'Project', |
||||
:user_id, |
||||
projects.created_at, |
||||
projects.updated_at, |
||||
1, |
||||
'Journal::ProjectJournal', |
||||
project_journals_insertion.id |
||||
FROM projects |
||||
FULL JOIN project_journals_insertion ON projects.identifier = project_journals_insertion.identifier |
||||
RETURNING id, |
||||
journable_id |
||||
), |
||||
customizable_journals_insertion AS ( |
||||
INSERT into customizable_journals ( |
||||
journal_id, |
||||
custom_field_id, |
||||
value |
||||
) |
||||
SELECT journals_insertion.id, |
||||
custom_field_id, |
||||
value |
||||
FROM custom_values |
||||
FULL JOIN journals_insertion ON custom_values.customized_id = journals_insertion.journable_id |
||||
WHERE custom_values.customized_type = 'Project' |
||||
) |
||||
SELECT COUNT(1) |
||||
FROM journals_insertion; |
||||
SQL |
||||
sql = ::ActiveRecord::Base.sanitize_sql_array([sql, { user_id: User.system.id }]) |
||||
execute(sql) |
||||
end |
||||
|
||||
def delete_journal_entries_for_projects |
||||
execute(<<~SQL.squish) |
||||
DELETE |
||||
FROM customizable_journals |
||||
WHERE journal_id IN ( |
||||
SELECT id FROM journals WHERE journable_type = 'Project' |
||||
) |
||||
SQL |
||||
execute(<<~SQL.squish) |
||||
DELETE |
||||
FROM journals |
||||
WHERE journable_type = 'Project' |
||||
SQL |
||||
execute(<<~SQL.squish) |
||||
DELETE |
||||
FROM project_journals |
||||
SQL |
||||
end |
||||
end |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -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. |
||||
#++ |
||||
|
||||
# This patch adds our job status extension to background jobs carried out when mailing with |
||||
# perform_later. |
||||
|
||||
module OpenProject |
||||
module Patches |
||||
module DelayedJobAdapter |
||||
module AllowNonExistingJobClass |
||||
def log_arguments? |
||||
super |
||||
rescue NameError |
||||
false |
||||
end |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
ActiveJob::QueueAdapters::DelayedJobAdapter::JobWrapper.prepend( |
||||
OpenProject::Patches::DelayedJobAdapter::AllowNonExistingJobClass |
||||
) |
@ -0,0 +1,223 @@ |
||||
#-- 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 Project, 'acts_as_journalized' do |
||||
shared_let(:user) { create(:user) } |
||||
|
||||
let!(:project) do |
||||
User.execute_as user do |
||||
create(:project, |
||||
description: 'project description') |
||||
end |
||||
end |
||||
|
||||
context 'on project creation' do |
||||
it 'has one journal entry' do |
||||
expect(Journal.all.count).to eq(1) |
||||
expect(Journal.first.journable).to eq(project) |
||||
end |
||||
|
||||
it 'notes the changes to name' do |
||||
expect(Journal.first.details[:name]) |
||||
.to match_array [nil, project.name] |
||||
end |
||||
|
||||
it 'notes the changes to description' do |
||||
expect(Journal.first.details[:description]) |
||||
.to match_array [nil, project.description] |
||||
end |
||||
|
||||
it 'notes the changes to public flag' do |
||||
expect(Journal.first.details[:public]) |
||||
.to match_array [nil, project.public] |
||||
end |
||||
|
||||
it 'notes the changes to identifier' do |
||||
expect(Journal.first.details[:identifier]) |
||||
.to match_array [nil, project.identifier] |
||||
end |
||||
|
||||
it 'notes the changes to active flag' do |
||||
expect(Journal.first.details[:active]) |
||||
.to match_array [nil, project.active] |
||||
end |
||||
|
||||
it 'notes the changes to template flag' do |
||||
expect(Journal.first.details[:templated]) |
||||
.to match_array [nil, project.templated] |
||||
end |
||||
|
||||
it 'has the timestamp of the project update time for created_at' do |
||||
expect(Journal.first.created_at) |
||||
.to eql(project.reload.updated_at) |
||||
end |
||||
end |
||||
|
||||
context 'when nothing is changed' do |
||||
it { expect { project.save! }.not_to change(Journal, :count) } |
||||
end |
||||
|
||||
describe 'on project update', with_settings: { journal_aggregation_time_minutes: 0 } do |
||||
shared_let(:parent_project) { create(:project) } |
||||
|
||||
before do |
||||
project.name = 'changed project name' |
||||
project.description = 'changed project description' |
||||
project.public = !project.public |
||||
project.parent = parent_project |
||||
project.identifier = 'changed-identifier' |
||||
project.active = !project.active |
||||
project.templated = !project.templated |
||||
|
||||
project.save! |
||||
end |
||||
|
||||
context 'for last created journal' do |
||||
it 'has the timestamp of the project update time for created_at' do |
||||
expect(project.last_journal.created_at) |
||||
.to eql(project.reload.updated_at) |
||||
end |
||||
|
||||
it 'contains last changes' do |
||||
%i[name description public parent_id identifier active templated].each do |prop| |
||||
expect(project.last_journal.details).to have_key(prop.to_s), "Missing change for #{prop}" |
||||
end |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'custom values', with_settings: { journal_aggregation_time_minutes: 0 } do |
||||
shared_let(:custom_field) { create(:string_project_custom_field) } |
||||
let(:custom_value) do |
||||
build(:custom_value, |
||||
value: 'some string value for project custom field', |
||||
custom_field:) |
||||
end |
||||
let(:custom_field_id) { "custom_fields_#{custom_value.custom_field_id}" } |
||||
|
||||
shared_context 'for project with new custom value' do |
||||
before do |
||||
project.update(custom_values: [custom_value]) |
||||
end |
||||
end |
||||
|
||||
context 'for new custom value' do |
||||
include_context 'for project with new custom value' |
||||
|
||||
it 'contains the new custom value change' do |
||||
expect(project.last_journal.details) |
||||
.to include(custom_field_id => [nil, custom_value.value]) |
||||
end |
||||
end |
||||
|
||||
context 'for updated custom value' do |
||||
include_context 'for project with new custom value' |
||||
|
||||
let(:modified_custom_value) do |
||||
build(:custom_value, |
||||
value: 'some modified value for project custom field', |
||||
custom_field:) |
||||
end |
||||
|
||||
before do |
||||
project.update(custom_values: [modified_custom_value]) |
||||
end |
||||
|
||||
it 'contains the change from previous value to updated value' do |
||||
expect(project.last_journal.details) |
||||
.to include(custom_field_id => [custom_value.value, modified_custom_value.value]) |
||||
end |
||||
end |
||||
|
||||
context 'when project saved without any changes' do |
||||
include_context 'for project with new custom value' |
||||
|
||||
let(:unmodified_custom_value) do |
||||
build(:custom_value, |
||||
value: custom_value.value, |
||||
custom_field:) |
||||
end |
||||
|
||||
before do |
||||
project.custom_values = [unmodified_custom_value] |
||||
end |
||||
|
||||
it { expect { project.save! }.not_to change(Journal, :count) } |
||||
end |
||||
|
||||
context 'when custom value removed' do |
||||
include_context 'for project with new custom value' |
||||
|
||||
before do |
||||
project.update(custom_values: []) |
||||
end |
||||
|
||||
it 'contains the change from previous value to nil' do |
||||
expect(project.last_journal.details) |
||||
.to include(custom_field_id => [custom_value.value, nil]) |
||||
end |
||||
end |
||||
end |
||||
|
||||
describe 'on project deletion' do |
||||
shared_let(:custom_field) { create(:string_project_custom_field) } |
||||
let(:custom_value) do |
||||
build(:custom_value, |
||||
value: 'some string value for project custom field', |
||||
custom_field:) |
||||
end |
||||
let!(:project) do |
||||
User.execute_as user do |
||||
create(:project, custom_values: [custom_value]) |
||||
end |
||||
end |
||||
let!(:journal) { project.last_journal } |
||||
let!(:customizable_journals) { journal.customizable_journals } |
||||
|
||||
before do |
||||
project.destroy |
||||
end |
||||
|
||||
it 'removes the journal' do |
||||
expect(Journal.find_by(id: journal.id)) |
||||
.to be_nil |
||||
end |
||||
|
||||
it 'removes the journal data' do |
||||
expect(Journal::ProjectJournal.find_by(id: journal.data_id)) |
||||
.to be_nil |
||||
end |
||||
|
||||
it 'removes the customizable journals' do |
||||
expect(Journal::CustomizableJournal.find_by(id: customizable_journals.map(&:id))) |
||||
.to be_nil |
||||
end |
||||
end |
||||
end |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue