##// END OF EJS Templates
Adds ability to bulk copy issues (#1847)....
Jean-Philippe Lang -
r2311:1ad255155949
parent child
Show More
@@ -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 self.relations_from.clear
83 issue.relations_from.clear
81 self.relations_to.clear
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 self.category = new_category
89 issue.category = new_category
87 self.fixed_version = nil
90 issue.fixed_version = nil
88 self.project = new_project
91 issue.project = new_project
89 end
92 end
90 if new_tracker
93 if new_tracker
91 self.tracker = new_tracker
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 true
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