Feature #37862 » 0001-estimated-remaining-hours-issue-query-column.patch
app/helpers/queries_helper.rb | ||
---|---|---|
186 | 186 |
def total_tag(column, value) |
187 | 187 |
label = content_tag('span', "#{column.caption}:") |
188 | 188 |
value = |
189 |
if [:hours, :spent_hours, :total_spent_hours, :estimated_hours, :total_estimated_hours].include? column.name |
|
189 |
if [:hours, :spent_hours, :total_spent_hours, :estimated_hours, :total_estimated_hours, :estimated_remaining_hours].include? column.name
|
|
190 | 190 |
format_hours(value) |
191 | 191 |
else |
192 | 192 |
format_object(value) |
... | ... | |
265 | 265 |
'span', |
266 | 266 |
value.to_s(item) {|other| link_to_issue(other, :subject => false, :tracker => false)}.html_safe, |
267 | 267 |
:class => value.css_classes_for(item)) |
268 |
when :hours, :estimated_hours, :total_estimated_hours |
|
268 |
when :hours, :estimated_hours, :total_estimated_hours, :estimated_remaining_hours
|
|
269 | 269 |
format_hours(value) |
270 | 270 |
when :spent_hours |
271 | 271 |
link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "#{item.id}")) |
app/models/issue_query.rb | ||
---|---|---|
18 | 18 |
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
19 | 19 | |
20 | 20 |
class IssueQuery < Query |
21 |
class EstimatedRemainingHoursColumn < QueryColumn |
|
22 |
COLUMN_SQL = Arel.sql("COALESCE(#{Issue.table_name}.estimated_hours, 0) * (100 - COALESCE(#{Issue.table_name}.done_ratio, 0)) / 100") |
|
23 | ||
24 |
def initialize |
|
25 |
super :estimated_remaining_hours, totalable: true, sortable: COLUMN_SQL |
|
26 |
end |
|
27 | ||
28 |
def value(object) |
|
29 |
(object.estimated_hours || 0) * (100 - (object.done_ratio || 0)) / 100 |
|
30 |
end |
|
31 | ||
32 |
def value_object(object) |
|
33 |
value(object) |
|
34 |
end |
|
35 |
end |
|
36 | ||
21 | 37 |
self.queried_class = Issue |
22 | 38 |
self.view_permission = :view_issues |
23 | 39 | |
... | ... | |
49 | 65 |
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date", :groupable => true), |
50 | 66 |
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours", |
51 | 67 |
:totalable => true), |
68 |
EstimatedRemainingHoursColumn.new, |
|
52 | 69 |
QueryColumn.new( |
53 | 70 |
:total_estimated_hours, |
54 | 71 |
:sortable => |
... | ... | |
324 | 341 |
end |
325 | 342 | |
326 | 343 |
disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.sub(/_id$/, '')} |
327 |
disabled_fields << "total_estimated_hours" if disabled_fields.include?("estimated_hours") |
|
344 |
if disabled_fields.include?("estimated_hours") |
|
345 |
disabled_fields += %w[total_estimated_hours estimated_remaining_hours] |
|
346 |
end |
|
328 | 347 |
@available_columns.reject! do |column| |
329 | 348 |
disabled_fields.include?(column.name.to_s) |
330 | 349 |
end |
... | ... | |
364 | 383 |
map_total(scope.sum(:estimated_hours)) {|t| t.to_f.round(2)} |
365 | 384 |
end |
366 | 385 | |
386 |
def total_for_estimated_remaining_hours(scope) |
|
387 |
map_total(scope.sum(EstimatedRemainingHoursColumn::COLUMN_SQL)) {|t| t.to_f.round(2)} |
|
388 |
end |
|
389 | ||
367 | 390 |
# Returns sum of all the issue's time entries hours |
368 | 391 |
def total_for_spent_hours(scope) |
369 | 392 |
total = scope.joins(:time_entries). |
config/locales/de.yml | ||
---|---|---|
398 | 398 |
field_watcher: Beobachter |
399 | 399 |
field_default_assigned_to: Standardbearbeiter |
400 | 400 |
field_unique_id: Eindeutige ID |
401 |
field_estimated_remaining_hours: Geschätzter verbleibender Aufwand |
|
401 | 402 | |
402 | 403 |
general_csv_decimal_separator: ',' |
403 | 404 |
general_csv_encoding: ISO-8859-1 |
config/locales/en.yml | ||
---|---|---|
418 | 418 |
field_default_issue_query: Default issue query |
419 | 419 |
field_default_project_query: Default project query |
420 | 420 |
field_default_time_entry_activity: Default spent time activity |
421 |
field_estimated_remaining_hours: Estimated remaining time |
|
421 | 422 | |
422 | 423 |
setting_app_title: Application title |
423 | 424 |
setting_welcome_text: Welcome text |
test/unit/query_test.rb | ||
---|---|---|
2029 | 2029 |
assert_include :estimated_hours, q.available_totalable_columns.map(&:name) |
2030 | 2030 |
end |
2031 | 2031 | |
2032 |
def test_available_totalable_columns_should_include_estimated_remaining_hours |
|
2033 |
q = IssueQuery.new |
|
2034 |
assert_include :estimated_remaining_hours, q.available_totalable_columns.map(&:name) |
|
2035 |
end |
|
2036 | ||
2032 | 2037 |
def test_available_totalable_columns_should_include_spent_hours |
2033 | 2038 |
User.current = User.find(1) |
2034 | 2039 | |
... | ... | |
2102 | 2107 |
) |
2103 | 2108 |
end |
2104 | 2109 | |
2110 |
def test_total_for_estimated_remaining_hours |
|
2111 |
Issue.delete_all |
|
2112 |
Issue.generate!(:estimated_hours => 5.5, :done_ratio => 50) |
|
2113 |
Issue.generate!(:estimated_hours => 1.1, :done_ratio => 100) |
|
2114 |
Issue.generate! |
|
2115 | ||
2116 |
q = IssueQuery.new |
|
2117 |
assert_equal 2.75, q.total_for(:estimated_remaining_hours) |
|
2118 |
end |
|
2119 | ||
2120 |
def test_total_by_group_for_estimated_remaining_hours |
|
2121 |
Issue.delete_all |
|
2122 |
Issue.generate!(:estimated_hours => 5.5, :assigned_to_id => 2, :done_ratio => 50) |
|
2123 |
Issue.generate!(:estimated_hours => 1.1, :assigned_to_id => 3, :done_ratio => 100) |
|
2124 |
Issue.generate!(:estimated_hours => 3.5, :done_ratio => 0) |
|
2125 | ||
2126 |
q = IssueQuery.new(:group_by => 'assigned_to') |
|
2127 |
assert_equal( |
|
2128 |
{nil => 3.5, User.find(2) => 2.75, User.find(3) => 0}, |
|
2129 |
q.total_by_group_for(:estimated_remaining_hours) |
|
2130 |
) |
|
2131 |
end |
|
2132 | ||
2105 | 2133 |
def test_total_for_spent_hours |
2106 | 2134 |
TimeEntry.delete_all |
2107 | 2135 |
TimeEntry.generate!(:hours => 5.5) |