@@ -283,7 +283,7 class IssuesController < ApplicationController | |||||
283 | unsaved_issue_ids = [] |
|
283 | unsaved_issue_ids = [] | |
284 | @issues.each do |issue| |
|
284 | @issues.each do |issue| | |
285 | issue.init_journal(User.current) |
|
285 | issue.init_journal(User.current) | |
286 | unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker) |
|
286 | unsaved_issue_ids << issue.id unless issue.move_to(@target_project, new_tracker, params[:copy_options]) | |
287 | end |
|
287 | end | |
288 | if unsaved_issue_ids.empty? |
|
288 | if unsaved_issue_ids.empty? | |
289 | flash[:notice] = l(:notice_successful_update) unless @issues.empty? |
|
289 | flash[:notice] = l(:notice_successful_update) unless @issues.empty? |
@@ -71,34 +71,43 class Issue < ActiveRecord::Base | |||||
71 | self |
|
71 | self | |
72 | end |
|
72 | end | |
73 |
|
73 | |||
74 | # Move an issue to a new project and tracker |
|
74 | # Moves/copies an issue to a new project and tracker | |
75 | def move_to(new_project, new_tracker = nil) |
|
75 | # Returns the moved/copied issue on success, false on failure | |
|
76 | def move_to(new_project, new_tracker = nil, options = {}) | |||
|
77 | options ||= {} | |||
|
78 | issue = options[:copy] ? self.clone : self | |||
76 | transaction do |
|
79 | transaction do | |
77 | if new_project && project_id != new_project.id |
|
80 | if new_project && issue.project_id != new_project.id | |
78 | # delete issue relations |
|
81 | # delete issue relations | |
79 | unless Setting.cross_project_issue_relations? |
|
82 | unless Setting.cross_project_issue_relations? | |
80 |
se |
|
83 | issue.relations_from.clear | |
81 |
se |
|
84 | issue.relations_to.clear | |
82 | end |
|
85 | end | |
83 | # issue is moved to another project |
|
86 | # issue is moved to another project | |
84 | # reassign to the category with same name if any |
|
87 | # reassign to the category with same name if any | |
85 | new_category = category.nil? ? nil : new_project.issue_categories.find_by_name(category.name) |
|
88 | new_category = issue.category.nil? ? nil : new_project.issue_categories.find_by_name(issue.category.name) | |
86 |
se |
|
89 | issue.category = new_category | |
87 |
se |
|
90 | issue.fixed_version = nil | |
88 |
se |
|
91 | issue.project = new_project | |
89 | end |
|
92 | end | |
90 | if new_tracker |
|
93 | if new_tracker | |
91 |
se |
|
94 | issue.tracker = new_tracker | |
92 | end |
|
95 | end | |
93 | if save |
|
96 | if options[:copy] | |
94 | # Manually update project_id on related time entries |
|
97 | issue.custom_field_values = self.custom_field_values.inject({}) {|h,v| h[v.custom_field_id] = v.value; h} | |
95 | TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id}) |
|
98 | issue.status = self.status | |
|
99 | end | |||
|
100 | if issue.save | |||
|
101 | unless options[:copy] | |||
|
102 | # Manually update project_id on related time entries | |||
|
103 | TimeEntry.update_all("project_id = #{new_project.id}", {:issue_id => id}) | |||
|
104 | end | |||
96 | else |
|
105 | else | |
97 | rollback_db_transaction |
|
106 | Issue.connection.rollback_db_transaction | |
98 | return false |
|
107 | return false | |
99 | end |
|
108 | end | |
100 | end |
|
109 | end | |
101 |
return |
|
110 | return issue | |
102 | end |
|
111 | end | |
103 |
|
112 | |||
104 | def priority_id=(pid) |
|
113 | def priority_id=(pid) |
@@ -16,6 +16,9 | |||||
16 |
|
16 | |||
17 | <p><label for="new_tracker_id"><%=l(:field_tracker)%> :</label> |
|
17 | <p><label for="new_tracker_id"><%=l(:field_tracker)%> :</label> | |
18 | <%= select_tag "new_tracker_id", "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, "id", "name") %></p> |
|
18 | <%= select_tag "new_tracker_id", "<option value=\"\">#{l(:label_no_change_option)}</option>" + options_from_collection_for_select(@trackers, "id", "name") %></p> | |
|
19 | ||||
|
20 | <p><label for="copy_options_copy"><%= l(:button_copy)%></label> | |||
|
21 | <%= check_box_tag "copy_options[copy]", "1" %></p> | |||
19 | </div> |
|
22 | </div> | |
20 |
|
23 | |||
21 | <%= submit_tag l(:button_move) %> |
|
24 | <%= submit_tag l(:button_move) %> |
@@ -672,6 +672,16 class IssuesControllerTest < Test::Unit::TestCase | |||||
672 | assert_equal 2, Issue.find(1).tracker_id |
|
672 | assert_equal 2, Issue.find(1).tracker_id | |
673 | assert_equal 2, Issue.find(2).tracker_id |
|
673 | assert_equal 2, Issue.find(2).tracker_id | |
674 | end |
|
674 | end | |
|
675 | ||||
|
676 | def test_bulk_copy_to_another_project | |||
|
677 | @request.session[:user_id] = 1 | |||
|
678 | assert_difference 'Issue.count', 2 do | |||
|
679 | assert_no_difference 'Project.find(1).issues.count' do | |||
|
680 | post :move, :ids => [1, 2], :new_project_id => 2, :copy_options => {:copy => '1'} | |||
|
681 | end | |||
|
682 | end | |||
|
683 | assert_redirected_to 'projects/ecookbook/issues' | |||
|
684 | end | |||
675 |
|
685 | |||
676 | def test_context_menu_one_issue |
|
686 | def test_context_menu_one_issue | |
677 | @request.session[:user_id] = 2 |
|
687 | @request.session[:user_id] = 2 |
@@ -191,6 +191,30 class IssueTest < Test::Unit::TestCase | |||||
191 | assert_nil issue.category_id |
|
191 | assert_nil issue.category_id | |
192 | end |
|
192 | end | |
193 |
|
193 | |||
|
194 | def test_copy_to_the_same_project | |||
|
195 | issue = Issue.find(1) | |||
|
196 | copy = nil | |||
|
197 | assert_difference 'Issue.count' do | |||
|
198 | copy = issue.move_to(issue.project, nil, :copy => true) | |||
|
199 | end | |||
|
200 | assert_kind_of Issue, copy | |||
|
201 | assert_equal issue.project, copy.project | |||
|
202 | assert_equal "125", copy.custom_value_for(2).value | |||
|
203 | end | |||
|
204 | ||||
|
205 | def test_copy_to_another_project_and_tracker | |||
|
206 | issue = Issue.find(1) | |||
|
207 | copy = nil | |||
|
208 | assert_difference 'Issue.count' do | |||
|
209 | copy = issue.move_to(Project.find(3), Tracker.find(2), :copy => true) | |||
|
210 | end | |||
|
211 | assert_kind_of Issue, copy | |||
|
212 | assert_equal Project.find(3), copy.project | |||
|
213 | assert_equal Tracker.find(2), copy.tracker | |||
|
214 | # Custom field #2 is not associated with target tracker | |||
|
215 | assert_nil copy.custom_value_for(2) | |||
|
216 | end | |||
|
217 | ||||
194 | def test_issue_destroy |
|
218 | def test_issue_destroy | |
195 | Issue.find(1).destroy |
|
219 | Issue.find(1).destroy | |
196 | assert_nil Issue.find_by_id(1) |
|
220 | assert_nil Issue.find_by_id(1) |
General Comments 0
You need to be logged in to leave comments.
Login now