##// END OF EJS Templates
Fixed: Deleting statuses doesn't delete all workflow entries (#5811)....
Jean-Philippe Lang -
r3767:c47d23a87be4
parent child
Show More
@@ -1,92 +1,99
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006 Jean-Philippe Lang
2 # Copyright (C) 2006 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class IssueStatus < ActiveRecord::Base
18 class IssueStatus < ActiveRecord::Base
19 before_destroy :check_integrity
19 before_destroy :check_integrity
20 has_many :workflows, :foreign_key => "old_status_id", :dependent => :delete_all
20 has_many :workflows, :foreign_key => "old_status_id"
21 acts_as_list
21 acts_as_list
22
22
23 before_destroy :delete_workflows
24
23 validates_presence_of :name
25 validates_presence_of :name
24 validates_uniqueness_of :name
26 validates_uniqueness_of :name
25 validates_length_of :name, :maximum => 30
27 validates_length_of :name, :maximum => 30
26 validates_format_of :name, :with => /^[\w\s\'\-]*$/i
28 validates_format_of :name, :with => /^[\w\s\'\-]*$/i
27 validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
29 validates_inclusion_of :default_done_ratio, :in => 0..100, :allow_nil => true
28
30
29 def after_save
31 def after_save
30 IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
32 IssueStatus.update_all("is_default=#{connection.quoted_false}", ['id <> ?', id]) if self.is_default?
31 end
33 end
32
34
33 # Returns the default status for new issues
35 # Returns the default status for new issues
34 def self.default
36 def self.default
35 find(:first, :conditions =>["is_default=?", true])
37 find(:first, :conditions =>["is_default=?", true])
36 end
38 end
37
39
38 # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+
40 # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+
39 def self.update_issue_done_ratios
41 def self.update_issue_done_ratios
40 if Issue.use_status_for_done_ratio?
42 if Issue.use_status_for_done_ratio?
41 IssueStatus.find(:all, :conditions => ["default_done_ratio >= 0"]).each do |status|
43 IssueStatus.find(:all, :conditions => ["default_done_ratio >= 0"]).each do |status|
42 Issue.update_all(["done_ratio = ?", status.default_done_ratio],
44 Issue.update_all(["done_ratio = ?", status.default_done_ratio],
43 ["status_id = ?", status.id])
45 ["status_id = ?", status.id])
44 end
46 end
45 end
47 end
46
48
47 return Issue.use_status_for_done_ratio?
49 return Issue.use_status_for_done_ratio?
48 end
50 end
49
51
50 # Returns an array of all statuses the given role can switch to
52 # Returns an array of all statuses the given role can switch to
51 # Uses association cache when called more than one time
53 # Uses association cache when called more than one time
52 def new_statuses_allowed_to(roles, tracker)
54 def new_statuses_allowed_to(roles, tracker)
53 if roles && tracker
55 if roles && tracker
54 role_ids = roles.collect(&:id)
56 role_ids = roles.collect(&:id)
55 new_statuses = workflows.select {|w| role_ids.include?(w.role_id) && w.tracker_id == tracker.id}.collect{|w| w.new_status}.compact.sort
57 new_statuses = workflows.select {|w| role_ids.include?(w.role_id) && w.tracker_id == tracker.id}.collect{|w| w.new_status}.compact.sort
56 else
58 else
57 []
59 []
58 end
60 end
59 end
61 end
60
62
61 # Same thing as above but uses a database query
63 # Same thing as above but uses a database query
62 # More efficient than the previous method if called just once
64 # More efficient than the previous method if called just once
63 def find_new_statuses_allowed_to(roles, tracker)
65 def find_new_statuses_allowed_to(roles, tracker)
64 if roles && tracker
66 if roles && tracker
65 workflows.find(:all,
67 workflows.find(:all,
66 :include => :new_status,
68 :include => :new_status,
67 :conditions => { :role_id => roles.collect(&:id),
69 :conditions => { :role_id => roles.collect(&:id),
68 :tracker_id => tracker.id}).collect{ |w| w.new_status }.compact.sort
70 :tracker_id => tracker.id}).collect{ |w| w.new_status }.compact.sort
69 else
71 else
70 []
72 []
71 end
73 end
72 end
74 end
73
75
74 def new_status_allowed_to?(status, roles, tracker)
76 def new_status_allowed_to?(status, roles, tracker)
75 if status && roles && tracker
77 if status && roles && tracker
76 !workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => roles.collect(&:id), :tracker_id => tracker.id}).nil?
78 !workflows.find(:first, :conditions => {:new_status_id => status.id, :role_id => roles.collect(&:id), :tracker_id => tracker.id}).nil?
77 else
79 else
78 false
80 false
79 end
81 end
80 end
82 end
81
83
82 def <=>(status)
84 def <=>(status)
83 position <=> status.position
85 position <=> status.position
84 end
86 end
85
87
86 def to_s; name end
88 def to_s; name end
87
89
88 private
90 private
89 def check_integrity
91 def check_integrity
90 raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id])
92 raise "Can't delete status" if Issue.find(:first, :conditions => ["status_id=?", self.id])
91 end
93 end
94
95 # Deletes associated workflows
96 def delete_workflows
97 Workflow.delete_all(["old_status_id = :id OR new_status_id = :id", {:id => id}])
98 end
92 end
99 end
@@ -1,105 +1,107
1 # redMine - project management software
1 # redMine - project management software
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
2 # Copyright (C) 2006-2007 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.dirname(__FILE__) + '/../test_helper'
18 require File.dirname(__FILE__) + '/../test_helper'
19
19
20 class IssueStatusTest < ActiveSupport::TestCase
20 class IssueStatusTest < ActiveSupport::TestCase
21 fixtures :issue_statuses, :issues
21 fixtures :issue_statuses, :issues
22
22
23 def test_create
23 def test_create
24 status = IssueStatus.new :name => "Assigned"
24 status = IssueStatus.new :name => "Assigned"
25 assert !status.save
25 assert !status.save
26 # status name uniqueness
26 # status name uniqueness
27 assert_equal 1, status.errors.count
27 assert_equal 1, status.errors.count
28
28
29 status.name = "Test Status"
29 status.name = "Test Status"
30 assert status.save
30 assert status.save
31 assert !status.is_default
31 assert !status.is_default
32 end
32 end
33
33
34 def test_destroy
34 def test_destroy
35 count_before = IssueStatus.count
36 status = IssueStatus.find(3)
35 status = IssueStatus.find(3)
36 assert_difference 'IssueStatus.count', -1 do
37 assert status.destroy
37 assert status.destroy
38 assert_equal count_before - 1, IssueStatus.count
38 end
39 assert_nil Workflow.first(:conditions => {:old_status_id => status.id})
40 assert_nil Workflow.first(:conditions => {:new_status_id => status.id})
39 end
41 end
40
42
41 def test_destroy_status_in_use
43 def test_destroy_status_in_use
42 # Status assigned to an Issue
44 # Status assigned to an Issue
43 status = Issue.find(1).status
45 status = Issue.find(1).status
44 assert_raise(RuntimeError, "Can't delete status") { status.destroy }
46 assert_raise(RuntimeError, "Can't delete status") { status.destroy }
45 end
47 end
46
48
47 def test_default
49 def test_default
48 status = IssueStatus.default
50 status = IssueStatus.default
49 assert_kind_of IssueStatus, status
51 assert_kind_of IssueStatus, status
50 end
52 end
51
53
52 def test_change_default
54 def test_change_default
53 status = IssueStatus.find(2)
55 status = IssueStatus.find(2)
54 assert !status.is_default
56 assert !status.is_default
55 status.is_default = true
57 status.is_default = true
56 assert status.save
58 assert status.save
57 status.reload
59 status.reload
58
60
59 assert_equal status, IssueStatus.default
61 assert_equal status, IssueStatus.default
60 assert !IssueStatus.find(1).is_default
62 assert !IssueStatus.find(1).is_default
61 end
63 end
62
64
63 def test_reorder_should_not_clear_default_status
65 def test_reorder_should_not_clear_default_status
64 status = IssueStatus.default
66 status = IssueStatus.default
65 status.move_to_bottom
67 status.move_to_bottom
66 status.reload
68 status.reload
67 assert status.is_default?
69 assert status.is_default?
68 end
70 end
69
71
70 context "#update_done_ratios" do
72 context "#update_done_ratios" do
71 setup do
73 setup do
72 @issue = Issue.find(1)
74 @issue = Issue.find(1)
73 @issue_status = IssueStatus.find(1)
75 @issue_status = IssueStatus.find(1)
74 @issue_status.update_attribute(:default_done_ratio, 50)
76 @issue_status.update_attribute(:default_done_ratio, 50)
75 end
77 end
76
78
77 context "with Setting.issue_done_ratio using the issue_field" do
79 context "with Setting.issue_done_ratio using the issue_field" do
78 setup do
80 setup do
79 Setting.issue_done_ratio = 'issue_field'
81 Setting.issue_done_ratio = 'issue_field'
80 end
82 end
81
83
82 should "change nothing" do
84 should "change nothing" do
83 IssueStatus.update_issue_done_ratios
85 IssueStatus.update_issue_done_ratios
84
86
85 assert_equal 0, Issue.count(:conditions => {:done_ratio => 50})
87 assert_equal 0, Issue.count(:conditions => {:done_ratio => 50})
86 end
88 end
87 end
89 end
88
90
89 context "with Setting.issue_done_ratio using the issue_status" do
91 context "with Setting.issue_done_ratio using the issue_status" do
90 setup do
92 setup do
91 Setting.issue_done_ratio = 'issue_status'
93 Setting.issue_done_ratio = 'issue_status'
92 end
94 end
93
95
94 should "update all of the issue's done_ratios to match their Issue Status" do
96 should "update all of the issue's done_ratios to match their Issue Status" do
95 IssueStatus.update_issue_done_ratios
97 IssueStatus.update_issue_done_ratios
96
98
97 issues = Issue.find([1,3,4,5,6,7,9,10])
99 issues = Issue.find([1,3,4,5,6,7,9,10])
98 issues.each do |issue|
100 issues.each do |issue|
99 assert_equal @issue_status, issue.status
101 assert_equal @issue_status, issue.status
100 assert_equal 50, issue.read_attribute(:done_ratio)
102 assert_equal 50, issue.read_attribute(:done_ratio)
101 end
103 end
102 end
104 end
103 end
105 end
104 end
106 end
105 end
107 end
General Comments 0
You need to be logged in to leave comments. Login now