@@ -343,21 +343,28 class IssuesController < ApplicationController | |||||
343 |
|
343 | |||
344 | def destroy |
|
344 | def destroy | |
345 | raise Unauthorized unless @issues.all?(&:deletable?) |
|
345 | raise Unauthorized unless @issues.all?(&:deletable?) | |
346 | @hours = TimeEntry.where(:issue_id => @issues.map(&:id)).sum(:hours).to_f |
|
346 | ||
|
347 | # all issues and their descendants are about to be deleted | |||
|
348 | issues_and_descendants_ids = Issue.self_and_descendants(@issues).pluck(:id) | |||
|
349 | time_entries = TimeEntry.where(:issue_id => issues_and_descendants_ids) | |||
|
350 | @hours = time_entries.sum(:hours).to_f | |||
|
351 | ||||
347 | if @hours > 0 |
|
352 | if @hours > 0 | |
348 | case params[:todo] |
|
353 | case params[:todo] | |
349 | when 'destroy' |
|
354 | when 'destroy' | |
350 | # nothing to do |
|
355 | # nothing to do | |
351 | when 'nullify' |
|
356 | when 'nullify' | |
352 |
|
|
357 | time_entries.update_all(:issue_id => nil) | |
353 | when 'reassign' |
|
358 | when 'reassign' | |
354 | reassign_to = @project.issues.find_by_id(params[:reassign_to_id]) |
|
359 | reassign_to = @project.issues.find_by_id(params[:reassign_to_id]) | |
355 | if reassign_to.nil? |
|
360 | if reassign_to.nil? | |
356 | flash.now[:error] = l(:error_issue_not_found_in_project) |
|
361 | flash.now[:error] = l(:error_issue_not_found_in_project) | |
357 | return |
|
362 | return | |
|
363 | elsif issues_and_descendants_ids.include?(reassign_to.id) | |||
|
364 | flash.now[:error] = l(:error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted) | |||
|
365 | return | |||
358 | else |
|
366 | else | |
359 | TimeEntry.where(['issue_id IN (?)', @issues]). |
|
367 | time_entries.update_all(:issue_id => reassign_to.id) | |
360 | update_all("issue_id = #{reassign_to.id}") |
|
|||
361 | end |
|
368 | end | |
362 | else |
|
369 | else | |
363 | # display the destroy form if it's a user request |
|
370 | # display the destroy form if it's a user request |
@@ -1102,6 +1102,15 class Issue < ActiveRecord::Base | |||||
1102 | end |
|
1102 | end | |
1103 | end |
|
1103 | end | |
1104 |
|
1104 | |||
|
1105 | # Returns a scope of the given issues and their descendants | |||
|
1106 | def self.self_and_descendants(issues) | |||
|
1107 | issue_ids = Issue.joins("JOIN #{Issue.table_name} ancestors" + | |||
|
1108 | " ON ancestors.root_id = #{Issue.table_name}.root_id" + | |||
|
1109 | " AND ancestors.lft <= #{Issue.table_name}.lft AND ancestors.rgt >= #{Issue.table_name}.rgt" | |||
|
1110 | ). | |||
|
1111 | where(:ancestors => {:id => issues.map(&:id)}) | |||
|
1112 | end | |||
|
1113 | ||||
1105 | # Finds an issue relation given its id. |
|
1114 | # Finds an issue relation given its id. | |
1106 | def find_relation(relation_id) |
|
1115 | def find_relation(relation_id) | |
1107 | IssueRelation.where("issue_to_id = ? OR issue_from_id = ?", id, id).find(relation_id) |
|
1116 | IssueRelation.where("issue_to_id = ? OR issue_from_id = ?", id, id).find(relation_id) |
@@ -218,6 +218,7 en: | |||||
218 | error_no_tracker_allowed_for_new_issue_in_project: "The project doesn't have any trackers for which you can create an issue" |
|
218 | error_no_tracker_allowed_for_new_issue_in_project: "The project doesn't have any trackers for which you can create an issue" | |
219 | error_no_projects_with_tracker_allowed_for_new_issue: "There are no projects with trackers for which you can create an issue" |
|
219 | error_no_projects_with_tracker_allowed_for_new_issue: "There are no projects with trackers for which you can create an issue" | |
220 | error_move_of_child_not_possible: "Subtask %{child} could not be moved to the new project: %{errors}" |
|
220 | error_move_of_child_not_possible: "Subtask %{child} could not be moved to the new project: %{errors}" | |
|
221 | error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: "Spent time cannot be reassigned to an issue that is about to be deleted" | |||
221 |
|
222 | |||
222 | mail_subject_lost_password: "Your %{value} password" |
|
223 | mail_subject_lost_password: "Your %{value} password" | |
223 | mail_body_lost_password: 'To change your password, click on the following link:' |
|
224 | mail_body_lost_password: 'To change your password, click on the following link:' |
@@ -238,6 +238,7 fr: | |||||
238 | error_no_tracker_allowed_for_new_issue_in_project: "Le projet ne dispose d'aucun tracker sur lequel vous pouvez crΓ©er une demande" |
|
238 | error_no_tracker_allowed_for_new_issue_in_project: "Le projet ne dispose d'aucun tracker sur lequel vous pouvez crΓ©er une demande" | |
239 | error_no_projects_with_tracker_allowed_for_new_issue: "Aucun projet ne dispose d'un tracker sur lequel vous pouvez crΓ©er une demande" |
|
239 | error_no_projects_with_tracker_allowed_for_new_issue: "Aucun projet ne dispose d'un tracker sur lequel vous pouvez crΓ©er une demande" | |
240 | error_move_of_child_not_possible: "La sous-tΓ’che %{child} n'a pas pu Γͺtre dΓ©placΓ©e dans le nouveau projet : %{errors}" |
|
240 | error_move_of_child_not_possible: "La sous-tΓ’che %{child} n'a pas pu Γͺtre dΓ©placΓ©e dans le nouveau projet : %{errors}" | |
|
241 | error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted: "Le temps passΓ© ne peut pas Γͺtre rΓ©affectΓ© Γ une demande qui va Γͺtre supprimΓ©e" | |||
241 |
|
242 | |||
242 | mail_subject_lost_password: "Votre mot de passe %{value}" |
|
243 | mail_subject_lost_password: "Votre mot de passe %{value}" | |
243 | mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' |
|
244 | mail_body_lost_password: 'Pour changer votre mot de passe, cliquez sur le lien suivant :' |
@@ -4609,7 +4609,7 class IssuesControllerTest < Redmine::ControllerTest | |||||
4609 | assert_response :success |
|
4609 | assert_response :success | |
4610 | end |
|
4610 | end | |
4611 |
|
4611 | |||
4612 | def test_destroy_issue_with_no_time_entries |
|
4612 | def test_destroy_issue_with_no_time_entries_should_delete_the_issues | |
4613 | assert_nil TimeEntry.find_by_issue_id(2) |
|
4613 | assert_nil TimeEntry.find_by_issue_id(2) | |
4614 | @request.session[:user_id] = 2 |
|
4614 | @request.session[:user_id] = 2 | |
4615 |
|
4615 | |||
@@ -4620,7 +4620,7 class IssuesControllerTest < Redmine::ControllerTest | |||||
4620 | assert_nil Issue.find_by_id(2) |
|
4620 | assert_nil Issue.find_by_id(2) | |
4621 | end |
|
4621 | end | |
4622 |
|
4622 | |||
4623 | def test_destroy_issues_with_time_entries |
|
4623 | def test_destroy_issues_with_time_entries_should_show_the_reassign_form | |
4624 | @request.session[:user_id] = 2 |
|
4624 | @request.session[:user_id] = 2 | |
4625 |
|
4625 | |||
4626 | assert_no_difference 'Issue.count' do |
|
4626 | assert_no_difference 'Issue.count' do | |
@@ -4633,6 +4633,20 class IssuesControllerTest < Redmine::ControllerTest | |||||
4633 | end |
|
4633 | end | |
4634 | end |
|
4634 | end | |
4635 |
|
4635 | |||
|
4636 | def test_destroy_issues_with_time_entries_should_show_hours_on_issues_and_descendants | |||
|
4637 | parent = Issue.generate_with_child! | |||
|
4638 | TimeEntry.generate!(:issue => parent) | |||
|
4639 | TimeEntry.generate!(:issue => parent.children.first) | |||
|
4640 | leaf = Issue.generate! | |||
|
4641 | TimeEntry.generate!(:issue => leaf) | |||
|
4642 | @request.session[:user_id] = 2 | |||
|
4643 | ||||
|
4644 | delete :destroy, :ids => [parent.id, leaf.id] | |||
|
4645 | assert_response :success | |||
|
4646 | ||||
|
4647 | assert_select 'p', :text => /3\.00 hours were reported/ | |||
|
4648 | end | |||
|
4649 | ||||
4636 | def test_destroy_issues_and_destroy_time_entries |
|
4650 | def test_destroy_issues_and_destroy_time_entries | |
4637 | @request.session[:user_id] = 2 |
|
4651 | @request.session[:user_id] = 2 | |
4638 |
|
4652 | |||
@@ -4674,6 +4688,24 class IssuesControllerTest < Redmine::ControllerTest | |||||
4674 | assert_equal 2, TimeEntry.find(2).issue_id |
|
4688 | assert_equal 2, TimeEntry.find(2).issue_id | |
4675 | end |
|
4689 | end | |
4676 |
|
4690 | |||
|
4691 | def test_destroy_issues_with_time_entries_should_reassign_time_entries_of_issues_and_descendants | |||
|
4692 | parent = Issue.generate_with_child! | |||
|
4693 | TimeEntry.generate!(:issue => parent) | |||
|
4694 | TimeEntry.generate!(:issue => parent.children.first) | |||
|
4695 | leaf = Issue.generate! | |||
|
4696 | TimeEntry.generate!(:issue => leaf) | |||
|
4697 | target = Issue.generate! | |||
|
4698 | @request.session[:user_id] = 2 | |||
|
4699 | ||||
|
4700 | assert_difference 'Issue.count', -3 do | |||
|
4701 | assert_no_difference 'TimeEntry.count' do | |||
|
4702 | delete :destroy, :ids => [parent.id, leaf.id], :todo => 'reassign', :reassign_to_id => target.id | |||
|
4703 | assert_response 302 | |||
|
4704 | end | |||
|
4705 | end | |||
|
4706 | assert_equal 3, target.time_entries.count | |||
|
4707 | end | |||
|
4708 | ||||
4677 | def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail |
|
4709 | def test_destroy_issues_and_reassign_time_entries_to_an_invalid_issue_should_fail | |
4678 | @request.session[:user_id] = 2 |
|
4710 | @request.session[:user_id] = 2 | |
4679 |
|
4711 | |||
@@ -4686,6 +4718,18 class IssuesControllerTest < Redmine::ControllerTest | |||||
4686 | assert_response :success |
|
4718 | assert_response :success | |
4687 | end |
|
4719 | end | |
4688 |
|
4720 | |||
|
4721 | def test_destroy_issues_and_reassign_time_entries_to_an_issue_to_delete_should_fail | |||
|
4722 | @request.session[:user_id] = 2 | |||
|
4723 | ||||
|
4724 | assert_no_difference 'Issue.count' do | |||
|
4725 | assert_no_difference 'TimeEntry.count' do | |||
|
4726 | delete :destroy, :ids => [1, 3], :todo => 'reassign', :reassign_to_id => 3 | |||
|
4727 | end | |||
|
4728 | end | |||
|
4729 | assert_response :success | |||
|
4730 | assert_select '#flash_error', :text => I18n.t(:error_cannot_reassign_time_entries_to_an_issue_about_to_be_deleted) | |||
|
4731 | end | |||
|
4732 | ||||
4689 | def test_destroy_issues_from_different_projects |
|
4733 | def test_destroy_issues_from_different_projects | |
4690 | @request.session[:user_id] = 2 |
|
4734 | @request.session[:user_id] = 2 | |
4691 |
|
4735 |
General Comments 0
You need to be logged in to leave comments.
Login now