Feature #35657 » 0001-Add-a-basic-and-incomplete-POC-implementation-of-tot.patch
| app/helpers/issues_helper.rb | ||
|---|---|---|
| 284 | 284 |
s |
| 285 | 285 |
end |
| 286 | 286 | |
| 287 |
def spent_time_ratio_details(issue) |
|
| 288 |
if issue.total_estimated_hours.present? && issue.total_estimated_hours > 0 |
|
| 289 |
if issue.total_spent_time_ratio == issue.spent_time_ratio |
|
| 290 |
content_tag( |
|
| 291 |
:span, |
|
| 292 |
"#{issue.spent_time_ratio.to_s(:percentage, precision: 2)}",
|
|
| 293 |
class: "spent-time-ratio-value" |
|
| 294 |
) |
|
| 295 |
else |
|
| 296 |
s = !issue.spent_time_ratio.nil? && (issue.spent_time_ratio > 0) ? |
|
| 297 |
content_tag( |
|
| 298 |
:span, |
|
| 299 |
"#{issue.spent_time_ratio.to_s(:percentage, precision: 2)}",
|
|
| 300 |
class: "spent-time-ratio-value" |
|
| 301 |
) : |
|
| 302 |
"" |
|
| 303 |
label = l(:label_total) |
|
| 304 |
value = content_tag( |
|
| 305 |
:span, |
|
| 306 |
"#{issue.total_spent_time_ratio.to_s(:percentage, precision: 2)}",
|
|
| 307 |
class: "total-spent-time-ratio-value" |
|
| 308 |
) |
|
| 309 |
s += " ("
|
|
| 310 |
s += label |
|
| 311 |
s += ": " |
|
| 312 |
s += value |
|
| 313 |
s += ")" |
|
| 314 |
s.html_safe |
|
| 315 |
end |
|
| 316 |
end |
|
| 317 |
end |
|
| 318 | ||
| 287 | 319 |
# Returns a link for adding a new subtask to the given issue |
| 288 | 320 |
def link_to_new_subtask(issue) |
| 289 | 321 |
link_to(l(:button_add), url_for_new_subtask(issue)) |
| app/helpers/queries_helper.rb | ||
|---|---|---|
| 273 | 273 |
link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "~#{item.id}"))
|
| 274 | 274 |
when :attachments |
| 275 | 275 |
value.to_a.map {|a| format_object(a)}.join(" ").html_safe
|
| 276 |
when :spent_time_ratio, :total_spent_time_ratio |
|
| 277 |
!value.nil? ? value.to_s(:percentage, precision: 2) : '' |
|
| 276 | 278 |
else |
| 277 | 279 |
format_object(value) |
| 278 | 280 |
end |
| app/models/issue.rb | ||
|---|---|---|
| 952 | 952 |
due_date.present? && (due_date < User.current.today) && !closed? |
| 953 | 953 |
end |
| 954 | 954 | |
| 955 |
def overspent? |
|
| 956 |
spent_time_ratio.present? && spent_time_ratio > 100 |
|
| 957 |
end |
|
| 958 | ||
| 959 |
def total_overspent? |
|
| 960 |
total_spent_time_ratio.present? && total_spent_time_ratio > 100 |
|
| 961 |
end |
|
| 962 | ||
| 955 | 963 |
# Is the amount of work done less than it should for the due date |
| 956 | 964 |
def behind_schedule? |
| 957 | 965 |
return false if start_date.nil? || due_date.nil? |
| ... | ... | |
| 1146 | 1154 |
end |
| 1147 | 1155 |
end |
| 1148 | 1156 | |
| 1157 |
def spent_time_ratio |
|
| 1158 |
if (estimated_hours.present? && estimated_hours > 0) |
|
| 1159 |
if (spent_hours.present? && spent_hours == 0) |
|
| 1160 |
0.to_f.round(2) |
|
| 1161 |
elsif (spent_hours.present? && spent_hours > 0) |
|
| 1162 |
spent_time_ratio = spent_hours.to_f / estimated_hours.to_f * 100.0 |
|
| 1163 |
spent_time_ratio.round(2) |
|
| 1164 |
end |
|
| 1165 |
end |
|
| 1166 |
end |
|
| 1167 | ||
| 1168 |
def total_spent_time_ratio |
|
| 1169 |
if (total_estimated_hours.present? && total_estimated_hours > 0) |
|
| 1170 |
if (total_spent_hours.present? && total_spent_hours == 0) |
|
| 1171 |
0.to_f.round(2) |
|
| 1172 |
elsif (total_spent_hours.present? && total_spent_hours > 0) |
|
| 1173 |
total_spent_time_ratio = |
|
| 1174 |
total_spent_hours.to_f / total_estimated_hours.to_f * 100.0 |
|
| 1175 |
total_spent_time_ratio.round(2) |
|
| 1176 |
end |
|
| 1177 |
end |
|
| 1178 |
end |
|
| 1179 | ||
| 1149 | 1180 |
def relations |
| 1150 | 1181 |
@relations ||= IssueRelation::Relations.new(self, (relations_from + relations_to).sort) |
| 1151 | 1182 |
end |
| ... | ... | |
| 1439 | 1470 |
s << ' parent' unless leaf? |
| 1440 | 1471 |
s << ' private' if is_private? |
| 1441 | 1472 |
s << ' behind-schedule' if behind_schedule? |
| 1473 |
if user.allowed_to?(:view_time_entries, project, :global => true) |
|
| 1474 |
s << ' overspent' if overspent? |
|
| 1475 |
s << ' total-overspent' if total_overspent? |
|
| 1476 |
end |
|
| 1442 | 1477 |
if user.logged? |
| 1443 | 1478 |
s << ' created-by-me' if author_id == user.id |
| 1444 | 1479 |
s << ' assigned-to-me' if assigned_to_id == user.id |
| app/models/issue_query.rb | ||
|---|---|---|
| 305 | 305 |
:default_order => 'desc', |
| 306 | 306 |
:caption => :label_total_spent_time) |
| 307 | 307 |
) |
| 308 | ||
| 309 |
@available_columns.insert( |
|
| 310 |
index + 2, |
|
| 311 |
QueryColumn.new(:spent_time_ratio, |
|
| 312 |
:caption => "Spent time ratio") |
|
| 313 |
) |
|
| 314 | ||
| 315 |
@available_columns.insert( |
|
| 316 |
index + 3, |
|
| 317 |
QueryColumn.new(:total_spent_time_ratio, |
|
| 318 |
:caption => "Total spent time ratio") |
|
| 319 |
) |
|
| 308 | 320 |
end |
| 309 | 321 | |
| 310 | 322 |
if User.current.allowed_to?(:set_issues_private, nil, :global => true) || |
| ... | ... | |
| 399 | 411 |
if has_column?(:total_spent_hours) |
| 400 | 412 |
Issue.load_visible_total_spent_hours(issues) |
| 401 | 413 |
end |
| 414 |
if has_column?(:spent_time_ratio) |
|
| 415 |
Issue.load_visible_spent_hours(issues) |
|
| 416 |
end |
|
| 417 |
if has_column?(:total_spent_time_ratio) |
|
| 418 |
Issue.load_visible_total_spent_hours(issues) |
|
| 419 |
end |
|
| 402 | 420 |
if has_column?(:last_updated_by) |
| 403 | 421 |
Issue.load_visible_last_updated_by(issues) |
| 404 | 422 |
end |
| app/models/version.rb | ||
|---|---|---|
| 241 | 241 |
@spent_hours ||= TimeEntry.joins(:issue).where("#{Issue.table_name}.fixed_version_id = ?", id).sum(:hours).to_f
|
| 242 | 242 |
end |
| 243 | 243 | |
| 244 |
def spent_time_ratio |
|
| 245 |
if (estimated_hours.present? && estimated_hours > 0) |
|
| 246 |
if (spent_hours.present? && spent_hours == 0) |
|
| 247 |
0.to_f.round(2) |
|
| 248 |
elsif (spent_hours.present? && spent_hours > 0) |
|
| 249 |
spent_time_ratio = spent_hours.to_f / estimated_hours.to_f * 100.0 |
|
| 250 |
spent_time_ratio.round(2) |
|
| 251 |
end |
|
| 252 |
end |
|
| 253 |
end |
|
| 254 | ||
| 244 | 255 |
def closed? |
| 245 | 256 |
status == 'closed' |
| 246 | 257 |
end |
| app/views/issues/show.html.erb | ||
|---|---|---|
| 73 | 73 |
end |
| 74 | 74 |
if User.current.allowed_to?(:view_time_entries, @project) && @issue.total_spent_hours > 0 |
| 75 | 75 |
rows.right l(:label_spent_time), issue_spent_hours_details(@issue), :class => 'spent-time' |
| 76 |
if spent_time_ratio_details(@issue).present? |
|
| 77 |
rows.right 'Spent time ratio', spent_time_ratio_details(@issue), :class => 'spent-time-ratio' |
|
| 78 |
end |
|
| 76 | 79 |
end |
| 77 | 80 |
end %> |
| 78 | 81 |
<%= render_half_width_custom_fields_rows(@issue) %> |
| app/views/versions/show.html.erb | ||
|---|---|---|
| 28 | 28 |
<td class="total-hours"><%= link_to html_hours(l_hours(@version.spent_hours)), |
| 29 | 29 |
project_time_entries_path(@version.project, :set_filter => 1, :"issue.fixed_version_id" => @version.id) %></td> |
| 30 | 30 |
</tr> |
| 31 |
<% if !@version.spent_time_ratio.nil? %> |
|
| 32 |
<tr> |
|
| 33 |
<th><%= 'Spent time ratio' %></th> |
|
| 34 |
<% overspent_class = @version.spent_time_ratio > 100 ? 'overspent' : '' %> |
|
| 35 |
<td class="spent-time-ratio <%= overspent_class %>"><%= @version.spent_time_ratio.to_s(:percentage, precision: 2) %></td> |
|
| 36 |
</tr> |
|
| 37 |
<% end %> |
|
| 31 | 38 |
<% end %> |
| 32 | 39 |
</table> |
| 33 | 40 |
</fieldset> |
| public/stylesheets/application.css | ||
|---|---|---|
| 263 | 263 |
table.list td.reorder {width:15%; white-space:nowrap; text-align:center; }
|
| 264 | 264 |
table.list table.progress td {padding-right:0px;}
|
| 265 | 265 |
table.list caption { text-align: left; padding: 0.5em 0.5em 0.5em 0; }
|
| 266 |
table.list tr.overdue td.due_date { color: #c22; }
|
|
| 266 |
table.list tr.overdue td.due_date, |
|
| 267 |
table.list tr.overspent td.spent_time_ratio, |
|
| 268 |
table.list tr.total-overspent td.total_spent_time_ratio, |
|
| 269 |
div#version-summary td.spent-time-ratio.overspent { color: #c22; }
|
|
| 267 | 270 |
#role-permissions-trackers table.list th {white-space:normal;}
|
| 268 | 271 | |
| 269 | 272 |
.table-list-cell {display: table-cell; vertical-align: top; padding:2px; }
|
| ... | ... | |
| 540 | 543 |
div.issue .attributes .attribute {padding-left:180px; clear:left; min-height: 1.8em;}
|
| 541 | 544 |
div.issue .attributes .attribute .label {width: 170px; margin-left:-180px; font-weight:bold; float:left; overflow:hidden; text-overflow: ellipsis;}
|
| 542 | 545 |
div.issue .attribute .value {overflow:auto; text-overflow: ellipsis;}
|
| 543 |
div.issue.overdue .due-date .value { color: #c22; }
|
|
| 546 |
div.issue.overdue .due-date .value, |
|
| 547 |
div.issue.overspent .spent-time-ratio.attribute .value span.spent-time-ratio-value, |
|
| 548 |
div.issue.total-overpent .spent-time-ratio.attribute .value, span.total-spent-time-ratio-value { color: #c22; }
|
|
| 544 | 549 |
body.controller-issues h2.inline-flex {padding-right: 0}
|
| 545 | 550 | |
| 546 | 551 |
#issue_tree table.issues, #relations table.issues { border: 0; }
|
| ... | ... | |
| 643 | 648 |
div#version-summary { float:right; width:28%; margin-left: 16px; margin-bottom: 16px; background-color: #fff; }
|
| 644 | 649 |
div#version-summary fieldset { margin-bottom: 1em; }
|
| 645 | 650 |
div#version-summary fieldset.time-tracking table { width:100%; }
|
| 646 |
div#version-summary th, div#version-summary td.total-hours { text-align: right; }
|
|
| 651 |
div#version-summary th, |
|
| 652 |
div#version-summary td.total-hours, |
|
| 653 |
div#version-summary td.spent-time-ratio { text-align: right; }
|
|
| 647 | 654 | |
| 648 | 655 |
table#time-report td.hours, table#time-report th.period, table#time-report th.total { text-align: right; padding-right: 0.5em; }
|
| 649 | 656 |
table#time-report tbody tr.subtotal { font-style: italic; color:#777;}
|
| ... | ... | |
| 725 | 732 |
ul.properties li {list-style-type:none;}
|
| 726 | 733 |
ul.properties li span {font-style:italic;}
|
| 727 | 734 | |
| 728 |
.total-hours { font-size: 110%; font-weight: bold; }
|
|
| 735 |
.total-hours, |
|
| 736 |
div#version-summary .spent-time-ratio { font-size: 110%; font-weight: bold; }
|
|
| 729 | 737 |
.total-hours span.hours-int { font-size: 120%; }
|
| 730 | 738 | |
| 731 | 739 |
.autoscroll {overflow-x: auto; padding:1px; margin-bottom: 1.2em; position: relative;}
|