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;} |