@@ -0,0 +1,9 | |||
|
1 | class AddDefaultDoneRatioToIssueStatus < ActiveRecord::Migration | |
|
2 | def self.up | |
|
3 | add_column :issue_statuses, :default_done_ratio, :integer | |
|
4 | end | |
|
5 | ||
|
6 | def self.down | |
|
7 | remove_column :issue_statuses, :default_done_ratio | |
|
8 | end | |
|
9 | end |
|
1 | NO CONTENT: new file 100644, binary diff hidden |
@@ -18,7 +18,7 | |||
|
18 | 18 | class IssueStatusesController < ApplicationController |
|
19 | 19 | before_filter :require_admin |
|
20 | 20 | |
|
21 | verify :method => :post, :only => [ :destroy, :create, :update, :move ], | |
|
21 | verify :method => :post, :only => [ :destroy, :create, :update, :move, :update_issue_done_ratio ], | |
|
22 | 22 | :redirect_to => { :action => :list } |
|
23 | 23 | |
|
24 | 24 | def index |
@@ -66,4 +66,13 class IssueStatusesController < ApplicationController | |||
|
66 | 66 | flash[:error] = "Unable to delete issue status" |
|
67 | 67 | redirect_to :action => 'list' |
|
68 | 68 | end |
|
69 | ||
|
70 | def update_issue_done_ratio | |
|
71 | if IssueStatus.update_issue_done_ratios | |
|
72 | flash[:notice] = l(:notice_issue_done_ratios_updated) | |
|
73 | else | |
|
74 | flash[:error] = l(:error_issue_done_ratios_not_updated) | |
|
75 | end | |
|
76 | redirect_to :action => 'list' | |
|
77 | end | |
|
69 | 78 | end |
@@ -46,6 +46,8 class Issue < ActiveRecord::Base | |||
|
46 | 46 | acts_as_activity_provider :find_options => {:include => [:project, :author, :tracker]}, |
|
47 | 47 | :author_key => :author_id |
|
48 | 48 | |
|
49 | DONE_RATIO_OPTIONS = %w(issue_field issue_status) | |
|
50 | ||
|
49 | 51 | validates_presence_of :subject, :priority, :project, :tracker, :author, :status |
|
50 | 52 | validates_length_of :subject, :maximum => 255 |
|
51 | 53 | validates_inclusion_of :done_ratio, :in => 0..100 |
@@ -56,6 +58,7 class Issue < ActiveRecord::Base | |||
|
56 | 58 | |
|
57 | 59 | named_scope :open, :conditions => ["#{IssueStatus.table_name}.is_closed = ?", false], :include => :status |
|
58 | 60 | |
|
61 | before_save :update_done_ratio_from_issue_status | |
|
59 | 62 | after_save :create_journal |
|
60 | 63 | |
|
61 | 64 | # Returns true if usr or current user is allowed to view the issue |
@@ -162,6 +165,22 class Issue < ActiveRecord::Base | |||
|
162 | 165 | write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h) |
|
163 | 166 | end |
|
164 | 167 | |
|
168 | def done_ratio | |
|
169 | if Issue.use_status_for_done_ratio? && !self.status.default_done_ratio.blank? | |
|
170 | self.status.default_done_ratio | |
|
171 | else | |
|
172 | read_attribute(:done_ratio) | |
|
173 | end | |
|
174 | end | |
|
175 | ||
|
176 | def self.use_status_for_done_ratio? | |
|
177 | Setting.issue_done_ratio == 'issue_status' | |
|
178 | end | |
|
179 | ||
|
180 | def self.use_field_for_done_ratio? | |
|
181 | Setting.issue_done_ratio == 'issue_field' | |
|
182 | end | |
|
183 | ||
|
165 | 184 | def validate |
|
166 | 185 | if self.due_date.nil? && @attributes['due_date'] && !@attributes['due_date'].empty? |
|
167 | 186 | errors.add :due_date, :not_a_date |
@@ -198,6 +217,14 class Issue < ActiveRecord::Base | |||
|
198 | 217 | end |
|
199 | 218 | end |
|
200 | 219 | |
|
220 | # Set the done_ratio using the status if that setting is set. This will keep the done_ratios | |
|
221 | # even if the user turns off the setting later | |
|
222 | def update_done_ratio_from_issue_status | |
|
223 | if Issue.use_status_for_done_ratio? && !self.status.default_done_ratio.blank? | |
|
224 | self.done_ratio = self.status.default_done_ratio | |
|
225 | end | |
|
226 | end | |
|
227 | ||
|
201 | 228 | def after_save |
|
202 | 229 | # Reload is needed in order to get the right status |
|
203 | 230 | reload |
@@ -34,6 +34,18 class IssueStatus < ActiveRecord::Base | |||
|
34 | 34 | find(:first, :conditions =>["is_default=?", true]) |
|
35 | 35 | end |
|
36 | 36 | |
|
37 | # Update all the +Issues+ setting their done_ratio to the value of their +IssueStatus+ | |
|
38 | def self.update_issue_done_ratios | |
|
39 | if Issue.use_status_for_done_ratio? | |
|
40 | IssueStatus.find(:all, :conditions => ["default_done_ratio >= 0"]).each do |status| | |
|
41 | Issue.update_all(["done_ratio = ?", status.default_done_ratio], | |
|
42 | ["status_id = ?", status.id]) | |
|
43 | end | |
|
44 | end | |
|
45 | ||
|
46 | return Issue.use_status_for_done_ratio? | |
|
47 | end | |
|
48 | ||
|
37 | 49 | # Returns an array of all statuses the given role can switch to |
|
38 | 50 | # Uses association cache when called more than one time |
|
39 | 51 | def new_statuses_allowed_to(roles, tracker) |
@@ -5,6 +5,11 | |||
|
5 | 5 | <p><label for="issue_status_name"><%=l(:field_name)%><span class="required"> *</span></label> |
|
6 | 6 | <%= text_field 'issue_status', 'name' %></p> |
|
7 | 7 | |
|
8 | <% if Issue.use_status_for_done_ratio? %> | |
|
9 | <p><label for="issue_done_ratio"><%=l(:field_done_ratio)%></label> | |
|
10 | <%= select 'issue_status', :default_done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p> | |
|
11 | <% end %> | |
|
12 | ||
|
8 | 13 | <p><label for="issue_status_is_closed"><%=l(:field_is_closed)%></label> |
|
9 | 14 | <%= check_box 'issue_status', 'is_closed' %></p> |
|
10 | 15 |
@@ -1,5 +1,6 | |||
|
1 | 1 | <div class="contextual"> |
|
2 | 2 | <%= link_to l(:label_issue_status_new), {:action => 'new'}, :class => 'icon icon-add' %> |
|
3 | <%= link_to(l(:label_update_issue_done_ratios), {:action => 'update_issue_done_ratio'}, :class => 'icon icon-multiple', :method => 'post', :confirm => l(:text_are_you_sure)) if Issue.use_status_for_done_ratio? %> | |
|
3 | 4 | </div> |
|
4 | 5 | |
|
5 | 6 | <h2><%=l(:label_issue_status_plural)%></h2> |
@@ -7,6 +8,9 | |||
|
7 | 8 | <table class="list"> |
|
8 | 9 | <thead><tr> |
|
9 | 10 | <th><%=l(:field_status)%></th> |
|
11 | <% if Issue.use_status_for_done_ratio? %> | |
|
12 | <th><%=l(:field_done_ratio)%></th> | |
|
13 | <% end %> | |
|
10 | 14 | <th><%=l(:field_is_default)%></th> |
|
11 | 15 | <th><%=l(:field_is_closed)%></th> |
|
12 | 16 | <th><%=l(:button_sort)%></th> |
@@ -16,6 +20,9 | |||
|
16 | 20 | <% for status in @issue_statuses %> |
|
17 | 21 | <tr class="<%= cycle("odd", "even") %>"> |
|
18 | 22 | <td><%= link_to status.name, :action => 'edit', :id => status %></td> |
|
23 | <% if Issue.use_status_for_done_ratio? %> | |
|
24 | <td align="center"><%= h status.default_done_ratio %></td> | |
|
25 | <% end %> | |
|
19 | 26 | <td align="center"><%= image_tag 'true.png' if status.is_default? %></td> |
|
20 | 27 | <td align="center"><%= image_tag 'true.png' if status.is_closed? %></td> |
|
21 | 28 | <td align="center" style="width:15%;"><%= reorder_links('issue_status', {:action => 'update', :id => status}) %></td> |
@@ -34,7 +34,9 | |||
|
34 | 34 | <p><%= f.text_field :start_date, :size => 10 %><%= calendar_for('issue_start_date') %></p> |
|
35 | 35 | <p><%= f.text_field :due_date, :size => 10 %><%= calendar_for('issue_due_date') %></p> |
|
36 | 36 | <p><%= f.text_field :estimated_hours, :size => 3 %> <%= l(:field_hours) %></p> |
|
37 | <% if Issue.use_field_for_done_ratio? %> | |
|
37 | 38 | <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p> |
|
39 | <% end %> | |
|
38 | 40 | </div> |
|
39 | 41 | |
|
40 | 42 | <div style="clear:both;"> </div> |
@@ -4,7 +4,9 | |||
|
4 | 4 | <p><%= f.select :assigned_to_id, (@issue.assignable_users.collect {|m| [m.name, m.id]}), :include_blank => true %></p> |
|
5 | 5 | </div> |
|
6 | 6 | <div class="splitcontentright"> |
|
7 | <% if Issue.use_field_for_done_ratio? %> | |
|
7 | 8 | <p><%= f.select :done_ratio, ((0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></p> |
|
9 | <% end %> | |
|
8 | 10 | <% unless @issue.assignable_versions.empty? %> |
|
9 | 11 | <p><%= f.select :fixed_version_id, (@issue.assignable_versions.collect {|v| [v.name, v.id]}), :include_blank => true %></p> |
|
10 | 12 | <% end %> |
@@ -39,8 +39,10 | |||
|
39 | 39 | <%= text_field_tag 'start_date', '', :size => 10 %><%= calendar_for('start_date') %></label> |
|
40 | 40 | <label><%= l(:field_due_date) %>: |
|
41 | 41 | <%= text_field_tag 'due_date', '', :size => 10 %><%= calendar_for('due_date') %></label> |
|
42 | <% if Issue.use_field_for_done_ratio? %> | |
|
42 | 43 | <label><%= l(:field_done_ratio) %>: |
|
43 | 44 | <%= select_tag 'done_ratio', options_for_select([[l(:label_no_change_option), '']] + (0..10).to_a.collect {|r| ["#{r*10} %", r*10] }) %></label> |
|
45 | <% end %> | |
|
44 | 46 | </p> |
|
45 | 47 | |
|
46 | 48 | <% @custom_fields.each do |custom_field| %> |
@@ -77,6 +77,7 | |||
|
77 | 77 | </ul> |
|
78 | 78 | </li> |
|
79 | 79 | <% end -%> |
|
80 | <% if Issue.use_field_for_done_ratio? %> | |
|
80 | 81 | <li class="folder"> |
|
81 | 82 | <a href="#" class="submenu"><%= l(:field_done_ratio) %></a> |
|
82 | 83 | <ul> |
@@ -86,7 +87,7 | |||
|
86 | 87 | <% end -%> |
|
87 | 88 | </ul> |
|
88 | 89 | </li> |
|
89 | ||
|
90 | <% end %> | |
|
90 | 91 | <% if !@issue.nil? %> |
|
91 | 92 | <% if @can[:log_time] -%> |
|
92 | 93 | <li><%= context_menu_link l(:button_log_time), {:controller => 'timelog', :action => 'edit', :issue_id => @issue}, |
@@ -11,6 +11,9 | |||
|
11 | 11 | <%= check_box_tag 'settings[display_subprojects_issues]', 1, Setting.display_subprojects_issues? %> |
|
12 | 12 | </p> |
|
13 | 13 | |
|
14 | <p><label><%= l(:setting_issue_done_ratio) %></label> | |
|
15 | <%= select_tag 'settings[issue_done_ratio]', options_for_select(Issue::DONE_RATIO_OPTIONS.collect {|i| [l(i.to_sym), i]}, Setting.issue_done_ratio) %></p> | |
|
16 | ||
|
14 | 17 | <p><label><%= l(:setting_issues_export_limit) %></label> |
|
15 | 18 | <%= text_field_tag 'settings[issues_export_limit]', Setting.issues_export_limit, :size => 6 %></p> |
|
16 | 19 | </div> |
@@ -147,6 +147,7 en: | |||
|
147 | 147 | notice_account_pending: "Your account was created and is now pending administrator approval." |
|
148 | 148 | notice_default_data_loaded: Default configuration successfully loaded. |
|
149 | 149 | notice_unable_delete_version: Unable to delete version. |
|
150 | notice_issue_done_ratios_updated: Issue done ratios updated. | |
|
150 | 151 | |
|
151 | 152 | error_can_t_load_default_data: "Default configuration could not be loaded: {{value}}" |
|
152 | 153 | error_scm_not_found: "The entry or revision was not found in the repository." |
@@ -157,6 +158,7 en: | |||
|
157 | 158 | error_no_default_issue_status: 'No default issue status is defined. Please check your configuration (Go to "Administration -> Issue statuses").' |
|
158 | 159 | error_can_not_reopen_issue_on_closed_version: 'An issue assigned to a closed version can not be reopened' |
|
159 | 160 | error_can_not_archive_project: This project can not be archived |
|
161 | error_issue_done_ratios_not_updated: "Issue done ratios not updated." | |
|
160 | 162 | |
|
161 | 163 | warning_attachments_not_saved: "{{count}} file(s) could not be saved." |
|
162 | 164 | |
@@ -309,6 +311,7 en: | |||
|
309 | 311 | setting_sequential_project_identifiers: Generate sequential project identifiers |
|
310 | 312 | setting_gravatar_enabled: Use Gravatar user icons |
|
311 | 313 | setting_gravatar_default: Default Gravatar image |
|
314 | setting_issue_done_ratio: Calculate the issue done ratio with | |
|
312 | 315 | setting_diff_max_lines_displayed: Max number of diff lines displayed |
|
313 | 316 | setting_file_max_size_displayed: Max size of text files displayed inline |
|
314 | 317 | setting_repository_log_display_limit: Maximum number of revisions displayed on file log |
@@ -716,6 +719,7 en: | |||
|
716 | 719 | label_version_sharing_hierarchy: With project hierarchy |
|
717 | 720 | label_version_sharing_tree: With project tree |
|
718 | 721 | label_version_sharing_system: With all projects |
|
722 | label_update_issue_done_ratios: Update issue done ratios | |
|
719 | 723 | |
|
720 | 724 | button_login: Login |
|
721 | 725 | button_submit: Submit |
@@ -850,3 +854,6 en: | |||
|
850 | 854 | enumeration_doc_categories: Document categories |
|
851 | 855 | enumeration_activities: Activities (time tracking) |
|
852 | 856 | enumeration_system_activity: System Activity |
|
857 | ||
|
858 | issue_field: Use the issue field | |
|
859 | issue_status: Use the issue status |
@@ -129,6 +129,8 issue_list_default_columns: | |||
|
129 | 129 | - updated_on |
|
130 | 130 | display_subprojects_issues: |
|
131 | 131 | default: 1 |
|
132 | issue_done_ratio: | |
|
133 | default: 'issue_field' | |
|
132 | 134 | default_projects_public: |
|
133 | 135 | default: 1 |
|
134 | 136 | default_projects_modules: |
@@ -702,6 +702,7 vertical-align: middle; | |||
|
702 | 702 | .icon-move { background-image: url(../images/move.png); } |
|
703 | 703 | .icon-save { background-image: url(../images/save.png); } |
|
704 | 704 | .icon-cancel { background-image: url(../images/cancel.png); } |
|
705 | .icon-multiple { background-image: url(../images/table_multiple.png); } | |
|
705 | 706 | .icon-folder { background-image: url(../images/folder.png); } |
|
706 | 707 | .open .icon-folder { background-image: url(../images/folder_open.png); } |
|
707 | 708 | .icon-package { background-image: url(../images/package.png); } |
@@ -70,4 +70,27 class IssueStatusesControllerTest < ActionController::TestCase | |||
|
70 | 70 | assert_redirected_to 'issue_statuses/list' |
|
71 | 71 | assert_not_nil IssueStatus.find_by_id(1) |
|
72 | 72 | end |
|
73 | ||
|
74 | context "on POST to :update_issue_done_ratio" do | |
|
75 | context "with Setting.issue_done_ratio using the issue_field" do | |
|
76 | setup do | |
|
77 | Setting.issue_done_ratio = 'issue_field' | |
|
78 | post :update_issue_done_ratio | |
|
79 | end | |
|
80 | ||
|
81 | should_set_the_flash_to /not updated/ | |
|
82 | should_redirect_to('the list') { '/issue_statuses/list' } | |
|
83 | end | |
|
84 | ||
|
85 | context "with Setting.issue_done_ratio using the issue_status" do | |
|
86 | setup do | |
|
87 | Setting.issue_done_ratio = 'issue_status' | |
|
88 | post :update_issue_done_ratio | |
|
89 | end | |
|
90 | ||
|
91 | should_set_the_flash_to /Issue done ratios updated/ | |
|
92 | should_redirect_to('the list') { '/issue_statuses/list' } | |
|
93 | end | |
|
94 | end | |
|
95 | ||
|
73 | 96 | end |
@@ -66,4 +66,40 class IssueStatusTest < ActiveSupport::TestCase | |||
|
66 | 66 | status.reload |
|
67 | 67 | assert status.is_default? |
|
68 | 68 | end |
|
69 | ||
|
70 | context "#update_done_ratios" do | |
|
71 | setup do | |
|
72 | @issue = Issue.find(1) | |
|
73 | @issue_status = IssueStatus.find(1) | |
|
74 | @issue_status.update_attribute(:default_done_ratio, 50) | |
|
75 | end | |
|
76 | ||
|
77 | context "with Setting.issue_done_ratio using the issue_field" do | |
|
78 | setup do | |
|
79 | Setting.issue_done_ratio = 'issue_field' | |
|
80 | end | |
|
81 | ||
|
82 | should "change nothing" do | |
|
83 | IssueStatus.update_issue_done_ratios | |
|
84 | ||
|
85 | assert_equal 0, Issue.count(:conditions => {:done_ratio => 50}) | |
|
86 | end | |
|
87 | end | |
|
88 | ||
|
89 | context "with Setting.issue_done_ratio using the issue_status" do | |
|
90 | setup do | |
|
91 | Setting.issue_done_ratio = 'issue_status' | |
|
92 | end | |
|
93 | ||
|
94 | should "update all of the issue's done_ratios to match their Issue Status" do | |
|
95 | IssueStatus.update_issue_done_ratios | |
|
96 | ||
|
97 | issues = Issue.find([1,3,4,5,6,7,9,10]) | |
|
98 | issues.each do |issue| | |
|
99 | assert_equal @issue_status, issue.status | |
|
100 | assert_equal 50, issue.read_attribute(:done_ratio) | |
|
101 | end | |
|
102 | end | |
|
103 | end | |
|
104 | end | |
|
69 | 105 | end |
@@ -529,4 +529,64 class IssueTest < ActiveSupport::TestCase | |||
|
529 | 529 | end |
|
530 | 530 | assert ActionMailer::Base.deliveries.empty? |
|
531 | 531 | end |
|
532 | ||
|
533 | context "#done_ratio" do | |
|
534 | setup do | |
|
535 | @issue = Issue.find(1) | |
|
536 | @issue_status = IssueStatus.find(1) | |
|
537 | @issue_status.update_attribute(:default_done_ratio, 50) | |
|
538 | end | |
|
539 | ||
|
540 | context "with Setting.issue_done_ratio using the issue_field" do | |
|
541 | setup do | |
|
542 | Setting.issue_done_ratio = 'issue_field' | |
|
543 | end | |
|
544 | ||
|
545 | should "read the issue's field" do | |
|
546 | assert_equal 0, @issue.done_ratio | |
|
547 | end | |
|
548 | end | |
|
549 | ||
|
550 | context "with Setting.issue_done_ratio using the issue_status" do | |
|
551 | setup do | |
|
552 | Setting.issue_done_ratio = 'issue_status' | |
|
553 | end | |
|
554 | ||
|
555 | should "read the Issue Status's default done ratio" do | |
|
556 | assert_equal 50, @issue.done_ratio | |
|
557 | end | |
|
558 | end | |
|
559 | end | |
|
560 | ||
|
561 | context "#update_done_ratio_from_issue_status" do | |
|
562 | setup do | |
|
563 | @issue = Issue.find(1) | |
|
564 | @issue_status = IssueStatus.find(1) | |
|
565 | @issue_status.update_attribute(:default_done_ratio, 50) | |
|
566 | end | |
|
567 | ||
|
568 | context "with Setting.issue_done_ratio using the issue_field" do | |
|
569 | setup do | |
|
570 | Setting.issue_done_ratio = 'issue_field' | |
|
571 | end | |
|
572 | ||
|
573 | should "not change the issue" do | |
|
574 | @issue.update_done_ratio_from_issue_status | |
|
575 | ||
|
576 | assert_equal 0, @issue.done_ratio | |
|
577 | end | |
|
578 | end | |
|
579 | ||
|
580 | context "with Setting.issue_done_ratio using the issue_status" do | |
|
581 | setup do | |
|
582 | Setting.issue_done_ratio = 'issue_status' | |
|
583 | end | |
|
584 | ||
|
585 | should "not change the issue's done ratio" do | |
|
586 | @issue.update_done_ratio_from_issue_status | |
|
587 | ||
|
588 | assert_equal 50, @issue.done_ratio | |
|
589 | end | |
|
590 | end | |
|
591 | end | |
|
532 | 592 | end |
General Comments 0
You need to be logged in to leave comments.
Login now