Revision 4232
Added by Eric Davis over 14 years ago
trunk/app/controllers/time_entry_reports_controller.rb | ||
---|---|---|
1 |
class TimeEntryReportsController < ApplicationController |
|
2 |
menu_item :issues |
|
3 |
before_filter :find_optional_project |
|
4 |
before_filter :load_available_criterias |
|
5 |
|
|
6 |
helper :sort |
|
7 |
include SortHelper |
|
8 |
helper :issues |
|
9 |
helper :timelog |
|
10 |
include TimelogHelper |
|
11 |
helper :custom_fields |
|
12 |
include CustomFieldsHelper |
|
13 |
|
|
14 |
def report |
|
15 |
@criterias = params[:criterias] || [] |
|
16 |
@criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria} |
|
17 |
@criterias.uniq! |
|
18 |
@criterias = @criterias[0,3] |
|
19 |
|
|
20 |
@columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month' |
|
21 |
|
|
22 |
retrieve_date_range |
|
23 |
|
|
24 |
unless @criterias.empty? |
|
25 |
sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ') |
|
26 |
sql_group_by = @criterias.collect{|criteria| @available_criterias[criteria][:sql]}.join(', ') |
|
27 |
sql_condition = '' |
|
28 |
|
|
29 |
if @project.nil? |
|
30 |
sql_condition = Project.allowed_to_condition(User.current, :view_time_entries) |
|
31 |
elsif @issue.nil? |
|
32 |
sql_condition = @project.project_condition(Setting.display_subprojects_issues?) |
|
33 |
else |
|
34 |
sql_condition = "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}" |
|
35 |
end |
|
36 |
|
|
37 |
sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours" |
|
38 |
sql << " FROM #{TimeEntry.table_name}" |
|
39 |
sql << time_report_joins |
|
40 |
sql << " WHERE" |
|
41 |
sql << " (%s) AND" % sql_condition |
|
42 |
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)] |
|
43 |
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on" |
|
44 |
|
|
45 |
@hours = ActiveRecord::Base.connection.select_all(sql) |
|
46 |
|
|
47 |
@hours.each do |row| |
|
48 |
case @columns |
|
49 |
when 'year' |
|
50 |
row['year'] = row['tyear'] |
|
51 |
when 'month' |
|
52 |
row['month'] = "#{row['tyear']}-#{row['tmonth']}" |
|
53 |
when 'week' |
|
54 |
row['week'] = "#{row['tyear']}-#{row['tweek']}" |
|
55 |
when 'day' |
|
56 |
row['day'] = "#{row['spent_on']}" |
|
57 |
end |
|
58 |
end |
|
59 |
|
|
60 |
@total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f} |
|
61 |
|
|
62 |
@periods = [] |
|
63 |
# Date#at_beginning_of_ not supported in Rails 1.2.x |
|
64 |
date_from = @from.to_time |
|
65 |
# 100 columns max |
|
66 |
while date_from <= @to.to_time && @periods.length < 100 |
|
67 |
case @columns |
|
68 |
when 'year' |
|
69 |
@periods << "#{date_from.year}" |
|
70 |
date_from = (date_from + 1.year).at_beginning_of_year |
|
71 |
when 'month' |
|
72 |
@periods << "#{date_from.year}-#{date_from.month}" |
|
73 |
date_from = (date_from + 1.month).at_beginning_of_month |
|
74 |
when 'week' |
|
75 |
@periods << "#{date_from.year}-#{date_from.to_date.cweek}" |
|
76 |
date_from = (date_from + 7.day).at_beginning_of_week |
|
77 |
when 'day' |
|
78 |
@periods << "#{date_from.to_date}" |
|
79 |
date_from = date_from + 1.day |
|
80 |
end |
|
81 |
end |
|
82 |
end |
|
83 |
|
|
84 |
respond_to do |format| |
|
85 |
format.html { render :layout => !request.xhr? } |
|
86 |
format.csv { send_data(report_to_csv(@criterias, @periods, @hours), :type => 'text/csv; header=present', :filename => 'timelog.csv') } |
|
87 |
end |
|
88 |
end |
|
89 |
|
|
90 |
private |
|
91 |
|
|
92 |
# TODO: duplicated in TimelogController |
|
93 |
def find_optional_project |
|
94 |
if !params[:issue_id].blank? |
|
95 |
@issue = Issue.find(params[:issue_id]) |
|
96 |
@project = @issue.project |
|
97 |
elsif !params[:project_id].blank? |
|
98 |
@project = Project.find(params[:project_id]) |
|
99 |
end |
|
100 |
deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true) |
|
101 |
end |
|
102 |
|
|
103 |
# Retrieves the date range based on predefined ranges or specific from/to param dates |
|
104 |
# TODO: duplicated in TimelogController |
|
105 |
def retrieve_date_range |
|
106 |
@free_period = false |
|
107 |
@from, @to = nil, nil |
|
108 |
|
|
109 |
if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?) |
|
110 |
case params[:period].to_s |
|
111 |
when 'today' |
|
112 |
@from = @to = Date.today |
|
113 |
when 'yesterday' |
|
114 |
@from = @to = Date.today - 1 |
|
115 |
when 'current_week' |
|
116 |
@from = Date.today - (Date.today.cwday - 1)%7 |
|
117 |
@to = @from + 6 |
|
118 |
when 'last_week' |
|
119 |
@from = Date.today - 7 - (Date.today.cwday - 1)%7 |
|
120 |
@to = @from + 6 |
|
121 |
when '7_days' |
|
122 |
@from = Date.today - 7 |
|
123 |
@to = Date.today |
|
124 |
when 'current_month' |
|
125 |
@from = Date.civil(Date.today.year, Date.today.month, 1) |
|
126 |
@to = (@from >> 1) - 1 |
|
127 |
when 'last_month' |
|
128 |
@from = Date.civil(Date.today.year, Date.today.month, 1) << 1 |
|
129 |
@to = (@from >> 1) - 1 |
|
130 |
when '30_days' |
|
131 |
@from = Date.today - 30 |
|
132 |
@to = Date.today |
|
133 |
when 'current_year' |
|
134 |
@from = Date.civil(Date.today.year, 1, 1) |
|
135 |
@to = Date.civil(Date.today.year, 12, 31) |
|
136 |
end |
|
137 |
elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?)) |
|
138 |
begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end |
|
139 |
begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end |
|
140 |
@free_period = true |
|
141 |
else |
|
142 |
# default |
|
143 |
end |
|
144 |
|
|
145 |
@from, @to = @to, @from if @from && @to && @from > @to |
|
146 |
@from ||= (TimeEntry.earilest_date_for_project(@project) || Date.today) |
|
147 |
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today) |
|
148 |
end |
|
149 |
|
|
150 |
def load_available_criterias |
|
151 |
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id", |
|
152 |
:klass => Project, |
|
153 |
:label => :label_project}, |
|
154 |
'version' => {:sql => "#{Issue.table_name}.fixed_version_id", |
|
155 |
:klass => Version, |
|
156 |
:label => :label_version}, |
|
157 |
'category' => {:sql => "#{Issue.table_name}.category_id", |
|
158 |
:klass => IssueCategory, |
|
159 |
:label => :field_category}, |
|
160 |
'member' => {:sql => "#{TimeEntry.table_name}.user_id", |
|
161 |
:klass => User, |
|
162 |
:label => :label_member}, |
|
163 |
'tracker' => {:sql => "#{Issue.table_name}.tracker_id", |
|
164 |
:klass => Tracker, |
|
165 |
:label => :label_tracker}, |
|
166 |
'activity' => {:sql => "#{TimeEntry.table_name}.activity_id", |
|
167 |
:klass => TimeEntryActivity, |
|
168 |
:label => :label_activity}, |
|
169 |
'issue' => {:sql => "#{TimeEntry.table_name}.issue_id", |
|
170 |
:klass => Issue, |
|
171 |
:label => :label_issue} |
|
172 |
} |
|
173 |
|
|
174 |
# Add list and boolean custom fields as available criterias |
|
175 |
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields) |
|
176 |
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf| |
|
177 |
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id)", |
|
178 |
:format => cf.field_format, |
|
179 |
:label => cf.name} |
|
180 |
end if @project |
|
181 |
|
|
182 |
# Add list and boolean time entry custom fields |
|
183 |
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf| |
|
184 |
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id)", |
|
185 |
:format => cf.field_format, |
|
186 |
:label => cf.name} |
|
187 |
end |
|
188 |
|
|
189 |
# Add list and boolean time entry activity custom fields |
|
190 |
TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf| |
|
191 |
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id)", |
|
192 |
:format => cf.field_format, |
|
193 |
:label => cf.name} |
|
194 |
end |
|
195 |
|
|
196 |
call_hook(:controller_timelog_available_criterias, { :available_criterias => @available_criterias, :project => @project }) |
|
197 |
@available_criterias |
|
198 |
end |
|
199 |
|
|
200 |
def time_report_joins |
|
201 |
sql = '' |
|
202 |
sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id" |
|
203 |
sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id" |
|
204 |
# TODO: rename hook |
|
205 |
call_hook(:controller_timelog_time_report_joins, {:sql => sql} ) |
|
206 |
sql |
|
207 |
end |
|
208 |
|
|
209 |
end |
trunk/app/controllers/timelog_controller.rb | ||
---|---|---|
18 | 18 |
class TimelogController < ApplicationController |
19 | 19 |
menu_item :issues |
20 | 20 |
before_filter :find_project, :authorize, :only => [:edit, :destroy] |
21 |
before_filter :find_optional_project, :only => [:report, :details] |
|
22 |
before_filter :load_available_criterias, :only => [:report] |
|
21 |
before_filter :find_optional_project, :only => [:details] |
|
23 | 22 |
|
24 | 23 |
verify :method => :post, :only => :destroy, :redirect_to => { :action => :details } |
25 | 24 |
|
... | ... | |
30 | 29 |
helper :custom_fields |
31 | 30 |
include CustomFieldsHelper |
32 | 31 |
|
33 |
def report |
|
34 |
@criterias = params[:criterias] || [] |
|
35 |
@criterias = @criterias.select{|criteria| @available_criterias.has_key? criteria} |
|
36 |
@criterias.uniq! |
|
37 |
@criterias = @criterias[0,3] |
|
38 |
|
|
39 |
@columns = (params[:columns] && %w(year month week day).include?(params[:columns])) ? params[:columns] : 'month' |
|
40 |
|
|
41 |
retrieve_date_range |
|
42 |
|
|
43 |
unless @criterias.empty? |
|
44 |
sql_select = @criterias.collect{|criteria| @available_criterias[criteria][:sql] + " AS " + criteria}.join(', ') |
|
45 |
sql_group_by = @criterias.collect{|criteria| @available_criterias[criteria][:sql]}.join(', ') |
|
46 |
sql_condition = '' |
|
47 |
|
|
48 |
if @project.nil? |
|
49 |
sql_condition = Project.allowed_to_condition(User.current, :view_time_entries) |
|
50 |
elsif @issue.nil? |
|
51 |
sql_condition = @project.project_condition(Setting.display_subprojects_issues?) |
|
52 |
else |
|
53 |
sql_condition = "#{Issue.table_name}.root_id = #{@issue.root_id} AND #{Issue.table_name}.lft >= #{@issue.lft} AND #{Issue.table_name}.rgt <= #{@issue.rgt}" |
|
54 |
end |
|
55 |
|
|
56 |
sql = "SELECT #{sql_select}, tyear, tmonth, tweek, spent_on, SUM(hours) AS hours" |
|
57 |
sql << " FROM #{TimeEntry.table_name}" |
|
58 |
sql << time_report_joins |
|
59 |
sql << " WHERE" |
|
60 |
sql << " (%s) AND" % sql_condition |
|
61 |
sql << " (spent_on BETWEEN '%s' AND '%s')" % [ActiveRecord::Base.connection.quoted_date(@from), ActiveRecord::Base.connection.quoted_date(@to)] |
|
62 |
sql << " GROUP BY #{sql_group_by}, tyear, tmonth, tweek, spent_on" |
|
63 |
|
|
64 |
@hours = ActiveRecord::Base.connection.select_all(sql) |
|
65 |
|
|
66 |
@hours.each do |row| |
|
67 |
case @columns |
|
68 |
when 'year' |
|
69 |
row['year'] = row['tyear'] |
|
70 |
when 'month' |
|
71 |
row['month'] = "#{row['tyear']}-#{row['tmonth']}" |
|
72 |
when 'week' |
|
73 |
row['week'] = "#{row['tyear']}-#{row['tweek']}" |
|
74 |
when 'day' |
|
75 |
row['day'] = "#{row['spent_on']}" |
|
76 |
end |
|
77 |
end |
|
78 |
|
|
79 |
@total_hours = @hours.inject(0) {|s,k| s = s + k['hours'].to_f} |
|
80 |
|
|
81 |
@periods = [] |
|
82 |
# Date#at_beginning_of_ not supported in Rails 1.2.x |
|
83 |
date_from = @from.to_time |
|
84 |
# 100 columns max |
|
85 |
while date_from <= @to.to_time && @periods.length < 100 |
|
86 |
case @columns |
|
87 |
when 'year' |
|
88 |
@periods << "#{date_from.year}" |
|
89 |
date_from = (date_from + 1.year).at_beginning_of_year |
|
90 |
when 'month' |
|
91 |
@periods << "#{date_from.year}-#{date_from.month}" |
|
92 |
date_from = (date_from + 1.month).at_beginning_of_month |
|
93 |
when 'week' |
|
94 |
@periods << "#{date_from.year}-#{date_from.to_date.cweek}" |
|
95 |
date_from = (date_from + 7.day).at_beginning_of_week |
|
96 |
when 'day' |
|
97 |
@periods << "#{date_from.to_date}" |
|
98 |
date_from = date_from + 1.day |
|
99 |
end |
|
100 |
end |
|
101 |
end |
|
102 |
|
|
103 |
respond_to do |format| |
|
104 |
format.html { render :layout => !request.xhr? } |
|
105 |
format.csv { send_data(report_to_csv(@criterias, @periods, @hours), :type => 'text/csv; header=present', :filename => 'timelog.csv') } |
|
106 |
end |
|
107 |
end |
|
108 |
|
|
109 | 32 |
def details |
110 | 33 |
sort_init 'spent_on', 'desc' |
111 | 34 |
sort_update 'spent_on' => 'spent_on', |
... | ... | |
264 | 187 |
@to ||= (TimeEntry.latest_date_for_project(@project) || Date.today) |
265 | 188 |
end |
266 | 189 |
|
267 |
def load_available_criterias |
|
268 |
@available_criterias = { 'project' => {:sql => "#{TimeEntry.table_name}.project_id", |
|
269 |
:klass => Project, |
|
270 |
:label => :label_project}, |
|
271 |
'version' => {:sql => "#{Issue.table_name}.fixed_version_id", |
|
272 |
:klass => Version, |
|
273 |
:label => :label_version}, |
|
274 |
'category' => {:sql => "#{Issue.table_name}.category_id", |
|
275 |
:klass => IssueCategory, |
|
276 |
:label => :field_category}, |
|
277 |
'member' => {:sql => "#{TimeEntry.table_name}.user_id", |
|
278 |
:klass => User, |
|
279 |
:label => :label_member}, |
|
280 |
'tracker' => {:sql => "#{Issue.table_name}.tracker_id", |
|
281 |
:klass => Tracker, |
|
282 |
:label => :label_tracker}, |
|
283 |
'activity' => {:sql => "#{TimeEntry.table_name}.activity_id", |
|
284 |
:klass => TimeEntryActivity, |
|
285 |
:label => :label_activity}, |
|
286 |
'issue' => {:sql => "#{TimeEntry.table_name}.issue_id", |
|
287 |
:klass => Issue, |
|
288 |
:label => :label_issue} |
|
289 |
} |
|
290 |
|
|
291 |
# Add list and boolean custom fields as available criterias |
|
292 |
custom_fields = (@project.nil? ? IssueCustomField.for_all : @project.all_issue_custom_fields) |
|
293 |
custom_fields.select {|cf| %w(list bool).include? cf.field_format }.each do |cf| |
|
294 |
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Issue' AND c.customized_id = #{Issue.table_name}.id)", |
|
295 |
:format => cf.field_format, |
|
296 |
:label => cf.name} |
|
297 |
end if @project |
|
298 |
|
|
299 |
# Add list and boolean time entry custom fields |
|
300 |
TimeEntryCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf| |
|
301 |
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'TimeEntry' AND c.customized_id = #{TimeEntry.table_name}.id)", |
|
302 |
:format => cf.field_format, |
|
303 |
:label => cf.name} |
|
304 |
end |
|
305 |
|
|
306 |
# Add list and boolean time entry activity custom fields |
|
307 |
TimeEntryActivityCustomField.find(:all).select {|cf| %w(list bool).include? cf.field_format }.each do |cf| |
|
308 |
@available_criterias["cf_#{cf.id}"] = {:sql => "(SELECT c.value FROM #{CustomValue.table_name} c WHERE c.custom_field_id = #{cf.id} AND c.customized_type = 'Enumeration' AND c.customized_id = #{TimeEntry.table_name}.activity_id)", |
|
309 |
:format => cf.field_format, |
|
310 |
:label => cf.name} |
|
311 |
end |
|
312 |
|
|
313 |
call_hook(:controller_timelog_available_criterias, { :available_criterias => @available_criterias, :project => @project }) |
|
314 |
@available_criterias |
|
315 |
end |
|
316 |
|
|
317 |
def time_report_joins |
|
318 |
sql = '' |
|
319 |
sql << " LEFT JOIN #{Issue.table_name} ON #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id" |
|
320 |
sql << " LEFT JOIN #{Project.table_name} ON #{TimeEntry.table_name}.project_id = #{Project.table_name}.id" |
|
321 |
call_hook(:controller_timelog_time_report_joins, {:sql => sql} ) |
|
322 |
sql |
|
323 |
end |
|
324 | 190 |
end |
trunk/app/views/projects/show.rhtml | ||
---|---|---|
68 | 68 |
<h3><%= l(:label_spent_time) %></h3> |
69 | 69 |
<p><span class="icon icon-time"><%= l_hours(@total_hours) %></span></p> |
70 | 70 |
<p><%= link_to(l(:label_details), {:controller => 'timelog', :action => 'details', :project_id => @project}) %> | |
71 |
<%= link_to(l(:label_report), {:controller => 'timelog', :action => 'report', :project_id => @project}) %></p>
|
|
71 |
<%= link_to(l(:label_report), {:controller => 'time_entry_reports', :action => 'report', :project_id => @project}) %></p>
|
|
72 | 72 |
<% end %> |
73 | 73 |
<%= call_hook(:view_projects_show_sidebar_bottom, :project => @project) %> |
74 | 74 |
<% end %> |
trunk/app/views/time_entry_reports/_report_criteria.rhtml | ||
---|---|---|
1 |
<% @hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value| %> |
|
2 |
<% hours_for_value = select_hours(hours, criterias[level], value) -%> |
|
3 |
<% next if hours_for_value.empty? -%> |
|
4 |
<tr class="<%= cycle('odd', 'even') %> <%= 'last-level' unless criterias.length > level+1 %>"> |
|
5 |
<%= '<td></td>' * level %> |
|
6 |
<td><%= h(format_criteria_value(criterias[level], value)) %></td> |
|
7 |
<%= '<td></td>' * (criterias.length - level - 1) -%> |
|
8 |
<% total = 0 -%> |
|
9 |
<% @periods.each do |period| -%> |
|
10 |
<% sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s)); total += sum -%> |
|
11 |
<td class="hours"><%= html_hours("%.2f" % sum) if sum > 0 %></td> |
|
12 |
<% end -%> |
|
13 |
<td class="hours"><%= html_hours("%.2f" % total) if total > 0 %></td> |
|
14 |
</tr> |
|
15 |
<% if criterias.length > level+1 -%> |
|
16 |
<%= render(:partial => 'report_criteria', :locals => {:criterias => criterias, :hours => hours_for_value, :level => (level + 1)}) %> |
|
17 |
<% end -%> |
|
18 |
|
|
19 |
<% end %> |
|
0 | 20 |
trunk/app/views/time_entry_reports/report.rhtml | ||
---|---|---|
1 |
<div class="contextual"> |
|
2 |
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %> |
|
3 |
</div> |
|
4 |
|
|
5 |
<%= render_timelog_breadcrumb %> |
|
6 |
|
|
7 |
<h2><%= l(:label_spent_time) %></h2> |
|
8 |
|
|
9 |
<% form_remote_tag(:url => {}, :html => {:method => :get, :id => 'query_form'}, :method => :get, :update => 'content') do %> |
|
10 |
<% @criterias.each do |criteria| %> |
|
11 |
<%= hidden_field_tag 'criterias[]', criteria, :id => nil %> |
|
12 |
<% end %> |
|
13 |
<%# TODO: get rid of the project_id field, that should already be in the URL %> |
|
14 |
<%= hidden_field_tag('project_id', params[:project_id]) if @project %> |
|
15 |
<%= hidden_field_tag('issue_id', params[:issue_id]) if @issue %> |
|
16 |
<%= render :partial => 'timelog/date_range' %> |
|
17 |
|
|
18 |
<p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], |
|
19 |
[l(:label_month), 'month'], |
|
20 |
[l(:label_week), 'week'], |
|
21 |
[l(:label_day_plural).titleize, 'day']], @columns), |
|
22 |
:onchange => "this.form.onsubmit();" %> |
|
23 |
|
|
24 |
<%= l(:button_add) %>: <%= select_tag('criterias[]', options_for_select([[]] + (@available_criterias.keys - @criterias).collect{|k| [l_or_humanize(@available_criterias[k][:label]), k]}), |
|
25 |
:onchange => "this.form.onsubmit();", |
|
26 |
:style => 'width: 200px', |
|
27 |
:id => nil, |
|
28 |
:disabled => (@criterias.length >= 3)) %> |
|
29 |
<%= link_to_remote l(:button_clear), {:url => {:project_id => @project, :period_type => params[:period_type], :period => params[:period], :from => @from, :to => @to, :columns => @columns}, |
|
30 |
:method => :get, |
|
31 |
:update => 'content' |
|
32 |
}, :class => 'icon icon-reload' %></p> |
|
33 |
<% end %> |
|
34 |
|
|
35 |
<% unless @criterias.empty? %> |
|
36 |
<div class="total-hours"> |
|
37 |
<p><%= l(:label_total) %>: <%= html_hours(l_hours(@total_hours)) %></p> |
|
38 |
</div> |
|
39 |
|
|
40 |
<% unless @hours.empty? %> |
|
41 |
<table class="list" id="time-report"> |
|
42 |
<thead> |
|
43 |
<tr> |
|
44 |
<% @criterias.each do |criteria| %> |
|
45 |
<th><%= l_or_humanize(@available_criterias[criteria][:label]) %></th> |
|
46 |
<% end %> |
|
47 |
<% columns_width = (40 / (@periods.length+1)).to_i %> |
|
48 |
<% @periods.each do |period| %> |
|
49 |
<th class="period" width="<%= columns_width %>%"><%= period %></th> |
|
50 |
<% end %> |
|
51 |
<th class="total" width="<%= columns_width %>%"><%= l(:label_total) %></th> |
|
52 |
</tr> |
|
53 |
</thead> |
|
54 |
<tbody> |
|
55 |
<%= render :partial => 'report_criteria', :locals => {:criterias => @criterias, :hours => @hours, :level => 0} %> |
|
56 |
<tr class="total"> |
|
57 |
<td><%= l(:label_total) %></td> |
|
58 |
<%= '<td></td>' * (@criterias.size - 1) %> |
|
59 |
<% total = 0 -%> |
|
60 |
<% @periods.each do |period| -%> |
|
61 |
<% sum = sum_hours(select_hours(@hours, @columns, period.to_s)); total += sum -%> |
|
62 |
<td class="hours"><%= html_hours("%.2f" % sum) if sum > 0 %></td> |
|
63 |
<% end -%> |
|
64 |
<td class="hours"><%= html_hours("%.2f" % total) if total > 0 %></td> |
|
65 |
</tr> |
|
66 |
</tbody> |
|
67 |
</table> |
|
68 |
|
|
69 |
<% other_formats_links do |f| %> |
|
70 |
<%= f.link_to 'CSV', :url => params %> |
|
71 |
<% end %> |
|
72 |
<% end %> |
|
73 |
<% end %> |
|
74 |
|
|
75 |
<% html_title l(:label_spent_time), l(:label_report) %> |
|
76 |
|
|
0 | 77 |
trunk/app/views/timelog/report.rhtml | ||
---|---|---|
1 |
<div class="contextual"> |
|
2 |
<%= link_to_if_authorized l(:button_log_time), {:controller => 'timelog', :action => 'edit', :project_id => @project, :issue_id => @issue}, :class => 'icon icon-time-add' %> |
|
3 |
</div> |
|
4 |
|
|
5 |
<%= render_timelog_breadcrumb %> |
|
6 |
|
|
7 |
<h2><%= l(:label_spent_time) %></h2> |
|
8 |
|
|
9 |
<% form_remote_tag(:url => {}, :html => {:method => :get, :id => 'query_form'}, :method => :get, :update => 'content') do %> |
|
10 |
<% @criterias.each do |criteria| %> |
|
11 |
<%= hidden_field_tag 'criterias[]', criteria, :id => nil %> |
|
12 |
<% end %> |
|
13 |
<%# TODO: get rid of the project_id field, that should already be in the URL %> |
|
14 |
<%= hidden_field_tag('project_id', params[:project_id]) if @project %> |
|
15 |
<%= hidden_field_tag('issue_id', params[:issue_id]) if @issue %> |
|
16 |
<%= render :partial => 'date_range' %> |
|
17 |
|
|
18 |
<p><%= l(:label_details) %>: <%= select_tag 'columns', options_for_select([[l(:label_year), 'year'], |
|
19 |
[l(:label_month), 'month'], |
|
20 |
[l(:label_week), 'week'], |
|
21 |
[l(:label_day_plural).titleize, 'day']], @columns), |
|
22 |
:onchange => "this.form.onsubmit();" %> |
|
23 |
|
|
24 |
<%= l(:button_add) %>: <%= select_tag('criterias[]', options_for_select([[]] + (@available_criterias.keys - @criterias).collect{|k| [l_or_humanize(@available_criterias[k][:label]), k]}), |
|
25 |
:onchange => "this.form.onsubmit();", |
|
26 |
:style => 'width: 200px', |
|
27 |
:id => nil, |
|
28 |
:disabled => (@criterias.length >= 3)) %> |
|
29 |
<%= link_to_remote l(:button_clear), {:url => {:project_id => @project, :period_type => params[:period_type], :period => params[:period], :from => @from, :to => @to, :columns => @columns}, |
|
30 |
:method => :get, |
|
31 |
:update => 'content' |
|
32 |
}, :class => 'icon icon-reload' %></p> |
|
33 |
<% end %> |
|
34 |
|
|
35 |
<% unless @criterias.empty? %> |
|
36 |
<div class="total-hours"> |
|
37 |
<p><%= l(:label_total) %>: <%= html_hours(l_hours(@total_hours)) %></p> |
|
38 |
</div> |
|
39 |
|
|
40 |
<% unless @hours.empty? %> |
|
41 |
<table class="list" id="time-report"> |
|
42 |
<thead> |
|
43 |
<tr> |
|
44 |
<% @criterias.each do |criteria| %> |
|
45 |
<th><%= l_or_humanize(@available_criterias[criteria][:label]) %></th> |
|
46 |
<% end %> |
|
47 |
<% columns_width = (40 / (@periods.length+1)).to_i %> |
|
48 |
<% @periods.each do |period| %> |
|
49 |
<th class="period" width="<%= columns_width %>%"><%= period %></th> |
|
50 |
<% end %> |
|
51 |
<th class="total" width="<%= columns_width %>%"><%= l(:label_total) %></th> |
|
52 |
</tr> |
|
53 |
</thead> |
|
54 |
<tbody> |
|
55 |
<%= render :partial => 'report_criteria', :locals => {:criterias => @criterias, :hours => @hours, :level => 0} %> |
|
56 |
<tr class="total"> |
|
57 |
<td><%= l(:label_total) %></td> |
|
58 |
<%= '<td></td>' * (@criterias.size - 1) %> |
|
59 |
<% total = 0 -%> |
|
60 |
<% @periods.each do |period| -%> |
|
61 |
<% sum = sum_hours(select_hours(@hours, @columns, period.to_s)); total += sum -%> |
|
62 |
<td class="hours"><%= html_hours("%.2f" % sum) if sum > 0 %></td> |
|
63 |
<% end -%> |
|
64 |
<td class="hours"><%= html_hours("%.2f" % total) if total > 0 %></td> |
|
65 |
</tr> |
|
66 |
</tbody> |
|
67 |
</table> |
|
68 |
|
|
69 |
<% other_formats_links do |f| %> |
|
70 |
<%= f.link_to 'CSV', :url => params %> |
|
71 |
<% end %> |
|
72 |
<% end %> |
|
73 |
<% end %> |
|
74 |
|
|
75 |
<% html_title l(:label_spent_time), l(:label_report) %> |
|
76 |
|
|
77 | 0 |
trunk/app/views/timelog/_report_criteria.rhtml | ||
---|---|---|
1 |
<% @hours.collect {|h| h[criterias[level]].to_s}.uniq.each do |value| %> |
|
2 |
<% hours_for_value = select_hours(hours, criterias[level], value) -%> |
|
3 |
<% next if hours_for_value.empty? -%> |
|
4 |
<tr class="<%= cycle('odd', 'even') %> <%= 'last-level' unless criterias.length > level+1 %>"> |
|
5 |
<%= '<td></td>' * level %> |
|
6 |
<td><%= h(format_criteria_value(criterias[level], value)) %></td> |
|
7 |
<%= '<td></td>' * (criterias.length - level - 1) -%> |
|
8 |
<% total = 0 -%> |
|
9 |
<% @periods.each do |period| -%> |
|
10 |
<% sum = sum_hours(select_hours(hours_for_value, @columns, period.to_s)); total += sum -%> |
|
11 |
<td class="hours"><%= html_hours("%.2f" % sum) if sum > 0 %></td> |
|
12 |
<% end -%> |
|
13 |
<td class="hours"><%= html_hours("%.2f" % total) if total > 0 %></td> |
|
14 |
</tr> |
|
15 |
<% if criterias.length > level+1 -%> |
|
16 |
<%= render(:partial => 'report_criteria', :locals => {:criterias => criterias, :hours => hours_for_value, :level => (level + 1)}) %> |
|
17 |
<% end -%> |
|
18 |
|
|
19 |
<% end %> |
|
20 | 0 |
trunk/app/views/timelog/_date_range.rhtml | ||
---|---|---|
30 | 30 |
<ul> |
31 | 31 |
<li><%= link_to(l(:label_details), url_params.merge({:controller => 'timelog', :action => 'details', :project_id => @project, :issue_id => @issue }), |
32 | 32 |
:class => (@controller.action_name == 'details' ? 'selected' : nil)) %></li> |
33 |
<li><%= link_to(l(:label_report), url_params.merge({:controller => 'timelog', :action => 'report', :project_id => @project, :issue_id => @issue}),
|
|
33 |
<li><%= link_to(l(:label_report), url_params.merge({:controller => 'time_entry_reports', :action => 'report', :project_id => @project, :issue_id => @issue}),
|
|
34 | 34 |
:class => (@controller.action_name == 'report' ? 'selected' : nil)) %></li> |
35 | 35 |
</ul> |
36 | 36 |
</div> |
trunk/config/routes.rb | ||
---|---|---|
30 | 30 |
time_details.connect 'projects/:project_id/issues/:issue_id/time_entries' |
31 | 31 |
time_details.connect 'projects/:project_id/issues/:issue_id/time_entries.:format' |
32 | 32 |
end |
33 |
timelog.connect 'projects/:project_id/time_entries/report', :action => 'report' |
|
34 |
timelog.with_options :action => 'report',:conditions => {:method => :get} do |time_report| |
|
33 |
timelog.connect 'projects/:project_id/time_entries/report', :controller => 'time_entry_reports', :action => 'report'
|
|
34 |
timelog.with_options :controller => 'time_entry_reports', :action => 'report',:conditions => {:method => :get} do |time_report|
|
|
35 | 35 |
time_report.connect 'time_entries/report' |
36 | 36 |
time_report.connect 'time_entries/report.:format' |
37 | 37 |
time_report.connect 'projects/:project_id/time_entries/report.:format' |
trunk/lib/redmine.rb | ||
---|---|---|
85 | 85 |
|
86 | 86 |
map.project_module :time_tracking do |map| |
87 | 87 |
map.permission :log_time, {:timelog => :edit}, :require => :loggedin |
88 |
map.permission :view_time_entries, :timelog => [:details, :report]
|
|
88 |
map.permission :view_time_entries, :timelog => [:details], :time_entry_reports => [:report]
|
|
89 | 89 |
map.permission :edit_time_entries, {:timelog => [:edit, :destroy]}, :require => :member |
90 | 90 |
map.permission :edit_own_time_entries, {:timelog => [:edit, :destroy]}, :require => :loggedin |
91 | 91 |
map.permission :manage_project_activities, {:project_enumerations => [:update, :destroy]}, :require => :member |
trunk/test/functional/time_entry_reports_controller_test.rb | ||
---|---|---|
1 |
# -*- coding: utf-8 -*- |
|
2 |
require File.dirname(__FILE__) + '/../test_helper' |
|
3 |
|
|
4 |
class TimeEntryReportsControllerTest < ActionController::TestCase |
|
5 |
fixtures :projects, :enabled_modules, :roles, :members, :member_roles, :issues, :time_entries, :users, :trackers, :enumerations, :issue_statuses, :custom_fields, :custom_values |
|
6 |
|
|
7 |
def test_report_no_criteria |
|
8 |
get :report, :project_id => 1 |
|
9 |
assert_response :success |
|
10 |
assert_template 'report' |
|
11 |
end |
|
12 |
|
|
13 |
def test_report_all_projects |
|
14 |
get :report |
|
15 |
assert_response :success |
|
16 |
assert_template 'report' |
|
17 |
end |
|
18 |
|
|
19 |
def test_report_all_projects_denied |
|
20 |
r = Role.anonymous |
|
21 |
r.permissions.delete(:view_time_entries) |
|
22 |
r.permissions_will_change! |
|
23 |
r.save |
|
24 |
get :report |
|
25 |
assert_redirected_to '/login?back_url=https%3A%2F%2Fsiteproxy.ruqli.workers.dev%3A443%2Fhttp%2Ftest.host%2Ftime_entries%2Freport' |
|
26 |
end |
|
27 |
|
|
28 |
def test_report_all_projects_one_criteria |
|
29 |
get :report, :columns => 'week', :from => "2007-04-01", :to => "2007-04-30", :criterias => ['project'] |
|
30 |
assert_response :success |
|
31 |
assert_template 'report' |
|
32 |
assert_not_nil assigns(:total_hours) |
|
33 |
assert_equal "8.65", "%.2f" % assigns(:total_hours) |
|
34 |
end |
|
35 |
|
|
36 |
def test_report_all_time |
|
37 |
get :report, :project_id => 1, :criterias => ['project', 'issue'] |
|
38 |
assert_response :success |
|
39 |
assert_template 'report' |
|
40 |
assert_not_nil assigns(:total_hours) |
|
41 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
42 |
end |
|
43 |
|
|
44 |
def test_report_all_time_by_day |
|
45 |
get :report, :project_id => 1, :criterias => ['project', 'issue'], :columns => 'day' |
|
46 |
assert_response :success |
|
47 |
assert_template 'report' |
|
48 |
assert_not_nil assigns(:total_hours) |
|
49 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
50 |
assert_tag :tag => 'th', :content => '2007-03-12' |
|
51 |
end |
|
52 |
|
|
53 |
def test_report_one_criteria |
|
54 |
get :report, :project_id => 1, :columns => 'week', :from => "2007-04-01", :to => "2007-04-30", :criterias => ['project'] |
|
55 |
assert_response :success |
|
56 |
assert_template 'report' |
|
57 |
assert_not_nil assigns(:total_hours) |
|
58 |
assert_equal "8.65", "%.2f" % assigns(:total_hours) |
|
59 |
end |
|
60 |
|
|
61 |
def test_report_two_criterias |
|
62 |
get :report, :project_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-12-31", :criterias => ["member", "activity"] |
|
63 |
assert_response :success |
|
64 |
assert_template 'report' |
|
65 |
assert_not_nil assigns(:total_hours) |
|
66 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
67 |
end |
|
68 |
|
|
69 |
def test_report_one_day |
|
70 |
get :report, :project_id => 1, :columns => 'day', :from => "2007-03-23", :to => "2007-03-23", :criterias => ["member", "activity"] |
|
71 |
assert_response :success |
|
72 |
assert_template 'report' |
|
73 |
assert_not_nil assigns(:total_hours) |
|
74 |
assert_equal "4.25", "%.2f" % assigns(:total_hours) |
|
75 |
end |
|
76 |
|
|
77 |
def test_report_at_issue_level |
|
78 |
get :report, :project_id => 1, :issue_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-12-31", :criterias => ["member", "activity"] |
|
79 |
assert_response :success |
|
80 |
assert_template 'report' |
|
81 |
assert_not_nil assigns(:total_hours) |
|
82 |
assert_equal "154.25", "%.2f" % assigns(:total_hours) |
|
83 |
end |
|
84 |
|
|
85 |
def test_report_custom_field_criteria |
|
86 |
get :report, :project_id => 1, :criterias => ['project', 'cf_1', 'cf_7'] |
|
87 |
assert_response :success |
|
88 |
assert_template 'report' |
|
89 |
assert_not_nil assigns(:total_hours) |
|
90 |
assert_not_nil assigns(:criterias) |
|
91 |
assert_equal 3, assigns(:criterias).size |
|
92 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
93 |
# Custom field column |
|
94 |
assert_tag :tag => 'th', :content => 'Database' |
|
95 |
# Custom field row |
|
96 |
assert_tag :tag => 'td', :content => 'MySQL', |
|
97 |
:sibling => { :tag => 'td', :attributes => { :class => 'hours' }, |
|
98 |
:child => { :tag => 'span', :attributes => { :class => 'hours hours-int' }, |
|
99 |
:content => '1' }} |
|
100 |
# Second custom field column |
|
101 |
assert_tag :tag => 'th', :content => 'Billable' |
|
102 |
end |
|
103 |
|
|
104 |
def test_report_one_criteria_no_result |
|
105 |
get :report, :project_id => 1, :columns => 'week', :from => "1998-04-01", :to => "1998-04-30", :criterias => ['project'] |
|
106 |
assert_response :success |
|
107 |
assert_template 'report' |
|
108 |
assert_not_nil assigns(:total_hours) |
|
109 |
assert_equal "0.00", "%.2f" % assigns(:total_hours) |
|
110 |
end |
|
111 |
|
|
112 |
def test_report_all_projects_csv_export |
|
113 |
get :report, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv" |
|
114 |
assert_response :success |
|
115 |
assert_equal 'text/csv', @response.content_type |
|
116 |
lines = @response.body.chomp.split("\n") |
|
117 |
# Headers |
|
118 |
assert_equal 'Project,Member,Activity,2007-1,2007-2,2007-3,2007-4,2007-5,2007-6,Total', lines.first |
|
119 |
# Total row |
|
120 |
assert_equal 'Total,"","","","",154.25,8.65,"","",162.90', lines.last |
|
121 |
end |
|
122 |
|
|
123 |
def test_report_csv_export |
|
124 |
get :report, :project_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv" |
|
125 |
assert_response :success |
|
126 |
assert_equal 'text/csv', @response.content_type |
|
127 |
lines = @response.body.chomp.split("\n") |
|
128 |
# Headers |
|
129 |
assert_equal 'Project,Member,Activity,2007-1,2007-2,2007-3,2007-4,2007-5,2007-6,Total', lines.first |
|
130 |
# Total row |
|
131 |
assert_equal 'Total,"","","","",154.25,8.65,"","",162.90', lines.last |
|
132 |
end |
|
133 |
|
|
134 |
end |
trunk/test/functional/timelog_controller_test.rb | ||
---|---|---|
137 | 137 |
TimeEntry.before_destroy.reject! {|callback| callback.method == :stop_callback_chain } |
138 | 138 |
end |
139 | 139 |
|
140 |
def test_report_no_criteria |
|
141 |
get :report, :project_id => 1 |
|
142 |
assert_response :success |
|
143 |
assert_template 'report' |
|
144 |
end |
|
145 |
|
|
146 |
def test_report_all_projects |
|
147 |
get :report |
|
148 |
assert_response :success |
|
149 |
assert_template 'report' |
|
150 |
end |
|
151 |
|
|
152 |
def test_report_all_projects_denied |
|
153 |
r = Role.anonymous |
|
154 |
r.permissions.delete(:view_time_entries) |
|
155 |
r.permissions_will_change! |
|
156 |
r.save |
|
157 |
get :report |
|
158 |
assert_redirected_to '/login?back_url=https%3A%2F%2Fsiteproxy.ruqli.workers.dev%3A443%2Fhttp%2Ftest.host%2Ftime_entries%2Freport' |
|
159 |
end |
|
160 |
|
|
161 |
def test_report_all_projects_one_criteria |
|
162 |
get :report, :columns => 'week', :from => "2007-04-01", :to => "2007-04-30", :criterias => ['project'] |
|
163 |
assert_response :success |
|
164 |
assert_template 'report' |
|
165 |
assert_not_nil assigns(:total_hours) |
|
166 |
assert_equal "8.65", "%.2f" % assigns(:total_hours) |
|
167 |
end |
|
168 |
|
|
169 |
def test_report_all_time |
|
170 |
get :report, :project_id => 1, :criterias => ['project', 'issue'] |
|
171 |
assert_response :success |
|
172 |
assert_template 'report' |
|
173 |
assert_not_nil assigns(:total_hours) |
|
174 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
175 |
end |
|
176 |
|
|
177 |
def test_report_all_time_by_day |
|
178 |
get :report, :project_id => 1, :criterias => ['project', 'issue'], :columns => 'day' |
|
179 |
assert_response :success |
|
180 |
assert_template 'report' |
|
181 |
assert_not_nil assigns(:total_hours) |
|
182 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
183 |
assert_tag :tag => 'th', :content => '2007-03-12' |
|
184 |
end |
|
185 |
|
|
186 |
def test_report_one_criteria |
|
187 |
get :report, :project_id => 1, :columns => 'week', :from => "2007-04-01", :to => "2007-04-30", :criterias => ['project'] |
|
188 |
assert_response :success |
|
189 |
assert_template 'report' |
|
190 |
assert_not_nil assigns(:total_hours) |
|
191 |
assert_equal "8.65", "%.2f" % assigns(:total_hours) |
|
192 |
end |
|
193 |
|
|
194 |
def test_report_two_criterias |
|
195 |
get :report, :project_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-12-31", :criterias => ["member", "activity"] |
|
196 |
assert_response :success |
|
197 |
assert_template 'report' |
|
198 |
assert_not_nil assigns(:total_hours) |
|
199 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
200 |
end |
|
201 |
|
|
202 |
def test_report_one_day |
|
203 |
get :report, :project_id => 1, :columns => 'day', :from => "2007-03-23", :to => "2007-03-23", :criterias => ["member", "activity"] |
|
204 |
assert_response :success |
|
205 |
assert_template 'report' |
|
206 |
assert_not_nil assigns(:total_hours) |
|
207 |
assert_equal "4.25", "%.2f" % assigns(:total_hours) |
|
208 |
end |
|
209 |
|
|
210 |
def test_report_at_issue_level |
|
211 |
get :report, :project_id => 1, :issue_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-12-31", :criterias => ["member", "activity"] |
|
212 |
assert_response :success |
|
213 |
assert_template 'report' |
|
214 |
assert_not_nil assigns(:total_hours) |
|
215 |
assert_equal "154.25", "%.2f" % assigns(:total_hours) |
|
216 |
end |
|
217 |
|
|
218 |
def test_report_custom_field_criteria |
|
219 |
get :report, :project_id => 1, :criterias => ['project', 'cf_1', 'cf_7'] |
|
220 |
assert_response :success |
|
221 |
assert_template 'report' |
|
222 |
assert_not_nil assigns(:total_hours) |
|
223 |
assert_not_nil assigns(:criterias) |
|
224 |
assert_equal 3, assigns(:criterias).size |
|
225 |
assert_equal "162.90", "%.2f" % assigns(:total_hours) |
|
226 |
# Custom field column |
|
227 |
assert_tag :tag => 'th', :content => 'Database' |
|
228 |
# Custom field row |
|
229 |
assert_tag :tag => 'td', :content => 'MySQL', |
|
230 |
:sibling => { :tag => 'td', :attributes => { :class => 'hours' }, |
|
231 |
:child => { :tag => 'span', :attributes => { :class => 'hours hours-int' }, |
|
232 |
:content => '1' }} |
|
233 |
# Second custom field column |
|
234 |
assert_tag :tag => 'th', :content => 'Billable' |
|
235 |
end |
|
236 |
|
|
237 |
def test_report_one_criteria_no_result |
|
238 |
get :report, :project_id => 1, :columns => 'week', :from => "1998-04-01", :to => "1998-04-30", :criterias => ['project'] |
|
239 |
assert_response :success |
|
240 |
assert_template 'report' |
|
241 |
assert_not_nil assigns(:total_hours) |
|
242 |
assert_equal "0.00", "%.2f" % assigns(:total_hours) |
|
243 |
end |
|
244 |
|
|
245 |
def test_report_all_projects_csv_export |
|
246 |
get :report, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv" |
|
247 |
assert_response :success |
|
248 |
assert_equal 'text/csv', @response.content_type |
|
249 |
lines = @response.body.chomp.split("\n") |
|
250 |
# Headers |
|
251 |
assert_equal 'Project,Member,Activity,2007-1,2007-2,2007-3,2007-4,2007-5,2007-6,Total', lines.first |
|
252 |
# Total row |
|
253 |
assert_equal 'Total,"","","","",154.25,8.65,"","",162.90', lines.last |
|
254 |
end |
|
255 |
|
|
256 |
def test_report_csv_export |
|
257 |
get :report, :project_id => 1, :columns => 'month', :from => "2007-01-01", :to => "2007-06-30", :criterias => ["project", "member", "activity"], :format => "csv" |
|
258 |
assert_response :success |
|
259 |
assert_equal 'text/csv', @response.content_type |
|
260 |
lines = @response.body.chomp.split("\n") |
|
261 |
# Headers |
|
262 |
assert_equal 'Project,Member,Activity,2007-1,2007-2,2007-3,2007-4,2007-5,2007-6,Total', lines.first |
|
263 |
# Total row |
|
264 |
assert_equal 'Total,"","","","",154.25,8.65,"","",162.90', lines.last |
|
265 |
end |
|
266 |
|
|
267 | 140 |
def test_details_all_projects |
268 | 141 |
get :details |
269 | 142 |
assert_response :success |
trunk/test/integration/routing_test.rb | ||
---|---|---|
226 | 226 |
should_route :get, "/projects/ecookbook/time_entries/new", :controller => 'timelog', :action => 'edit', :project_id => 'ecookbook' |
227 | 227 |
should_route :get, "/projects/ecookbook/issues/567/time_entries/new", :controller => 'timelog', :action => 'edit', :project_id => 'ecookbook', :issue_id => '567' |
228 | 228 |
should_route :get, "/time_entries/22/edit", :controller => 'timelog', :action => 'edit', :id => '22' |
229 |
should_route :get, "/time_entries/report", :controller => 'timelog', :action => 'report' |
|
230 |
should_route :get, "/projects/567/time_entries/report", :controller => 'timelog', :action => 'report', :project_id => '567' |
|
231 |
should_route :get, "/projects/567/time_entries/report.csv", :controller => 'timelog', :action => 'report', :project_id => '567', :format => 'csv' |
|
229 |
|
|
230 |
should_route :post, "/time_entries/55/destroy", :controller => 'timelog', :action => 'destroy', :id => '55' |
|
231 |
end |
|
232 |
|
|
233 |
context "time_entry_reports" do |
|
234 |
should_route :get, "/time_entries/report", :controller => 'time_entry_reports', :action => 'report' |
|
235 |
should_route :get, "/projects/567/time_entries/report", :controller => 'time_entry_reports', :action => 'report', :project_id => '567' |
|
236 |
should_route :get, "/projects/567/time_entries/report.csv", :controller => 'time_entry_reports', :action => 'report', :project_id => '567', :format => 'csv' |
|
237 |
|
|
232 | 238 |
should_route :get, "/time_entries", :controller => 'timelog', :action => 'details' |
233 | 239 |
should_route :get, "/time_entries.csv", :controller => 'timelog', :action => 'details', :format => 'csv' |
234 | 240 |
should_route :get, "/time_entries.atom", :controller => 'timelog', :action => 'details', :format => 'atom' |
... | ... | |
240 | 246 |
should_route :get, "/issues/234/time_entries.atom", :controller => 'timelog', :action => 'details', :issue_id => '234', :format => 'atom' |
241 | 247 |
should_route :get, "/projects/ecookbook/issues/123/time_entries", :controller => 'timelog', :action => 'details', :project_id => 'ecookbook', :issue_id => '123' |
242 | 248 |
|
243 |
should_route :post, "/time_entries/55/destroy", :controller => 'timelog', :action => 'destroy', :id => '55' |
|
244 | 249 |
end |
245 | 250 |
|
246 | 251 |
context "users" do |
Also available in: Unified diff
Refactor: extract TimelogController#report to a new controller class