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) |