diff --git a/app/controllers/issue_statuses_controller.rb b/app/controllers/issue_statuses_controller.rb
index bee7f4833..3be6abf3f 100644
--- a/app/controllers/issue_statuses_controller.rb
+++ b/app/controllers/issue_statuses_controller.rb
@@ -18,7 +18,7 @@
class IssueStatusesController < ApplicationController
before_filter :require_admin
- verify :method => :post, :only => [ :destroy, :create, :update, :move ],
+ verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ],
:redirect_to => { :action => :list }
def index
@@ -66,4 +66,13 @@ class IssueStatusesController < ApplicationController
flash[:error] = "Unable to delete issue status"
redirect_to :action => 'list'
end
+
+ def update_issue_done_ratio
+ if IssueStatus.update_issue_done_ratios
+ flash[:notice] = l(:notice_issue_done_ratios_updated)
+ else
+ flash[:error] = l(:error_issue_done_ratios_not_updated)
+ end
+ redirect_to :action => 'list'
+ end
end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index a0f47fdb8..2062e58e8 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -45,6 +45,8 @@ class Issue < ActiveRecord::Base
acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]},
:author_key => :author_id
+
+ DONE_RATIO_OPTIONS = %w(issue_field issue_status)
validates_presence_of :subject, :priority, :project, :tracker, :author, :status
validates_length_of :subject, :maximum => 255
@@ -55,7 +57,8 @@ class Issue < ActiveRecord::Base
:conditions => Project.allowed_to_condition(args.first || User.current, :view_issues) } }
named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status
-
+
+ before_save :update_done_ratio_from_issue_status
after_save :create_journal
# Returns true if usr or current user is allowed to view the issue
@@ -162,6 +165,22 @@ class Issue < ActiveRecord::Base
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h)
end
+ def done_ratio
+ if Issue.use_status_for_done_ratio? && !self.status.default_done_ratio.blank?
+ self.status.default_done_ratio
+ else
+ read_attribute(:done_ratio)
+ end
+ end
+
+ def self.use_status_for_done_ratio?
+ Setting.issue_done_ratio == 'issue_status'
+ end
+
+ def self.use_field_for_done_ratio?
+ Setting.issue_done_ratio == 'issue_field'
+ end
+
def validate
if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty?
errors.add :due_date, :not_a_date
@@ -198,6 +217,14 @@ class Issue < ActiveRecord::Base
end
end
+ # Set the done_ratio using the status if that setting is set. This will keep the done_ratios
+ # even if the user turns off the setting later
+ def update_done_ratio_from_issue_status
+ if Issue.use_status_for_done_ratio? && !self.status.default_done_ratio.blank?
+ self.done_ratio = self.status.default_done_ratio
+ end
+ end
+
def after_save
# Reload is needed in order to get the right status
reload
diff --git a/app/models/issue_status.rb b/app/models/issue_status.rb
index ca33d37d6..a9c1db584 100644
--- a/app/models/issue_status.rb
+++ b/app/models/issue_status.rb
@@ -33,6 +33,18 @@ class IssueStatus < ActiveRecord::Base
def self.default
find(:first, :conditions =>["is_default=?", true])
end
+
+ # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+
+ def self.update_issue_done_ratios
+ if Issue.use_status_for_done_ratio?
+ IssueStatus.find(:all, :conditions => ["default_done_ratio >= 0"]).each do |status|
+ Issue.update_all(["done_ratio = ?", status.default_done_ratio],
+ ["status_id = ?", status.id])
+ end
+ end
+
+ return Issue.use_status_for_done_ratio?
+ end
# Returns an array of all statuses the given role can switch to
# Uses association cache when called more than one time
diff --git a/app/views/issue_statuses/_form.rhtml b/app/views/issue_statuses/_form.rhtml
index b6a5bc19f..e36dec824 100644
--- a/app/views/issue_statuses/_form.rhtml
+++ b/app/views/issue_statuses/_form.rhtml
@@ -5,6 +5,11 @@
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 184788eee..6a439a337 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -147,6 +147,7 @@ en:
notice_account_pending: "Your account was created and is now pending administrator approval."
notice_default_data_loaded: Default configuration successfully loaded.
notice_unable_delete_version: Unable to delete version.
+ notice_issue_done_ratios_updated: Issue done ratios updated.
error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}"
error_scm_not_found: "The entry or revision was not found in the repository."
@@ -157,7 +158,8 @@ en:
error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").'
error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened'
error_can_not_archive_project: This project can not be archived
-
+ error_issue_done_ratios_not_updated: "Issue done ratios not updated."
+
warning_attachments_not_saved: "{{count}} file(s) could not be saved."
mail_subject_lost_password: "Your {{value}} password"
@@ -309,6 +311,7 @@ en:
setting_sequential_project_identifiers: Generate sequential project identifiers
setting_gravatar_enabled: Use Gravatar user icons
setting_gravatar_default: Default Gravatar image
+ setting_issue_done_ratio: Calculate the issue done ratio with
setting_diff_max_lines_displayed: Max number of diff lines displayed
setting_file_max_size_displayed: Max size of text files displayed inline
setting_repository_log_display_limit: Maximum number of revisions displayed on file log
@@ -716,6 +719,7 @@ en:
label_version_sharing_hierarchy: With project hierarchy
label_version_sharing_tree: With project tree
label_version_sharing_system: With all projects
+ label_update_issue_done_ratios: Update issue done ratios
button_login: Login
button_submit: Submit
@@ -850,3 +854,6 @@ en:
enumeration_doc_categories: Document categories
enumeration_activities: Activities (time tracking)
enumeration_system_activity: System Activity
+
+ issue_field: Use the issue field
+ issue_status: Use the issue status
diff --git a/config/settings.yml b/config/settings.yml
index ca0cc3d7b..754c0248a 100644
--- a/config/settings.yml
+++ b/config/settings.yml
@@ -129,6 +129,8 @@ issue_list_default_columns:
- updated_on
display_subprojects_issues:
default: 1
+issue_done_ratio:
+ default: 'issue_field'
default_projects_public:
default: 1
default_projects_modules:
diff --git a/db/migrate/20091123212029_add_default_done_ratio_to_issue_status.rb b/db/migrate/20091123212029_add_default_done_ratio_to_issue_status.rb
new file mode 100644
index 000000000..0ce672100
--- /dev/null
+++ b/db/migrate/20091123212029_add_default_done_ratio_to_issue_status.rb
@@ -0,0 +1,9 @@
+class AddDefaultDoneRatioToIssueStatus < ActiveRecord::Migration
+ def self.up
+ add_column :issue_statuses, :default_done_ratio, :integer
+ end
+
+ def self.down
+ remove_column :issue_statuses, :default_done_ratio
+ end
+end
diff --git a/public/images/table_multiple.png b/public/images/table_multiple.png
new file mode 100644
index 000000000..d76448e34
Binary files /dev/null and b/public/images/table_multiple.png differ
diff --git a/public/stylesheets/application.css b/public/stylesheets/application.css
index afbee7638..3d0c3b028 100644
--- a/public/stylesheets/application.css
+++ b/public/stylesheets/application.css
@@ -702,6 +702,7 @@ vertical-align: middle;
.icon-move { background-image: url(../images/move.png); }
.icon-save { background-image: url(../images/save.png); }
.icon-cancel { background-image: url(../images/cancel.png); }
+.icon-multiple { background-image: url(../images/table_multiple.png); }
.icon-folder { background-image: url(../images/folder.png); }
.open .icon-folder { background-image: url(../images/folder_open.png); }
.icon-package { background-image: url(../images/package.png); }
diff --git a/test/functional/issue_statuses_controller_test.rb b/test/functional/issue_statuses_controller_test.rb
index 5e012f7f4..4f88433bd 100644
--- a/test/functional/issue_statuses_controller_test.rb
+++ b/test/functional/issue_statuses_controller_test.rb
@@ -70,4 +70,27 @@ class IssueStatusesControllerTest < ActionController::TestCase
assert_redirected_to 'issue_statuses/list'
assert_not_nil IssueStatus.find_by_id(1)
end
+
+ context "on POST to :update_issue_done_ratio" do
+ context "with Setting.issue_done_ratio using the issue_field" do
+ setup do
+ Setting.issue_done_ratio = 'issue_field'
+ post :update_issue_done_ratio
+ end
+
+ should_set_the_flash_to /not updated/
+ should_redirect_to('the list') { '/issue_statuses/list' }
+ end
+
+ context "with Setting.issue_done_ratio using the issue_status" do
+ setup do
+ Setting.issue_done_ratio = 'issue_status'
+ post :update_issue_done_ratio
+ end
+
+ should_set_the_flash_to /Issue done ratios updated/
+ should_redirect_to('the list') { '/issue_statuses/list' }
+ end
+ end
+
end
diff --git a/test/unit/issue_status_test.rb b/test/unit/issue_status_test.rb
index 042f30e32..2c0685cec 100644
--- a/test/unit/issue_status_test.rb
+++ b/test/unit/issue_status_test.rb
@@ -66,4 +66,40 @@ class IssueStatusTest < ActiveSupport::TestCase
status.reload
assert status.is_default?
end
+
+ context "#update_done_ratios" do
+ setup do
+ @issue = Issue.find(1)
+ @issue_status = IssueStatus.find(1)
+ @issue_status.update_attribute(:default_done_ratio, 50)
+ end
+
+ context "with Setting.issue_done_ratio using the issue_field" do
+ setup do
+ Setting.issue_done_ratio = 'issue_field'
+ end
+
+ should "change nothing" do
+ IssueStatus.update_issue_done_ratios
+
+ assert_equal 0, Issue.count(:conditions => {:done_ratio => 50})
+ end
+ end
+
+ context "with Setting.issue_done_ratio using the issue_status" do
+ setup do
+ Setting.issue_done_ratio = 'issue_status'
+ end
+
+ should "update all of the issue's done_ratios to match their Issue Status" do
+ IssueStatus.update_issue_done_ratios
+
+ issues = Issue.find([1,3,4,5,6,7,9,10])
+ issues.each do |issue|
+ assert_equal @issue_status, issue.status
+ assert_equal 50, issue.read_attribute(:done_ratio)
+ end
+ end
+ end
+ end
end
diff --git a/test/unit/issue_test.rb b/test/unit/issue_test.rb
index e91880c08..fa9d56dbd 100644
--- a/test/unit/issue_test.rb
+++ b/test/unit/issue_test.rb
@@ -529,4 +529,64 @@ class IssueTest < ActiveSupport::TestCase
end
assert ActionMailer::Base.deliveries.empty?
end
+
+ context "#done_ratio" do
+ setup do
+ @issue = Issue.find(1)
+ @issue_status = IssueStatus.find(1)
+ @issue_status.update_attribute(:default_done_ratio, 50)
+ end
+
+ context "with Setting.issue_done_ratio using the issue_field" do
+ setup do
+ Setting.issue_done_ratio = 'issue_field'
+ end
+
+ should "read the issue's field" do
+ assert_equal 0, @issue.done_ratio
+ end
+ end
+
+ context "with Setting.issue_done_ratio using the issue_status" do
+ setup do
+ Setting.issue_done_ratio = 'issue_status'
+ end
+
+ should "read the Issue Status's default done ratio" do
+ assert_equal 50, @issue.done_ratio
+ end
+ end
+ end
+
+ context "#update_done_ratio_from_issue_status" do
+ setup do
+ @issue = Issue.find(1)
+ @issue_status = IssueStatus.find(1)
+ @issue_status.update_attribute(:default_done_ratio, 50)
+ end
+
+ context "with Setting.issue_done_ratio using the issue_field" do
+ setup do
+ Setting.issue_done_ratio = 'issue_field'
+ end
+
+ should "not change the issue" do
+ @issue.update_done_ratio_from_issue_status
+
+ assert_equal 0, @issue.done_ratio
+ end
+ end
+
+ context "with Setting.issue_done_ratio using the issue_status" do
+ setup do
+ Setting.issue_done_ratio = 'issue_status'
+ end
+
+ should "not change the issue's done ratio" do
+ @issue.update_done_ratio_from_issue_status
+
+ assert_equal 50, @issue.done_ratio
+ end
+ end
+ end
end