Patch #2268 » total_estimated_hour_2_6_4_edited.diff
app/controllers/issues_controller.rb (working copy) | ||
---|---|---|
81 | 81 |
:order => sort_clause, |
82 | 82 |
:offset => @offset, |
83 | 83 |
:limit => @limit) |
84 | ||
85 |
@all_issues = @query.issues(:include => [:status, :project, :assigned_to, :tracker, :priority, :category, :fixed_version]) |
|
86 |
|
|
84 | 87 |
@issue_count_by_group = @query.issue_count_by_group |
88 |
@issue_sum_by_group = @query.issue_sum_by_group |
|
89 |
@issue_progress_by_group = @query.issue_progress_by_group |
|
85 | 90 | |
86 | 91 |
respond_to do |format| |
87 | 92 |
format.html { render :template => 'issues/index', :layout => !request.xhr? } |
... | ... | |
148 | 153 |
call_hook(:controller_issues_new_before_save, { :params => params, :issue => @issue }) |
149 | 154 |
@issue.save_attachments(params[:attachments] || (params[:issue] && params[:issue][:uploads])) |
150 | 155 |
if @issue.save |
156 |
@issue.update_estimated_done |
|
151 | 157 |
call_hook(:controller_issues_new_after_save, { :params => params, :issue => @issue}) |
152 | 158 |
respond_to do |format| |
153 | 159 |
format.html { |
... | ... | |
195 | 201 |
end |
196 | 202 | |
197 | 203 |
if saved |
204 |
@issue.update_estimated_done |
|
198 | 205 |
render_attachment_warning_if_needed(@issue) |
199 | 206 |
flash[:notice] = l(:notice_successful_update) unless @issue.current_journal.new_record? |
200 | 207 |
app/helpers/issues_helper.rb (working copy) | ||
---|---|---|
427 | 427 |
end |
428 | 428 |
end |
429 | 429 |
end |
430 | ||
431 |
def estimated_done(issues) |
|
432 |
issues.map(&:estimated_done).reject{|x|x.nil?}.sum.round(2) |
|
433 |
end |
|
434 | ||
435 |
def estimated_hours(issues) |
|
436 |
issues.map(&:estimated_hours).reject{|x| x.nil?}.sum |
|
437 |
end |
|
438 | ||
439 |
def estimated_done_percentage(issues) |
|
440 |
(100 * estimated_done(issues) / estimated_hours(issues)).round(2) |
|
441 |
end |
|
430 | 442 |
end |
app/helpers/queries_helper.rb (working copy) | ||
---|---|---|
103 | 103 |
issue.description? ? content_tag('div', textilizable(issue, :description), :class => "wiki") : '' |
104 | 104 |
when :done_ratio |
105 | 105 |
progress_bar(value, :width => '80px') |
106 |
when :estimated_done |
|
107 |
sprintf "%.2f", value |
|
106 | 108 |
when :relations |
107 | 109 |
other = value.other_issue(issue) |
108 | 110 |
content_tag('span', |
app/models/issue.rb (working copy) | ||
---|---|---|
356 | 356 |
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h) |
357 | 357 |
end |
358 | 358 | |
359 |
def estimated_done=(h) |
|
360 |
write_attribute :estimated_done, (h.is_a?(String) ? h.to_hours : h) |
|
361 |
end |
|
362 |
|
|
359 | 363 |
safe_attributes 'project_id', |
360 | 364 |
:if => lambda {|issue, user| |
361 | 365 |
if issue.new_record? |
... | ... | |
569 | 573 |
private :workflow_rule_by_attribute |
570 | 574 | |
571 | 575 |
def done_ratio |
572 |
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio |
|
576 |
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio && self.leaves && self.leaves.count == 0
|
|
573 | 577 |
status.default_done_ratio |
574 | 578 |
else |
575 | 579 |
read_attribute(:done_ratio) |
... | ... | |
651 | 655 |
# Set the done_ratio using the status if that setting is set. This will keep the done_ratios |
652 | 656 |
# even if the user turns off the setting later |
653 | 657 |
def update_done_ratio_from_issue_status |
654 |
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio |
|
658 |
if Issue.use_status_for_done_ratio? && status && status.default_done_ratio && self.leaves && self.leaves.count == 0
|
|
655 | 659 |
self.done_ratio = status.default_done_ratio |
656 | 660 |
end |
657 | 661 |
end |
... | ... | |
770 | 774 |
!relations_to.detect {|ir| ir.relation_type == 'blocks' && !ir.issue_from.closed?}.nil? |
771 | 775 |
end |
772 | 776 | |
777 |
def update_estimated_done |
|
778 |
if children.count < 1 |
|
779 |
x1 = Issue.find_by_id(id).done_ratio.to_f |
|
780 |
x2 = Issue.find_by_id(id).estimated_hours.to_f |
|
781 |
r = ((x1 * x2)/100).round(2) |
|
782 |
Issue.update(id, :estimated_done => r) |
|
783 |
end |
|
784 |
end |
|
785 |
|
|
773 | 786 |
# Returns an array of statuses that user is able to apply |
774 | 787 |
def new_statuses_allowed_to(user=User.current, include_default=false) |
775 | 788 |
if new_record? && @copied_from |
... | ... | |
1406 | 1419 |
if average == 0 |
1407 | 1420 |
average = 1 |
1408 | 1421 |
end |
1409 |
done = p.leaves.joins(:status). |
|
1410 |
sum("COALESCE(CASE WHEN estimated_hours > 0 THEN estimated_hours ELSE NULL END, #{average}) " + |
|
1411 |
"* (CASE WHEN is_closed = #{connection.quoted_true} THEN 100 ELSE COALESCE(done_ratio, 0) END)").to_f |
|
1412 |
progress = done / (average * leaves_count) |
|
1413 |
p.done_ratio = progress.round |
|
1414 | 1422 |
end |
1415 | 1423 |
end |
1416 | 1424 | |
1417 | 1425 |
# estimate = sum of leaves estimates |
1418 | 1426 |
p.estimated_hours = p.leaves.sum(:estimated_hours).to_f |
1427 |
p.estimated_done = p.leaves.sum(:estimated_done).to_f |
|
1428 |
|
|
1429 |
if (p.estimated_hours > 0) |
|
1430 |
p.done_ratio = (100 * p.estimated_done / p.estimated_hours).to_f.round(2) |
|
1431 |
end |
|
1432 |
|
|
1419 | 1433 |
p.estimated_hours = nil if p.estimated_hours == 0.0 |
1434 |
p.estimated_done = nil if p.estimated_done == 0.0 |
|
1420 | 1435 | |
1421 | 1436 |
# ancestors will be recursively updated |
1422 | 1437 |
p.save(:validate => false) |
... | ... | |
1611 | 1626 |
group by s.id, s.is_closed, j.id") |
1612 | 1627 |
end |
1613 | 1628 |
end |
1629 | ||
1630 |
app/models/issue_query.rb (working copy) | ||
---|---|---|
38 | 38 |
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true), |
39 | 39 |
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'), |
40 | 40 |
QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'), |
41 |
QueryColumn.new(:estimated_done, :sortable => "#{Issue.table_name}.estimated_done", :caption => :field_estimated_done), |
|
41 | 42 |
QueryColumn.new(:relations, :caption => :label_related_issues), |
42 | 43 |
QueryColumn.new(:description, :inline => false) |
43 | 44 |
] |
... | ... | |
214 | 215 |
add_available_filter "due_date", :type => :date |
215 | 216 |
add_available_filter "estimated_hours", :type => :float |
216 | 217 |
add_available_filter "done_ratio", :type => :integer |
218 |
add_available_filter "estimated_done", :type => :float |
|
217 | 219 | |
218 | 220 |
if User.current.allowed_to?(:set_issues_private, nil, :global => true) || |
219 | 221 |
User.current.allowed_to?(:set_own_issues_private, nil, :global => true) |
... | ... | |
293 | 295 |
rescue ::ActiveRecord::StatementInvalid => e |
294 | 296 |
raise StatementInvalid.new(e.message) |
295 | 297 |
end |
298 |
|
|
299 |
# Returns sum of all the issue's estimated_hours |
|
300 |
def issue_sum |
|
301 |
Issue.visible.sum(:estimated_hours, :include => [:status, :project], :conditions => statement) |
|
302 |
rescue ::ActiveRecord::StatementInvalid => e |
|
303 |
raise StatementInvalid.new(e.message) |
|
304 |
end |
|
296 | 305 | |
306 |
# Returns sum of all the issue's estimated_done |
|
307 |
def issue_sum_in_progress |
|
308 |
Issue.visible.sum(:estimated_done, :include => [:status, :project], :conditions => statement) |
|
309 |
rescue ::ActiveRecord::StatementInvalid => e |
|
310 |
raise StatementInvalid.new(e.message) |
|
311 |
end |
|
312 | ||
297 | 313 |
# Returns the issue count by group or nil if query is not grouped |
298 | 314 |
def issue_count_by_group |
299 | 315 |
r = nil |
... | ... | |
319 | 335 |
raise StatementInvalid.new(e.message) |
320 | 336 |
end |
321 | 337 | |
338 |
# Returns sum of the issue's estimated_hours by group or nil if query is not grouped |
|
339 |
def issue_sum_by_group |
|
340 |
r = nil |
|
341 |
if grouped? |
|
342 |
begin |
|
343 |
r = Issue.visible.sum(:estimated_hours, :joins => joins_for_order_statement(group_by_statement), :group => group_by_statement, :include => [:status, :project], :conditions => statement) |
|
344 |
rescue ActiveRecord::RecordNotFound |
|
345 |
r= {r => issue_sum} |
|
346 |
end |
|
347 |
|
|
348 |
c = group_by_column |
|
349 |
if c.is_a?(QueryCustomFieldColumn) |
|
350 |
r = r.keys.inject({}) {|h,k| h[c.custom_field.cast_value(k)] = r[k]; h} |
|
351 |
end |
|
352 |
end |
|
353 |
r |
|
354 |
rescue ::ActiveRecord::StatementInvalid => e |
|
355 |
raise StatementInvalid.new(e.message) |
|
356 |
end |
|
357 |
|
|
358 |
# Returns sum of the issue's estimated_done by group or nil if query is not grouped |
|
359 |
def issue_progress_by_group |
|
360 |
r = nil |
|
361 |
if grouped? |
|
362 |
begin |
|
363 |
r = Issue.visible.sum(:estimated_done, :joins => joins_for_order_statement(group_by_statement), :group => group_by_statement, :include => [:status, :project], :conditions => statement) |
|
364 |
rescue ActiveRecord::RecordNotFound |
|
365 |
r= {r => issue_sum_by_group} |
|
366 |
end |
|
367 |
|
|
368 |
c = group_by_column |
|
369 |
if c.is_a?(QueryCustomFieldColumn) |
|
370 |
r = r.keys.inject({}) {|h, k| h[c.custom_field.cast_value(k)] = r[k]; h} |
|
371 |
end |
|
372 |
end |
|
373 |
r |
|
374 |
rescue ::ActiveRecord::StatementInvalid => e |
|
375 |
raise StatementInvalid.new(e.message) |
|
376 |
end |
|
377 |
|
|
322 | 378 |
# Returns the issues |
323 | 379 |
# Valid options are :order, :offset, :limit, :include, :conditions |
324 | 380 |
def issues(options={}) |
app/views/issues/_list.html.erb (working copy) | ||
---|---|---|
14 | 14 |
<% end %> |
15 | 15 |
</tr> |
16 | 16 |
</thead> |
17 |
<% previous_group, first = false, true %>
|
|
17 |
<% previous_group = false %>
|
|
18 | 18 |
<tbody> |
19 | 19 |
<% issue_list(issues) do |issue, level| -%> |
20 |
<% if @query.grouped? && ((group = @query.group_by_column.value(issue)) != previous_group || first) %>
|
|
20 |
<% if @query.grouped? && (group = @query.group_by_column.value(issue)) != previous_group %>
|
|
21 | 21 |
<% reset_cycle %> |
22 | 22 |
<tr class="group open"> |
23 | 23 |
<td colspan="<%= query.inline_columns.size + 2 %>"> |
24 | 24 |
<span class="expander" onclick="toggleRowGroup(this);"> </span> |
25 |
<%= (group.blank? && group != false) ? l(:label_none) : column_content(@query.group_by_column, issue) %> <span class="count"><%= @issue_count_by_group[group] %></span> |
|
26 |
<%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", |
|
27 |
"toggleAllRowGroups(this)", :class => 'toggle-all') %> |
|
25 |
<%= group.blank? ? l(:label_none) : column_content(@query.group_by_column, issue) %> <span class="count"><%= @issue_count_by_group[group] %>, Est Done: <%= (@issue_progress_by_group[group] * 100).round / 100.0 %> |
|
26 |
<% if @issue_sum_by_group[group] > 0 %> |
|
27 |
(<%= (100 * @issue_progress_by_group[group] / @issue_sum_by_group[group]).round(2) %>%), |
|
28 |
<% else %> |
|
29 |
(0.0%), |
|
30 |
<% end %> |
|
31 |
<%= l(:label_total) %>: <%= @issue_sum_by_group[group] %>)</span> |
|
32 |
<%= link_to_function("#{l(:button_collapse_all)}/#{l(:button_expand_all)}", "toggleAllRowGroups(this)", :class => 'toggle-all') %> |
|
28 | 33 |
</td> |
29 | 34 |
</tr> |
30 |
<% previous_group, first = group, false %>
|
|
35 |
<% previous_group = group %>
|
|
31 | 36 |
<% end %> |
32 | 37 |
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> |
33 | 38 |
<td class="checkbox hide-when-print"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td> |
... | ... | |
45 | 50 |
</table> |
46 | 51 |
</div> |
47 | 52 |
<% end -%> |
53 |
<p align="right"> |
|
54 |
Current page: <b><%=estimated_hours(issues) %></b> |
|
55 |
Est Done: <b><%= estimated_done(@all_issues) %> <% if estimated_hours(@all_issues) > 0 %> |
|
56 |
(<%= "#{estimated_done_percentage(@all_issues)}%" %>) |
|
57 |
<% else %>(0.0%) |
|
58 |
<% end %></b> |
|
59 |
<%= l(:label_total) %>: <b><%=@query.issue_sum %></b> |
|
60 |
</p> |
app/views/issues/show.html.erb (working copy) | ||
---|---|---|
60 | 60 |
rows.right l(:field_estimated_hours), l_hours(@issue.estimated_hours), :class => 'estimated-hours' |
61 | 61 |
end |
62 | 62 |
end |
63 |
unless @issue.disabled_core_fields.include?('estimated_hours') |
|
64 |
rows.right "Estimated done", l_hours(@issue.estimated_done), :class => 'estimated-hours' |
|
65 |
end |
|
63 | 66 |
if User.current.allowed_to?(:view_time_entries, @project) |
64 | 67 |
rows.right l(:label_spent_time), (@issue.total_spent_hours > 0 ? link_to(l_hours(@issue.total_spent_hours), issue_time_entries_path(@issue)) : "-"), :class => 'spent-time' |
65 | 68 |
end |
config/locales/en.yml (working copy) | ||
---|---|---|
300 | 300 |
field_assignable: Issues can be assigned to this role |
301 | 301 |
field_redirect_existing_links: Redirect existing links |
302 | 302 |
field_estimated_hours: Estimated time |
303 |
field_estimated_done: Estimated done |
|
303 | 304 |
field_column_names: Columns |
304 | 305 |
field_time_entries: Log time |
305 | 306 |
field_time_zone: Time zone |
- « Previous
- 1
- …
- 3
- 4
- 5
- Next »