Patch #1582 » add_time_spent_column_to_issues_withpermissions.patch
app/controllers/issues_controller.rb | ||
---|---|---|
77 | 77 |
@issues = @query.issues(:include => [:assigned_to, :tracker, :priority, :category, :fixed_version], |
78 | 78 |
:order => sort_clause, |
79 | 79 |
:offset => @issue_pages.current.offset, |
80 |
:limit => limit) |
|
80 |
:limit => limit, |
|
81 |
:joins => 'LEFT OUTER JOIN time_entries ON issues.id = time_entries.issue_id', |
|
82 |
:group => 'issues.id' |
|
83 |
) |
|
81 | 84 |
@issue_count_by_group = @query.issue_count_by_group |
82 | 85 |
|
83 | 86 |
respond_to do |format| |
app/helpers/issues_helper.rb | ||
---|---|---|
182 | 182 |
ic = Iconv.new(l(:general_csv_encoding), 'UTF-8') |
183 | 183 |
decimal_separator = l(:general_csv_decimal_separator) |
184 | 184 |
export = FCSV.generate(:col_sep => l(:general_csv_separator)) do |csv| |
185 |
# csv header fields |
|
186 |
headers = [ "#", |
|
187 |
l(:field_status), |
|
188 |
l(:field_project), |
|
189 |
l(:field_tracker), |
|
190 |
l(:field_priority), |
|
191 |
l(:field_subject), |
|
192 |
l(:field_assigned_to), |
|
193 |
l(:field_category), |
|
194 |
l(:field_fixed_version), |
|
195 |
l(:field_author), |
|
196 |
l(:field_start_date), |
|
197 |
l(:field_due_date), |
|
198 |
l(:field_done_ratio), |
|
199 |
l(:field_estimated_hours), |
|
200 |
l(:field_parent_issue), |
|
201 |
l(:field_created_on), |
|
202 |
l(:field_updated_on) |
|
203 |
] |
|
185 |
if User.current.allowed_to?(:view_time_entries, @project) |
|
186 |
#if !@project.nil? || !(User.current.allowed_to?(:view_time_entries, @project)) |
|
187 |
# csv header fields |
|
188 |
headers = [ "#", |
|
189 |
l(:field_status), |
|
190 |
l(:field_project), |
|
191 |
l(:field_tracker), |
|
192 |
l(:field_priority), |
|
193 |
l(:field_subject), |
|
194 |
l(:field_assigned_to), |
|
195 |
l(:field_category), |
|
196 |
l(:field_fixed_version), |
|
197 |
l(:field_author), |
|
198 |
l(:field_start_date), |
|
199 |
l(:field_due_date), |
|
200 |
l(:field_done_ratio), |
|
201 |
l(:field_estimated_hours), |
|
202 |
l(:field_spent_hours), |
|
203 |
l(:field_parent_issue), |
|
204 |
l(:field_created_on), |
|
205 |
l(:field_updated_on) |
|
206 |
] |
|
207 |
else |
|
208 |
# csv header fields |
|
209 |
headers = [ "#", |
|
210 |
l(:field_status), |
|
211 |
l(:field_project), |
|
212 |
l(:field_tracker), |
|
213 |
l(:field_priority), |
|
214 |
l(:field_subject), |
|
215 |
l(:field_assigned_to), |
|
216 |
l(:field_category), |
|
217 |
l(:field_fixed_version), |
|
218 |
l(:field_author), |
|
219 |
l(:field_start_date), |
|
220 |
l(:field_due_date), |
|
221 |
l(:field_done_ratio), |
|
222 |
l(:field_estimated_hours), |
|
223 |
l(:field_parent_issue), |
|
224 |
l(:field_created_on), |
|
225 |
l(:field_updated_on) |
|
226 |
] |
|
227 |
end |
|
204 | 228 |
# Export project custom fields if project is given |
205 | 229 |
# otherwise export custom fields marked as "For all projects" |
206 | 230 |
custom_fields = project.nil? ? IssueCustomField.for_all : project.all_issue_custom_fields |
... | ... | |
210 | 234 |
csv << headers.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end } |
211 | 235 |
# csv lines |
212 | 236 |
issues.each do |issue| |
237 |
if User.current.allowed_to?(:view_time_entries, @project) |
|
213 | 238 |
fields = [issue.id, |
214 | 239 |
issue.status.name, |
215 | 240 |
issue.project.name, |
... | ... | |
224 | 249 |
format_date(issue.due_date), |
225 | 250 |
issue.done_ratio, |
226 | 251 |
issue.estimated_hours.to_s.gsub('.', decimal_separator), |
252 |
issue.spent_hours.to_s.gsub('.', decimal_separator), |
|
227 | 253 |
issue.parent_id, |
228 | 254 |
format_time(issue.created_on), |
229 | 255 |
format_time(issue.updated_on) |
230 | 256 |
] |
257 |
else |
|
258 |
fields = [issue.id, |
|
259 |
issue.status.name, |
|
260 |
issue.project.name, |
|
261 |
issue.tracker.name, |
|
262 |
issue.priority.name, |
|
263 |
issue.subject, |
|
264 |
issue.assigned_to, |
|
265 |
issue.category, |
|
266 |
issue.fixed_version, |
|
267 |
issue.author.name, |
|
268 |
format_date(issue.start_date), |
|
269 |
format_date(issue.due_date), |
|
270 |
issue.done_ratio, |
|
271 |
issue.estimated_hours.to_s.gsub('.', decimal_separator), |
|
272 |
issue.parent_id, |
|
273 |
format_time(issue.created_on), |
|
274 |
format_time(issue.updated_on) |
|
275 |
] |
|
276 |
end |
|
277 |
|
|
231 | 278 |
custom_fields.each {|f| fields << show_value(issue.custom_value_for(f)) } |
232 | 279 |
fields << issue.description |
233 | 280 |
csv << fields.collect {|c| begin; ic.iconv(c.to_s); rescue; c.to_s; end } |
app/helpers/queries_helper.rb | ||
---|---|---|
44 | 44 |
when 'Fixnum', 'Float' |
45 | 45 |
if column.name == :done_ratio |
46 | 46 |
progress_bar(value, :width => '80px') |
47 |
else |
|
47 |
elsif column.name == :spent_hours |
|
48 |
return issue.send :spent_hours_colored, value |
|
49 |
else |
|
48 | 50 |
value.to_s |
49 | 51 |
end |
50 | 52 |
when 'User' |
... | ... | |
53 | 55 |
link_to_project value |
54 | 56 |
when 'Version' |
55 | 57 |
link_to(h(value), :controller => 'versions', :action => 'show', :id => value) |
58 |
# when 'spentHours' |
|
59 |
# return issue.send : spent_hours_colored, value |
|
56 | 60 |
when 'TrueClass' |
57 | 61 |
l(:general_text_Yes) |
58 | 62 |
when 'FalseClass' |
app/models/issue.rb | ||
---|---|---|
196 | 196 |
def estimated_hours=(h) |
197 | 197 |
write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h) |
198 | 198 |
end |
199 | ||
200 |
def spent_hours |
|
201 |
#write_attribute :estimated_hours, (h.is_a?(String) ? h.to_hours : h) |
|
202 |
@spent_hours ||= (time_entries.sum(:hours)*100).round.to_f / 100 || 0 |
|
203 |
end |
|
204 | ||
205 |
def spent_hours_colored(value) |
|
206 |
if (estimated_hours.nil?) |
|
207 |
return ('%.2f' % value).to_s |
|
208 |
elsif(value > estimated_hours) |
|
209 |
return "<FONT COLOR='RED'>"+('%.2f' % value).to_s+"</FONT>" |
|
210 |
else return ('%.2f' % value).to_s |
|
211 |
end |
|
212 |
end |
|
199 | 213 |
|
200 | 214 |
SAFE_ATTRIBUTES = %w( |
201 | 215 |
tracker_id |
app/models/query.rb | ||
---|---|---|
133 | 133 |
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date"), |
134 | 134 |
QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date"), |
135 | 135 |
QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours"), |
136 |
QueryColumn.new(:spent_hours, :sortable => "SUM(time_entries.hours)" ), |
|
136 | 137 |
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true), |
137 | 138 |
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'), |
138 | 139 |
] |
app/views/issues/_list.rhtml | ||
---|---|---|
8 | 8 |
</th> |
9 | 9 |
<%= sort_header_tag('id', :caption => '#', :default_order => 'desc') %> |
10 | 10 |
<% query.columns.each do |column| %> |
11 |
<%= column_header(column) %> |
|
11 |
<% if column.name == :spent_hours %> |
|
12 |
<% if User.current.allowed_to?(:view_time_entries, @project) %> |
|
13 |
<%= column_header(column) %> |
|
14 |
<% end %> |
|
15 |
<% else %> |
|
16 |
<%= column_header(column) %> |
|
17 |
<% end %> |
|
12 | 18 |
<% end %> |
13 | 19 |
</tr></thead> |
14 | 20 |
<% previous_group = false %> |
... | ... | |
27 | 33 |
<tr id="issue-<%= issue.id %>" class="hascontextmenu <%= cycle('odd', 'even') %> <%= issue.css_classes %> <%= level > 0 ? "idnt idnt-#{level}" : nil %>"> |
28 | 34 |
<td class="checkbox"><%= check_box_tag("ids[]", issue.id, false, :id => nil) %></td> |
29 | 35 |
<td class="id"><%= link_to issue.id, :controller => 'issues', :action => 'show', :id => issue %></td> |
30 |
<% query.columns.each do |column| %><%= content_tag 'td', column_content(column, issue), :class => column.name %><% end %> |
|
36 |
<% query.columns.each do |column| %> |
|
37 |
<% if column.name == :spent_hours %> |
|
38 |
<% if User.current.allowed_to?(:view_time_entries, @project) %> |
|
39 |
<%= content_tag 'td', column_content(column, issue) , :class => column.name %> |
|
40 |
<% end %> |
|
41 |
<% else %> |
|
42 |
<%= content_tag 'td', column_content(column, issue), :class => column.name %> |
|
43 |
<% end %> |
|
44 |
<% end %> |
|
31 | 45 |
</tr> |
32 | 46 |
<% end -%> |
33 | 47 |
</tbody> |
app/views/issues/index.rhtml | ||
---|---|---|
66 | 66 |
<% other_formats_links do |f| %> |
67 | 67 |
<%= f.link_to 'Atom', :url => { :project_id => @project, :query_id => (@query.new_record? ? nil : @query), :key => User.current.rss_key } %> |
68 | 68 |
<%= f.link_to 'CSV', :url => { :project_id => @project } %> |
69 |
<%= f.link_to 'PDF', :url => { :project_id => @project } %> |
|
69 |
<% if User.current.allowed_to?(:view_time_entries, @project) %> |
|
70 |
<%= f.link_to 'PDF', :url => { :project_id => @project } %> |
|
71 |
<% end %> |
|
70 | 72 |
<% end %> |
71 | 73 | |
72 | 74 |
<% end %> |
config/locales/en.yml | ||
---|---|---|
213 | 213 |
field_author: Author |
214 | 214 |
field_created_on: Created |
215 | 215 |
field_updated_on: Updated |
216 |
field_spent_hours: Spent hours |
|
216 | 217 |
field_field_format: Format |
217 | 218 |
field_is_for_all: For all projects |
218 | 219 |
field_possible_values: Possible values |
config/locales/fr.yml | ||
---|---|---|
224 | 224 |
field_author: Auteur |
225 | 225 |
field_created_on: "Créé " |
226 | 226 |
field_updated_on: "Mis-à-jour " |
227 |
field_spent_hours: "Temps passé" |
|
227 | 228 |
field_field_format: Format |
228 | 229 |
field_is_for_all: Pour tous les projets |
229 | 230 |
field_possible_values: Valeurs possibles |