-
Notifications
You must be signed in to change notification settings - Fork 3.4k
[AGILE-318] Assign copied work packages to copied sprints #24129
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
dombesz
merged 24 commits into
release/17.6
from
bug/agile-318-work-packages-wrong-assigned-sprints
Jul 22, 2026
Merged
Changes from 16 commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
b9c4c08
[AGILE-318] Copy sprints when copying a project
myabc 5b4c7cf
[AGILE-318] Remap copied WP sprint assignment
myabc e860036
Populate sprint lookup incrementally on copy
myabc f3d85ba
Ignore order in .copyable_dependencies expectation
myabc 1a75b1a
Add feature spec for copied-sprint reassignment
myabc c79858c
Copy work package positions too.
dombesz c3618d5
Sort copyable_dependencies in expectation
myabc d7e352f
Copy backlog buckets when copying a project
myabc ae1dee6
Prepend copy dependencies instead of index insert
myabc 02dac87
Rename copy spec and assert copied buckets
myabc 9357f44
Move copy-wait helper to general settings page
myabc 4225f3b
Copy sprints only when the source project has its own sprints, otherw…
dombesz fab189a
Dedup backlog copy into a tolerant id-map helper
myabc a1f65c0
Scope backlogs WP copy overrides to backlogs projects
myabc f62f348
Reuse the copy id-map helper for version copying
myabc 6f51e63
Copy owned sprint goals when copying a project
myabc 74ad337
Decide sprint copy per sprint, not per project
myabc 49faa14
Surface records skipped while copying a project
myabc d5a4aa7
Remap sprint and bucket query filters on copy
myabc 031fc1a
Resolve copied work-package sprints in one query
myabc 88de819
Copy categories through the shared id-map helper
myabc de96fb8
Keep borrowed sprint links on copied work packages
myabc 9825d65
Copy sprint goal through goal_text_for lookup
myabc 8638dd6
Scope copy specs' source project per describe
myabc File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
47 changes: 47 additions & 0 deletions
47
modules/backlogs/app/services/projects/copy/backlog_buckets_dependent_service.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) 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::Copy | ||
| class BacklogBucketsDependentService < Dependency | ||
| def self.human_name | ||
| I18n.t("projects.copy.backlog_buckets") | ||
| end | ||
|
|
||
| def source_count | ||
| source.backlog_buckets.count | ||
| end | ||
|
|
||
| protected | ||
|
|
||
| def copy_dependency(*) | ||
| state.backlog_bucket_id_lookup = copy_collection_with_id_map(:backlog_buckets) | ||
| end | ||
| end | ||
| end | ||
69 changes: 69 additions & 0 deletions
69
modules/backlogs/app/services/projects/copy/sprints_dependent_service.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) 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::Copy | ||
| class SprintsDependentService < Dependency | ||
| def self.human_name | ||
| I18n.t("projects.copy.sprints") | ||
| end | ||
|
|
||
| def source_count | ||
| source.sprints.count | ||
| end | ||
|
|
||
| protected | ||
|
|
||
| def copy_dependency(*) | ||
| if source.receive_shared_sprints? | ||
| preserve_sprint_assignments | ||
|
myabc marked this conversation as resolved.
Outdated
|
||
| else | ||
| copy_sprints | ||
| end | ||
| end | ||
|
myabc marked this conversation as resolved.
|
||
|
|
||
| def preserve_sprint_assignments | ||
| state.sprint_id_lookup = Sprint.for_project(source).pluck(:id).index_with { |id| id } | ||
| end | ||
|
|
||
| def copy_sprints | ||
| state.sprint_id_lookup = copy_collection_with_id_map(:sprints) do |source_sprint| | ||
| { goals_attributes: copied_goal_attributes(source_sprint) } | ||
| end | ||
| end | ||
|
|
||
| # Only the source project's own goals are carried over; a shared sprint may | ||
| # also hold goals owned by other projects, which are not ours to copy. | ||
| def copied_goal_attributes(source_sprint) | ||
| source_sprint.goals.where(project_id: source.id).map do |goal| | ||
| { text: goal.text, project_id: target.id } | ||
| end | ||
| end | ||
| end | ||
| end | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
69 changes: 69 additions & 0 deletions
69
modules/backlogs/lib/open_project/backlogs/patches/work_packages_dependent_service_patch.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) 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 OpenProject::Backlogs::Patches::WorkPackagesDependentServicePatch | ||
| extend ActiveSupport::Concern | ||
|
|
||
| included do | ||
| prepend InstanceMethods | ||
| end | ||
|
|
||
| module InstanceMethods | ||
| def copy_work_package(source_work_package, parent_id, user_cf_ids) | ||
| return super unless source.backlogs_enabled? | ||
|
myabc marked this conversation as resolved.
|
||
|
|
||
| # Disable the acts_as_list callbacks so the position is carried over from | ||
| # the source work package unchanged instead of being reappended. | ||
| WorkPackage.acts_as_list_no_update { super } | ||
| end | ||
|
|
||
| def copy_work_package_attribute_overrides(source_work_package, parent_id, user_cf_ids) | ||
| return super unless source.backlogs_enabled? | ||
|
|
||
| super.merge( | ||
| sprint_id: work_package_sprint_id(source_work_package), | ||
| backlog_bucket_id: work_package_backlog_bucket_id(source_work_package), | ||
| position: source_work_package.position | ||
| ) | ||
| end | ||
|
dombesz marked this conversation as resolved.
myabc marked this conversation as resolved.
|
||
|
|
||
| def work_package_sprint_id(source_work_package) | ||
| return unless source_work_package.sprint_id | ||
|
|
||
| state.sprint_id_lookup&.[](source_work_package.sprint_id) | ||
| end | ||
|
|
||
| def work_package_backlog_bucket_id(source_work_package) | ||
| return unless source_work_package.backlog_bucket_id | ||
|
|
||
| state.backlog_bucket_id_lookup&.[](source_work_package.backlog_bucket_id) | ||
| end | ||
| end | ||
| end | ||
91 changes: 91 additions & 0 deletions
91
modules/backlogs/spec/features/projects/copy_backlogs_spec.rb
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| # frozen_string_literal: true | ||
|
|
||
| #-- copyright | ||
| # OpenProject is an open source project management software. | ||
| # Copyright (C) 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" | ||
|
|
||
| RSpec.describe "Project copy with sprints and buckets", :js, | ||
| with_good_job_batches: [CopyProjectJob, | ||
| Storages::CopyProjectFoldersJob, | ||
| SendCopyProjectStatusEmailJob] do | ||
| shared_let(:admin) { create(:admin) } | ||
| shared_let(:type) { create(:type) } | ||
| shared_let(:project) do | ||
| create(:project, | ||
| enabled_module_names: %w[work_package_tracking backlogs], | ||
| types: [type]) | ||
| end | ||
| shared_let(:sprint) { create(:sprint, project:, name: "Sprint A") } | ||
| shared_let(:bucket) { create(:backlog_bucket, project:, name: "Bucket A") } | ||
| shared_let(:work_package) do | ||
| create(:work_package, project:, type:, subject: "Sprint story", sprint:) | ||
| end | ||
| shared_let(:bucket_work_package) do | ||
| create(:work_package, project:, type:, subject: "Bucket story", backlog_bucket: bucket) | ||
| end | ||
|
|
||
| let(:general_settings_page) { Pages::Projects::Settings::General.new(project) } | ||
|
|
||
| before do | ||
| # Clear jobs enqueued during object creation so they don't interfere with the copy. | ||
| clear_enqueued_jobs | ||
| clear_performed_jobs | ||
|
|
||
| login_as admin | ||
| end | ||
|
|
||
| it "assigns copied work packages to the copied sprint and bucket, not the source ones" do | ||
| general_settings_page.visit! | ||
| general_settings_page.click_copy_action | ||
|
|
||
| expect(page).to have_heading "Copy project \"#{project.name}\"" | ||
|
|
||
| fill_in "Name", with: "Copied project" | ||
| click_on "Copy" | ||
|
|
||
| general_settings_page.wait_for_copy_to_finish | ||
|
|
||
| copied_project = Project.find_by(name: "Copied project") | ||
| expect(copied_project).to be_present | ||
|
|
||
| copied_sprint = copied_project.sprints.find_by(name: "Sprint A") | ||
| expect(copied_sprint).to be_present | ||
| expect(copied_sprint.id).not_to eq(sprint.id) | ||
|
|
||
| copied_work_package = copied_project.work_packages.find_by(subject: "Sprint story") | ||
| expect(copied_work_package.sprint).to eq(copied_sprint) | ||
|
|
||
| copied_bucket = copied_project.backlog_buckets.find_by(name: "Bucket A") | ||
| expect(copied_bucket).to be_present | ||
| expect(copied_bucket.id).not_to eq(bucket.id) | ||
|
|
||
| copied_bucket_work_package = copied_project.work_packages.find_by(subject: "Bucket story") | ||
| expect(copied_bucket_work_package.backlog_bucket).to eq(copied_bucket) | ||
| end | ||
| end |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.