##// END OF EJS Templates
Adds a 'Create and continue' button on the spent time form (#9995)....
Jean-Philippe Lang -
r8567:1d07bb91a7d1
parent child
Show More
@@ -1,326 +1,330
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
2 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class TimelogController < ApplicationController
18 class TimelogController < ApplicationController
19 menu_item :issues
19 menu_item :issues
20 before_filter :find_project, :only => [:new, :create]
20 before_filter :find_project, :only => [:new, :create]
21 before_filter :find_time_entry, :only => [:show, :edit, :update]
21 before_filter :find_time_entry, :only => [:show, :edit, :update]
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
22 before_filter :find_time_entries, :only => [:bulk_edit, :bulk_update, :destroy]
23 before_filter :authorize, :except => [:index, :report]
23 before_filter :authorize, :except => [:index, :report]
24 before_filter :find_optional_project, :only => [:index, :report]
24 before_filter :find_optional_project, :only => [:index, :report]
25 accept_rss_auth :index
25 accept_rss_auth :index
26 accept_api_auth :index, :show, :create, :update, :destroy
26 accept_api_auth :index, :show, :create, :update, :destroy
27
27
28 helper :sort
28 helper :sort
29 include SortHelper
29 include SortHelper
30 helper :issues
30 helper :issues
31 include TimelogHelper
31 include TimelogHelper
32 helper :custom_fields
32 helper :custom_fields
33 include CustomFieldsHelper
33 include CustomFieldsHelper
34
34
35 def index
35 def index
36 sort_init 'spent_on', 'desc'
36 sort_init 'spent_on', 'desc'
37 sort_update 'spent_on' => 'spent_on',
37 sort_update 'spent_on' => 'spent_on',
38 'user' => 'user_id',
38 'user' => 'user_id',
39 'activity' => 'activity_id',
39 'activity' => 'activity_id',
40 'project' => "#{Project.table_name}.name",
40 'project' => "#{Project.table_name}.name",
41 'issue' => 'issue_id',
41 'issue' => 'issue_id',
42 'hours' => 'hours'
42 'hours' => 'hours'
43
43
44 retrieve_date_range
44 retrieve_date_range
45
45
46 scope = TimeEntry.visible.spent_between(@from, @to)
46 scope = TimeEntry.visible.spent_between(@from, @to)
47 if @issue
47 if @issue
48 scope = scope.on_issue(@issue)
48 scope = scope.on_issue(@issue)
49 elsif @project
49 elsif @project
50 scope = scope.on_project(@project, Setting.display_subprojects_issues?)
50 scope = scope.on_project(@project, Setting.display_subprojects_issues?)
51 end
51 end
52
52
53 respond_to do |format|
53 respond_to do |format|
54 format.html {
54 format.html {
55 # Paginate results
55 # Paginate results
56 @entry_count = scope.count
56 @entry_count = scope.count
57 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
57 @entry_pages = Paginator.new self, @entry_count, per_page_option, params['page']
58 @entries = scope.all(
58 @entries = scope.all(
59 :include => [:project, :activity, :user, {:issue => :tracker}],
59 :include => [:project, :activity, :user, {:issue => :tracker}],
60 :order => sort_clause,
60 :order => sort_clause,
61 :limit => @entry_pages.items_per_page,
61 :limit => @entry_pages.items_per_page,
62 :offset => @entry_pages.current.offset
62 :offset => @entry_pages.current.offset
63 )
63 )
64 @total_hours = scope.sum(:hours).to_f
64 @total_hours = scope.sum(:hours).to_f
65
65
66 render :layout => !request.xhr?
66 render :layout => !request.xhr?
67 }
67 }
68 format.api {
68 format.api {
69 @entry_count = scope.count
69 @entry_count = scope.count
70 @offset, @limit = api_offset_and_limit
70 @offset, @limit = api_offset_and_limit
71 @entries = scope.all(
71 @entries = scope.all(
72 :include => [:project, :activity, :user, {:issue => :tracker}],
72 :include => [:project, :activity, :user, {:issue => :tracker}],
73 :order => sort_clause,
73 :order => sort_clause,
74 :limit => @limit,
74 :limit => @limit,
75 :offset => @offset
75 :offset => @offset
76 )
76 )
77 }
77 }
78 format.atom {
78 format.atom {
79 entries = scope.all(
79 entries = scope.all(
80 :include => [:project, :activity, :user, {:issue => :tracker}],
80 :include => [:project, :activity, :user, {:issue => :tracker}],
81 :order => "#{TimeEntry.table_name}.created_on DESC",
81 :order => "#{TimeEntry.table_name}.created_on DESC",
82 :limit => Setting.feeds_limit.to_i
82 :limit => Setting.feeds_limit.to_i
83 )
83 )
84 render_feed(entries, :title => l(:label_spent_time))
84 render_feed(entries, :title => l(:label_spent_time))
85 }
85 }
86 format.csv {
86 format.csv {
87 # Export all entries
87 # Export all entries
88 @entries = scope.all(
88 @entries = scope.all(
89 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
89 :include => [:project, :activity, :user, {:issue => [:tracker, :assigned_to, :priority]}],
90 :order => sort_clause
90 :order => sort_clause
91 )
91 )
92 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
92 send_data(entries_to_csv(@entries), :type => 'text/csv; header=present', :filename => 'timelog.csv')
93 }
93 }
94 end
94 end
95 end
95 end
96
96
97 def report
97 def report
98 retrieve_date_range
98 retrieve_date_range
99 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to)
99 @report = Redmine::Helpers::TimeReport.new(@project, @issue, params[:criteria], params[:columns], @from, @to)
100
100
101 respond_to do |format|
101 respond_to do |format|
102 format.html { render :layout => !request.xhr? }
102 format.html { render :layout => !request.xhr? }
103 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
103 format.csv { send_data(report_to_csv(@report), :type => 'text/csv; header=present', :filename => 'timelog.csv') }
104 end
104 end
105 end
105 end
106
106
107 def show
107 def show
108 respond_to do |format|
108 respond_to do |format|
109 # TODO: Implement html response
109 # TODO: Implement html response
110 format.html { render :nothing => true, :status => 406 }
110 format.html { render :nothing => true, :status => 406 }
111 format.api
111 format.api
112 end
112 end
113 end
113 end
114
114
115 def new
115 def new
116 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
116 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
117 @time_entry.attributes = params[:time_entry]
117 @time_entry.attributes = params[:time_entry]
118 end
118 end
119
119
120 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
120 verify :method => :post, :only => :create, :render => {:nothing => true, :status => :method_not_allowed }
121 def create
121 def create
122 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
122 @time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
123 @time_entry.attributes = params[:time_entry]
123 @time_entry.attributes = params[:time_entry]
124
124
125 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
125 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
126
126
127 if @time_entry.save
127 if @time_entry.save
128 respond_to do |format|
128 respond_to do |format|
129 format.html {
129 format.html {
130 flash[:notice] = l(:notice_successful_update)
130 flash[:notice] = l(:notice_successful_create)
131 if params[:continue]
132 redirect_to :action => 'new', :project_id => @time_entry.project, :issue_id => @time_entry.issue
133 else
131 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
134 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
135 end
132 }
136 }
133 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
137 format.api { render :action => 'show', :status => :created, :location => time_entry_url(@time_entry) }
134 end
138 end
135 else
139 else
136 respond_to do |format|
140 respond_to do |format|
137 format.html { render :action => 'edit' }
141 format.html { render :action => 'edit' }
138 format.api { render_validation_errors(@time_entry) }
142 format.api { render_validation_errors(@time_entry) }
139 end
143 end
140 end
144 end
141 end
145 end
142
146
143 def edit
147 def edit
144 @time_entry.attributes = params[:time_entry]
148 @time_entry.attributes = params[:time_entry]
145 end
149 end
146
150
147 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
151 verify :method => :put, :only => :update, :render => {:nothing => true, :status => :method_not_allowed }
148 def update
152 def update
149 @time_entry.attributes = params[:time_entry]
153 @time_entry.attributes = params[:time_entry]
150
154
151 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
155 call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
152
156
153 if @time_entry.save
157 if @time_entry.save
154 respond_to do |format|
158 respond_to do |format|
155 format.html {
159 format.html {
156 flash[:notice] = l(:notice_successful_update)
160 flash[:notice] = l(:notice_successful_update)
157 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
161 redirect_back_or_default :action => 'index', :project_id => @time_entry.project
158 }
162 }
159 format.api { head :ok }
163 format.api { head :ok }
160 end
164 end
161 else
165 else
162 respond_to do |format|
166 respond_to do |format|
163 format.html { render :action => 'edit' }
167 format.html { render :action => 'edit' }
164 format.api { render_validation_errors(@time_entry) }
168 format.api { render_validation_errors(@time_entry) }
165 end
169 end
166 end
170 end
167 end
171 end
168
172
169 def bulk_edit
173 def bulk_edit
170 @available_activities = TimeEntryActivity.shared.active
174 @available_activities = TimeEntryActivity.shared.active
171 @custom_fields = TimeEntry.first.available_custom_fields
175 @custom_fields = TimeEntry.first.available_custom_fields
172 end
176 end
173
177
174 def bulk_update
178 def bulk_update
175 attributes = parse_params_for_bulk_time_entry_attributes(params)
179 attributes = parse_params_for_bulk_time_entry_attributes(params)
176
180
177 unsaved_time_entry_ids = []
181 unsaved_time_entry_ids = []
178 @time_entries.each do |time_entry|
182 @time_entries.each do |time_entry|
179 time_entry.reload
183 time_entry.reload
180 time_entry.attributes = attributes
184 time_entry.attributes = attributes
181 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
185 call_hook(:controller_time_entries_bulk_edit_before_save, { :params => params, :time_entry => time_entry })
182 unless time_entry.save
186 unless time_entry.save
183 # Keep unsaved time_entry ids to display them in flash error
187 # Keep unsaved time_entry ids to display them in flash error
184 unsaved_time_entry_ids << time_entry.id
188 unsaved_time_entry_ids << time_entry.id
185 end
189 end
186 end
190 end
187 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
191 set_flash_from_bulk_time_entry_save(@time_entries, unsaved_time_entry_ids)
188 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
192 redirect_back_or_default({:controller => 'timelog', :action => 'index', :project_id => @projects.first})
189 end
193 end
190
194
191 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
195 verify :method => :delete, :only => :destroy, :render => {:nothing => true, :status => :method_not_allowed }
192 def destroy
196 def destroy
193 @time_entries.each do |t|
197 @time_entries.each do |t|
194 begin
198 begin
195 unless t.destroy && t.destroyed?
199 unless t.destroy && t.destroyed?
196 respond_to do |format|
200 respond_to do |format|
197 format.html {
201 format.html {
198 flash[:error] = l(:notice_unable_delete_time_entry)
202 flash[:error] = l(:notice_unable_delete_time_entry)
199 redirect_to :back
203 redirect_to :back
200 }
204 }
201 format.api { render_validation_errors(t) }
205 format.api { render_validation_errors(t) }
202 end
206 end
203 return
207 return
204 end
208 end
205 rescue ::ActionController::RedirectBackError
209 rescue ::ActionController::RedirectBackError
206 redirect_to :action => 'index', :project_id => @projects.first
210 redirect_to :action => 'index', :project_id => @projects.first
207 return
211 return
208 end
212 end
209 end
213 end
210
214
211 respond_to do |format|
215 respond_to do |format|
212 format.html {
216 format.html {
213 flash[:notice] = l(:notice_successful_delete)
217 flash[:notice] = l(:notice_successful_delete)
214 redirect_back_or_default(:action => 'index', :project_id => @projects.first)
218 redirect_back_or_default(:action => 'index', :project_id => @projects.first)
215 }
219 }
216 format.api { head :ok }
220 format.api { head :ok }
217 end
221 end
218 end
222 end
219
223
220 private
224 private
221 def find_time_entry
225 def find_time_entry
222 @time_entry = TimeEntry.find(params[:id])
226 @time_entry = TimeEntry.find(params[:id])
223 unless @time_entry.editable_by?(User.current)
227 unless @time_entry.editable_by?(User.current)
224 render_403
228 render_403
225 return false
229 return false
226 end
230 end
227 @project = @time_entry.project
231 @project = @time_entry.project
228 rescue ActiveRecord::RecordNotFound
232 rescue ActiveRecord::RecordNotFound
229 render_404
233 render_404
230 end
234 end
231
235
232 def find_time_entries
236 def find_time_entries
233 @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
237 @time_entries = TimeEntry.find_all_by_id(params[:id] || params[:ids])
234 raise ActiveRecord::RecordNotFound if @time_entries.empty?
238 raise ActiveRecord::RecordNotFound if @time_entries.empty?
235 @projects = @time_entries.collect(&:project).compact.uniq
239 @projects = @time_entries.collect(&:project).compact.uniq
236 @project = @projects.first if @projects.size == 1
240 @project = @projects.first if @projects.size == 1
237 rescue ActiveRecord::RecordNotFound
241 rescue ActiveRecord::RecordNotFound
238 render_404
242 render_404
239 end
243 end
240
244
241 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
245 def set_flash_from_bulk_time_entry_save(time_entries, unsaved_time_entry_ids)
242 if unsaved_time_entry_ids.empty?
246 if unsaved_time_entry_ids.empty?
243 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
247 flash[:notice] = l(:notice_successful_update) unless time_entries.empty?
244 else
248 else
245 flash[:error] = l(:notice_failed_to_save_time_entries,
249 flash[:error] = l(:notice_failed_to_save_time_entries,
246 :count => unsaved_time_entry_ids.size,
250 :count => unsaved_time_entry_ids.size,
247 :total => time_entries.size,
251 :total => time_entries.size,
248 :ids => '#' + unsaved_time_entry_ids.join(', #'))
252 :ids => '#' + unsaved_time_entry_ids.join(', #'))
249 end
253 end
250 end
254 end
251
255
252 def find_project
256 def find_project
253 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
257 if (issue_id = (params[:issue_id] || params[:time_entry] && params[:time_entry][:issue_id])).present?
254 @issue = Issue.find(issue_id)
258 @issue = Issue.find(issue_id)
255 @project = @issue.project
259 @project = @issue.project
256 elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
260 elsif (project_id = (params[:project_id] || params[:time_entry] && params[:time_entry][:project_id])).present?
257 @project = Project.find(project_id)
261 @project = Project.find(project_id)
258 else
262 else
259 render_404
263 render_404
260 return false
264 return false
261 end
265 end
262 rescue ActiveRecord::RecordNotFound
266 rescue ActiveRecord::RecordNotFound
263 render_404
267 render_404
264 end
268 end
265
269
266 def find_optional_project
270 def find_optional_project
267 if !params[:issue_id].blank?
271 if !params[:issue_id].blank?
268 @issue = Issue.find(params[:issue_id])
272 @issue = Issue.find(params[:issue_id])
269 @project = @issue.project
273 @project = @issue.project
270 elsif !params[:project_id].blank?
274 elsif !params[:project_id].blank?
271 @project = Project.find(params[:project_id])
275 @project = Project.find(params[:project_id])
272 end
276 end
273 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
277 deny_access unless User.current.allowed_to?(:view_time_entries, @project, :global => true)
274 end
278 end
275
279
276 # Retrieves the date range based on predefined ranges or specific from/to param dates
280 # Retrieves the date range based on predefined ranges or specific from/to param dates
277 def retrieve_date_range
281 def retrieve_date_range
278 @free_period = false
282 @free_period = false
279 @from, @to = nil, nil
283 @from, @to = nil, nil
280
284
281 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
285 if params[:period_type] == '1' || (params[:period_type].nil? && !params[:period].nil?)
282 case params[:period].to_s
286 case params[:period].to_s
283 when 'today'
287 when 'today'
284 @from = @to = Date.today
288 @from = @to = Date.today
285 when 'yesterday'
289 when 'yesterday'
286 @from = @to = Date.today - 1
290 @from = @to = Date.today - 1
287 when 'current_week'
291 when 'current_week'
288 @from = Date.today - (Date.today.cwday - 1)%7
292 @from = Date.today - (Date.today.cwday - 1)%7
289 @to = @from + 6
293 @to = @from + 6
290 when 'last_week'
294 when 'last_week'
291 @from = Date.today - 7 - (Date.today.cwday - 1)%7
295 @from = Date.today - 7 - (Date.today.cwday - 1)%7
292 @to = @from + 6
296 @to = @from + 6
293 when '7_days'
297 when '7_days'
294 @from = Date.today - 7
298 @from = Date.today - 7
295 @to = Date.today
299 @to = Date.today
296 when 'current_month'
300 when 'current_month'
297 @from = Date.civil(Date.today.year, Date.today.month, 1)
301 @from = Date.civil(Date.today.year, Date.today.month, 1)
298 @to = (@from >> 1) - 1
302 @to = (@from >> 1) - 1
299 when 'last_month'
303 when 'last_month'
300 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
304 @from = Date.civil(Date.today.year, Date.today.month, 1) << 1
301 @to = (@from >> 1) - 1
305 @to = (@from >> 1) - 1
302 when '30_days'
306 when '30_days'
303 @from = Date.today - 30
307 @from = Date.today - 30
304 @to = Date.today
308 @to = Date.today
305 when 'current_year'
309 when 'current_year'
306 @from = Date.civil(Date.today.year, 1, 1)
310 @from = Date.civil(Date.today.year, 1, 1)
307 @to = Date.civil(Date.today.year, 12, 31)
311 @to = Date.civil(Date.today.year, 12, 31)
308 end
312 end
309 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
313 elsif params[:period_type] == '2' || (params[:period_type].nil? && (!params[:from].nil? || !params[:to].nil?))
310 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
314 begin; @from = params[:from].to_s.to_date unless params[:from].blank?; rescue; end
311 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
315 begin; @to = params[:to].to_s.to_date unless params[:to].blank?; rescue; end
312 @free_period = true
316 @free_period = true
313 else
317 else
314 # default
318 # default
315 end
319 end
316
320
317 @from, @to = @to, @from if @from && @to && @from > @to
321 @from, @to = @to, @from if @from && @to && @from > @to
318 end
322 end
319
323
320 def parse_params_for_bulk_time_entry_attributes(params)
324 def parse_params_for_bulk_time_entry_attributes(params)
321 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
325 attributes = (params[:time_entry] || {}).reject {|k,v| v.blank?}
322 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
326 attributes.keys.each {|k| attributes[k] = '' if attributes[k] == 'none'}
323 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
327 attributes[:custom_field_values].reject! {|k,v| v.blank?} if attributes[:custom_field_values]
324 attributes
328 attributes
325 end
329 end
326 end
330 end
@@ -1,6 +1,7
1 <h2><%= l(:label_spent_time) %></h2>
1 <h2><%= l(:label_spent_time) %></h2>
2
2
3 <% labelled_form_for @time_entry, :url => project_time_entries_path(@time_entry.project) do |f| %>
3 <% labelled_form_for @time_entry, :url => project_time_entries_path(@time_entry.project) do |f| %>
4 <%= render :partial => 'form', :locals => {:f => f} %>
4 <%= render :partial => 'form', :locals => {:f => f} %>
5 <%= submit_tag l(:button_save) %>
5 <%= submit_tag l(:button_create) %>
6 <%= submit_tag l(:button_create_and_continue), :name => 'continue' %>
6 <% end %>
7 <% end %>
@@ -1,588 +1,610
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # Redmine - project management software
2 # Redmine - project management software
3 # Copyright (C) 2006-2011 Jean-Philippe Lang
3 # Copyright (C) 2006-2011 Jean-Philippe Lang
4 #
4 #
5 # This program is free software; you can redistribute it and/or
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
8 # of the License, or (at your option) any later version.
9 #
9 #
10 # This program is distributed in the hope that it will be useful,
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
13 # GNU General Public License for more details.
14 #
14 #
15 # You should have received a copy of the GNU General Public License
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18
18
19 require File.expand_path('../../test_helper', __FILE__)
19 require File.expand_path('../../test_helper', __FILE__)
20 require 'timelog_controller'
20 require 'timelog_controller'
21
21
22 # Re-raise errors caught by the controller.
22 # Re-raise errors caught by the controller.
23 class TimelogController; def rescue_action(e) raise e end; end
23 class TimelogController; def rescue_action(e) raise e end; end
24
24
25 class TimelogControllerTest < ActionController::TestCase
25 class TimelogControllerTest < ActionController::TestCase
26 fixtures :projects, :enabled_modules, :roles, :members,
26 fixtures :projects, :enabled_modules, :roles, :members,
27 :member_roles, :issues, :time_entries, :users,
27 :member_roles, :issues, :time_entries, :users,
28 :trackers, :enumerations, :issue_statuses,
28 :trackers, :enumerations, :issue_statuses,
29 :custom_fields, :custom_values
29 :custom_fields, :custom_values
30
30
31 include Redmine::I18n
31 include Redmine::I18n
32
32
33 def setup
33 def setup
34 @controller = TimelogController.new
34 @controller = TimelogController.new
35 @request = ActionController::TestRequest.new
35 @request = ActionController::TestRequest.new
36 @response = ActionController::TestResponse.new
36 @response = ActionController::TestResponse.new
37 end
37 end
38
38
39 def test_get_new
39 def test_get_new
40 @request.session[:user_id] = 3
40 @request.session[:user_id] = 3
41 get :new, :project_id => 1
41 get :new, :project_id => 1
42 assert_response :success
42 assert_response :success
43 assert_template 'new'
43 assert_template 'new'
44 # Default activity selected
44 # Default activity selected
45 assert_tag :tag => 'option', :attributes => { :selected => 'selected' },
45 assert_tag :tag => 'option', :attributes => { :selected => 'selected' },
46 :content => 'Development'
46 :content => 'Development'
47 end
47 end
48
48
49 def test_get_new_should_only_show_active_time_entry_activities
49 def test_get_new_should_only_show_active_time_entry_activities
50 @request.session[:user_id] = 3
50 @request.session[:user_id] = 3
51 get :new, :project_id => 1
51 get :new, :project_id => 1
52 assert_response :success
52 assert_response :success
53 assert_template 'new'
53 assert_template 'new'
54 assert_no_tag :tag => 'option', :content => 'Inactive Activity'
54 assert_no_tag :tag => 'option', :content => 'Inactive Activity'
55 end
55 end
56
56
57 def test_get_edit_existing_time
57 def test_get_edit_existing_time
58 @request.session[:user_id] = 2
58 @request.session[:user_id] = 2
59 get :edit, :id => 2, :project_id => nil
59 get :edit, :id => 2, :project_id => nil
60 assert_response :success
60 assert_response :success
61 assert_template 'edit'
61 assert_template 'edit'
62 # Default activity selected
62 # Default activity selected
63 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
63 assert_tag :tag => 'form', :attributes => { :action => '/projects/ecookbook/time_entries/2' }
64 end
64 end
65
65
66 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
66 def test_get_edit_with_an_existing_time_entry_with_inactive_activity
67 te = TimeEntry.find(1)
67 te = TimeEntry.find(1)
68 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
68 te.activity = TimeEntryActivity.find_by_name("Inactive Activity")
69 te.save!
69 te.save!
70
70
71 @request.session[:user_id] = 1
71 @request.session[:user_id] = 1
72 get :edit, :project_id => 1, :id => 1
72 get :edit, :project_id => 1, :id => 1
73 assert_response :success
73 assert_response :success
74 assert_template 'edit'
74 assert_template 'edit'
75 # Blank option since nothing is pre-selected
75 # Blank option since nothing is pre-selected
76 assert_tag :tag => 'option', :content => '--- Please select ---'
76 assert_tag :tag => 'option', :content => '--- Please select ---'
77 end
77 end
78
78
79 def test_post_create
79 def test_post_create
80 # TODO: should POST to issues’ time log instead of project. change form
80 # TODO: should POST to issues’ time log instead of project. change form
81 # and routing
81 # and routing
82 @request.session[:user_id] = 3
82 @request.session[:user_id] = 3
83 post :create, :project_id => 1,
83 post :create, :project_id => 1,
84 :time_entry => {:comments => 'Some work on TimelogControllerTest',
84 :time_entry => {:comments => 'Some work on TimelogControllerTest',
85 # Not the default activity
85 # Not the default activity
86 :activity_id => '11',
86 :activity_id => '11',
87 :spent_on => '2008-03-14',
87 :spent_on => '2008-03-14',
88 :issue_id => '1',
88 :issue_id => '1',
89 :hours => '7.3'}
89 :hours => '7.3'}
90 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
90 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
91
91
92 i = Issue.find(1)
92 i = Issue.find(1)
93 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
93 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
94 assert_not_nil t
94 assert_not_nil t
95 assert_equal 11, t.activity_id
95 assert_equal 11, t.activity_id
96 assert_equal 7.3, t.hours
96 assert_equal 7.3, t.hours
97 assert_equal 3, t.user_id
97 assert_equal 3, t.user_id
98 assert_equal i, t.issue
98 assert_equal i, t.issue
99 assert_equal i.project, t.project
99 assert_equal i.project, t.project
100 end
100 end
101
101
102 def test_post_create_with_blank_issue
102 def test_post_create_with_blank_issue
103 # TODO: should POST to issues’ time log instead of project. change form
103 # TODO: should POST to issues’ time log instead of project. change form
104 # and routing
104 # and routing
105 @request.session[:user_id] = 3
105 @request.session[:user_id] = 3
106 post :create, :project_id => 1,
106 post :create, :project_id => 1,
107 :time_entry => {:comments => 'Some work on TimelogControllerTest',
107 :time_entry => {:comments => 'Some work on TimelogControllerTest',
108 # Not the default activity
108 # Not the default activity
109 :activity_id => '11',
109 :activity_id => '11',
110 :issue_id => '',
110 :issue_id => '',
111 :spent_on => '2008-03-14',
111 :spent_on => '2008-03-14',
112 :hours => '7.3'}
112 :hours => '7.3'}
113 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
113 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
114
114
115 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
115 t = TimeEntry.find_by_comments('Some work on TimelogControllerTest')
116 assert_not_nil t
116 assert_not_nil t
117 assert_equal 11, t.activity_id
117 assert_equal 11, t.activity_id
118 assert_equal 7.3, t.hours
118 assert_equal 7.3, t.hours
119 assert_equal 3, t.user_id
119 assert_equal 3, t.user_id
120 end
120 end
121
121
122 def test_create_and_continue
123 @request.session[:user_id] = 2
124 post :create, :project_id => 1,
125 :time_entry => {:activity_id => '11',
126 :issue_id => '',
127 :spent_on => '2008-03-14',
128 :hours => '7.3'},
129 :continue => '1'
130 assert_redirected_to '/projects/ecookbook/time_entries/new'
131 end
132
133 def test_create_and_continue_with_issue_id
134 @request.session[:user_id] = 2
135 post :create, :project_id => 1,
136 :time_entry => {:activity_id => '11',
137 :issue_id => '1',
138 :spent_on => '2008-03-14',
139 :hours => '7.3'},
140 :continue => '1'
141 assert_redirected_to '/projects/ecookbook/issues/1/time_entries/new'
142 end
143
122 def test_create_without_log_time_permission_should_be_denied
144 def test_create_without_log_time_permission_should_be_denied
123 @request.session[:user_id] = 2
145 @request.session[:user_id] = 2
124 Role.find_by_name('Manager').remove_permission! :log_time
146 Role.find_by_name('Manager').remove_permission! :log_time
125 post :create, :project_id => 1,
147 post :create, :project_id => 1,
126 :time_entry => {:activity_id => '11',
148 :time_entry => {:activity_id => '11',
127 :issue_id => '',
149 :issue_id => '',
128 :spent_on => '2008-03-14',
150 :spent_on => '2008-03-14',
129 :hours => '7.3'}
151 :hours => '7.3'}
130
152
131 assert_response 403
153 assert_response 403
132 end
154 end
133
155
134 def test_update
156 def test_update
135 entry = TimeEntry.find(1)
157 entry = TimeEntry.find(1)
136 assert_equal 1, entry.issue_id
158 assert_equal 1, entry.issue_id
137 assert_equal 2, entry.user_id
159 assert_equal 2, entry.user_id
138
160
139 @request.session[:user_id] = 1
161 @request.session[:user_id] = 1
140 put :update, :id => 1,
162 put :update, :id => 1,
141 :time_entry => {:issue_id => '2',
163 :time_entry => {:issue_id => '2',
142 :hours => '8'}
164 :hours => '8'}
143 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
165 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
144 entry.reload
166 entry.reload
145
167
146 assert_equal 8, entry.hours
168 assert_equal 8, entry.hours
147 assert_equal 2, entry.issue_id
169 assert_equal 2, entry.issue_id
148 assert_equal 2, entry.user_id
170 assert_equal 2, entry.user_id
149 end
171 end
150
172
151 def test_get_bulk_edit
173 def test_get_bulk_edit
152 @request.session[:user_id] = 2
174 @request.session[:user_id] = 2
153 get :bulk_edit, :ids => [1, 2]
175 get :bulk_edit, :ids => [1, 2]
154 assert_response :success
176 assert_response :success
155 assert_template 'bulk_edit'
177 assert_template 'bulk_edit'
156
178
157 # System wide custom field
179 # System wide custom field
158 assert_tag :select, :attributes => {:name => 'time_entry[custom_field_values][10]'}
180 assert_tag :select, :attributes => {:name => 'time_entry[custom_field_values][10]'}
159 end
181 end
160
182
161 def test_get_bulk_edit_on_different_projects
183 def test_get_bulk_edit_on_different_projects
162 @request.session[:user_id] = 2
184 @request.session[:user_id] = 2
163 get :bulk_edit, :ids => [1, 2, 6]
185 get :bulk_edit, :ids => [1, 2, 6]
164 assert_response :success
186 assert_response :success
165 assert_template 'bulk_edit'
187 assert_template 'bulk_edit'
166 end
188 end
167
189
168 def test_bulk_update
190 def test_bulk_update
169 @request.session[:user_id] = 2
191 @request.session[:user_id] = 2
170 # update time entry activity
192 # update time entry activity
171 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
193 post :bulk_update, :ids => [1, 2], :time_entry => { :activity_id => 9}
172
194
173 assert_response 302
195 assert_response 302
174 # check that the issues were updated
196 # check that the issues were updated
175 assert_equal [9, 9], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.activity_id}
197 assert_equal [9, 9], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.activity_id}
176 end
198 end
177
199
178 def test_bulk_update_with_failure
200 def test_bulk_update_with_failure
179 @request.session[:user_id] = 2
201 @request.session[:user_id] = 2
180 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
202 post :bulk_update, :ids => [1, 2], :time_entry => { :hours => 'A'}
181
203
182 assert_response 302
204 assert_response 302
183 assert_match /Failed to save 2 time entrie/, flash[:error]
205 assert_match /Failed to save 2 time entrie/, flash[:error]
184 end
206 end
185
207
186 def test_bulk_update_on_different_projects
208 def test_bulk_update_on_different_projects
187 @request.session[:user_id] = 2
209 @request.session[:user_id] = 2
188 # makes user a manager on the other project
210 # makes user a manager on the other project
189 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
211 Member.create!(:user_id => 2, :project_id => 3, :role_ids => [1])
190
212
191 # update time entry activity
213 # update time entry activity
192 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
214 post :bulk_update, :ids => [1, 2, 4], :time_entry => { :activity_id => 9 }
193
215
194 assert_response 302
216 assert_response 302
195 # check that the issues were updated
217 # check that the issues were updated
196 assert_equal [9, 9, 9], TimeEntry.find_all_by_id([1, 2, 4]).collect {|i| i.activity_id}
218 assert_equal [9, 9, 9], TimeEntry.find_all_by_id([1, 2, 4]).collect {|i| i.activity_id}
197 end
219 end
198
220
199 def test_bulk_update_on_different_projects_without_rights
221 def test_bulk_update_on_different_projects_without_rights
200 @request.session[:user_id] = 3
222 @request.session[:user_id] = 3
201 user = User.find(3)
223 user = User.find(3)
202 action = { :controller => "timelog", :action => "bulk_update" }
224 action = { :controller => "timelog", :action => "bulk_update" }
203 assert user.allowed_to?(action, TimeEntry.find(1).project)
225 assert user.allowed_to?(action, TimeEntry.find(1).project)
204 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
226 assert ! user.allowed_to?(action, TimeEntry.find(5).project)
205 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
227 post :bulk_update, :ids => [1, 5], :time_entry => { :activity_id => 9 }
206 assert_response 403
228 assert_response 403
207 end
229 end
208
230
209 def test_bulk_update_custom_field
231 def test_bulk_update_custom_field
210 @request.session[:user_id] = 2
232 @request.session[:user_id] = 2
211 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
233 post :bulk_update, :ids => [1, 2], :time_entry => { :custom_field_values => {'10' => '0'} }
212
234
213 assert_response 302
235 assert_response 302
214 assert_equal ["0", "0"], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.custom_value_for(10).value}
236 assert_equal ["0", "0"], TimeEntry.find_all_by_id([1, 2]).collect {|i| i.custom_value_for(10).value}
215 end
237 end
216
238
217 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
239 def test_post_bulk_update_should_redirect_back_using_the_back_url_parameter
218 @request.session[:user_id] = 2
240 @request.session[:user_id] = 2
219 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
241 post :bulk_update, :ids => [1,2], :back_url => '/time_entries'
220
242
221 assert_response :redirect
243 assert_response :redirect
222 assert_redirected_to '/time_entries'
244 assert_redirected_to '/time_entries'
223 end
245 end
224
246
225 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
247 def test_post_bulk_update_should_not_redirect_back_using_the_back_url_parameter_off_the_host
226 @request.session[:user_id] = 2
248 @request.session[:user_id] = 2
227 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
249 post :bulk_update, :ids => [1,2], :back_url => 'http://google.com'
228
250
229 assert_response :redirect
251 assert_response :redirect
230 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
252 assert_redirected_to :controller => 'timelog', :action => 'index', :project_id => Project.find(1).identifier
231 end
253 end
232
254
233 def test_post_bulk_update_without_edit_permission_should_be_denied
255 def test_post_bulk_update_without_edit_permission_should_be_denied
234 @request.session[:user_id] = 2
256 @request.session[:user_id] = 2
235 Role.find_by_name('Manager').remove_permission! :edit_time_entries
257 Role.find_by_name('Manager').remove_permission! :edit_time_entries
236 post :bulk_update, :ids => [1,2]
258 post :bulk_update, :ids => [1,2]
237
259
238 assert_response 403
260 assert_response 403
239 end
261 end
240
262
241 def test_destroy
263 def test_destroy
242 @request.session[:user_id] = 2
264 @request.session[:user_id] = 2
243 delete :destroy, :id => 1
265 delete :destroy, :id => 1
244 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
266 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
245 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
267 assert_equal I18n.t(:notice_successful_delete), flash[:notice]
246 assert_nil TimeEntry.find_by_id(1)
268 assert_nil TimeEntry.find_by_id(1)
247 end
269 end
248
270
249 def test_destroy_should_fail
271 def test_destroy_should_fail
250 # simulate that this fails (e.g. due to a plugin), see #5700
272 # simulate that this fails (e.g. due to a plugin), see #5700
251 TimeEntry.any_instance.expects(:destroy).returns(false)
273 TimeEntry.any_instance.expects(:destroy).returns(false)
252
274
253 @request.session[:user_id] = 2
275 @request.session[:user_id] = 2
254 delete :destroy, :id => 1
276 delete :destroy, :id => 1
255 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
277 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
256 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
278 assert_equal I18n.t(:notice_unable_delete_time_entry), flash[:error]
257 assert_not_nil TimeEntry.find_by_id(1)
279 assert_not_nil TimeEntry.find_by_id(1)
258 end
280 end
259
281
260 def test_index_all_projects
282 def test_index_all_projects
261 get :index
283 get :index
262 assert_response :success
284 assert_response :success
263 assert_template 'index'
285 assert_template 'index'
264 assert_not_nil assigns(:total_hours)
286 assert_not_nil assigns(:total_hours)
265 assert_equal "162.90", "%.2f" % assigns(:total_hours)
287 assert_equal "162.90", "%.2f" % assigns(:total_hours)
266 assert_tag :form,
288 assert_tag :form,
267 :attributes => {:action => "/time_entries", :id => 'query_form'}
289 :attributes => {:action => "/time_entries", :id => 'query_form'}
268 end
290 end
269
291
270 def test_index_at_project_level
292 def test_index_at_project_level
271 get :index, :project_id => 'ecookbook'
293 get :index, :project_id => 'ecookbook'
272 assert_response :success
294 assert_response :success
273 assert_template 'index'
295 assert_template 'index'
274 assert_not_nil assigns(:entries)
296 assert_not_nil assigns(:entries)
275 assert_equal 4, assigns(:entries).size
297 assert_equal 4, assigns(:entries).size
276 # project and subproject
298 # project and subproject
277 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
299 assert_equal [1, 3], assigns(:entries).collect(&:project_id).uniq.sort
278 assert_not_nil assigns(:total_hours)
300 assert_not_nil assigns(:total_hours)
279 assert_equal "162.90", "%.2f" % assigns(:total_hours)
301 assert_equal "162.90", "%.2f" % assigns(:total_hours)
280 # display all time by default
302 # display all time by default
281 assert_nil assigns(:from)
303 assert_nil assigns(:from)
282 assert_nil assigns(:to)
304 assert_nil assigns(:to)
283 assert_tag :form,
305 assert_tag :form,
284 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
306 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
285 end
307 end
286
308
287 def test_index_at_project_level_with_date_range
309 def test_index_at_project_level_with_date_range
288 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
310 get :index, :project_id => 'ecookbook', :from => '2007-03-20', :to => '2007-04-30'
289 assert_response :success
311 assert_response :success
290 assert_template 'index'
312 assert_template 'index'
291 assert_not_nil assigns(:entries)
313 assert_not_nil assigns(:entries)
292 assert_equal 3, assigns(:entries).size
314 assert_equal 3, assigns(:entries).size
293 assert_not_nil assigns(:total_hours)
315 assert_not_nil assigns(:total_hours)
294 assert_equal "12.90", "%.2f" % assigns(:total_hours)
316 assert_equal "12.90", "%.2f" % assigns(:total_hours)
295 assert_equal '2007-03-20'.to_date, assigns(:from)
317 assert_equal '2007-03-20'.to_date, assigns(:from)
296 assert_equal '2007-04-30'.to_date, assigns(:to)
318 assert_equal '2007-04-30'.to_date, assigns(:to)
297 assert_tag :form,
319 assert_tag :form,
298 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
320 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
299 end
321 end
300
322
301 def test_index_at_project_level_with_period
323 def test_index_at_project_level_with_period
302 get :index, :project_id => 'ecookbook', :period => '7_days'
324 get :index, :project_id => 'ecookbook', :period => '7_days'
303 assert_response :success
325 assert_response :success
304 assert_template 'index'
326 assert_template 'index'
305 assert_not_nil assigns(:entries)
327 assert_not_nil assigns(:entries)
306 assert_not_nil assigns(:total_hours)
328 assert_not_nil assigns(:total_hours)
307 assert_equal Date.today - 7, assigns(:from)
329 assert_equal Date.today - 7, assigns(:from)
308 assert_equal Date.today, assigns(:to)
330 assert_equal Date.today, assigns(:to)
309 assert_tag :form,
331 assert_tag :form,
310 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
332 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
311 end
333 end
312
334
313 def test_index_one_day
335 def test_index_one_day
314 get :index, :project_id => 'ecookbook', :from => "2007-03-23", :to => "2007-03-23"
336 get :index, :project_id => 'ecookbook', :from => "2007-03-23", :to => "2007-03-23"
315 assert_response :success
337 assert_response :success
316 assert_template 'index'
338 assert_template 'index'
317 assert_not_nil assigns(:total_hours)
339 assert_not_nil assigns(:total_hours)
318 assert_equal "4.25", "%.2f" % assigns(:total_hours)
340 assert_equal "4.25", "%.2f" % assigns(:total_hours)
319 assert_tag :form,
341 assert_tag :form,
320 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
342 :attributes => {:action => "/projects/ecookbook/time_entries", :id => 'query_form'}
321 end
343 end
322
344
323 def test_index_today
345 def test_index_today
324 Date.stubs(:today).returns('2011-12-15'.to_date)
346 Date.stubs(:today).returns('2011-12-15'.to_date)
325 get :index, :period => 'today'
347 get :index, :period => 'today'
326 assert_equal '2011-12-15'.to_date, assigns(:from)
348 assert_equal '2011-12-15'.to_date, assigns(:from)
327 assert_equal '2011-12-15'.to_date, assigns(:to)
349 assert_equal '2011-12-15'.to_date, assigns(:to)
328 end
350 end
329
351
330 def test_index_yesterday
352 def test_index_yesterday
331 Date.stubs(:today).returns('2011-12-15'.to_date)
353 Date.stubs(:today).returns('2011-12-15'.to_date)
332 get :index, :period => 'yesterday'
354 get :index, :period => 'yesterday'
333 assert_equal '2011-12-14'.to_date, assigns(:from)
355 assert_equal '2011-12-14'.to_date, assigns(:from)
334 assert_equal '2011-12-14'.to_date, assigns(:to)
356 assert_equal '2011-12-14'.to_date, assigns(:to)
335 end
357 end
336
358
337 def test_index_current_week
359 def test_index_current_week
338 Date.stubs(:today).returns('2011-12-15'.to_date)
360 Date.stubs(:today).returns('2011-12-15'.to_date)
339 get :index, :period => 'current_week'
361 get :index, :period => 'current_week'
340 assert_equal '2011-12-12'.to_date, assigns(:from)
362 assert_equal '2011-12-12'.to_date, assigns(:from)
341 assert_equal '2011-12-18'.to_date, assigns(:to)
363 assert_equal '2011-12-18'.to_date, assigns(:to)
342 end
364 end
343
365
344 def test_index_last_week
366 def test_index_last_week
345 Date.stubs(:today).returns('2011-12-15'.to_date)
367 Date.stubs(:today).returns('2011-12-15'.to_date)
346 get :index, :period => 'current_week'
368 get :index, :period => 'current_week'
347 assert_equal '2011-12-05'.to_date, assigns(:from)
369 assert_equal '2011-12-05'.to_date, assigns(:from)
348 assert_equal '2011-12-11'.to_date, assigns(:to)
370 assert_equal '2011-12-11'.to_date, assigns(:to)
349 end
371 end
350
372
351 def test_index_last_week
373 def test_index_last_week
352 Date.stubs(:today).returns('2011-12-15'.to_date)
374 Date.stubs(:today).returns('2011-12-15'.to_date)
353 get :index, :period => 'last_week'
375 get :index, :period => 'last_week'
354 assert_equal '2011-12-05'.to_date, assigns(:from)
376 assert_equal '2011-12-05'.to_date, assigns(:from)
355 assert_equal '2011-12-11'.to_date, assigns(:to)
377 assert_equal '2011-12-11'.to_date, assigns(:to)
356 end
378 end
357
379
358 def test_index_7_days
380 def test_index_7_days
359 Date.stubs(:today).returns('2011-12-15'.to_date)
381 Date.stubs(:today).returns('2011-12-15'.to_date)
360 get :index, :period => '7_days'
382 get :index, :period => '7_days'
361 assert_equal '2011-12-08'.to_date, assigns(:from)
383 assert_equal '2011-12-08'.to_date, assigns(:from)
362 assert_equal '2011-12-15'.to_date, assigns(:to)
384 assert_equal '2011-12-15'.to_date, assigns(:to)
363 end
385 end
364
386
365 def test_index_current_month
387 def test_index_current_month
366 Date.stubs(:today).returns('2011-12-15'.to_date)
388 Date.stubs(:today).returns('2011-12-15'.to_date)
367 get :index, :period => 'current_month'
389 get :index, :period => 'current_month'
368 assert_equal '2011-12-01'.to_date, assigns(:from)
390 assert_equal '2011-12-01'.to_date, assigns(:from)
369 assert_equal '2011-12-31'.to_date, assigns(:to)
391 assert_equal '2011-12-31'.to_date, assigns(:to)
370 end
392 end
371
393
372 def test_index_last_month
394 def test_index_last_month
373 Date.stubs(:today).returns('2011-12-15'.to_date)
395 Date.stubs(:today).returns('2011-12-15'.to_date)
374 get :index, :period => 'last_month'
396 get :index, :period => 'last_month'
375 assert_equal '2011-11-01'.to_date, assigns(:from)
397 assert_equal '2011-11-01'.to_date, assigns(:from)
376 assert_equal '2011-11-30'.to_date, assigns(:to)
398 assert_equal '2011-11-30'.to_date, assigns(:to)
377 end
399 end
378
400
379 def test_index_30_days
401 def test_index_30_days
380 Date.stubs(:today).returns('2011-12-15'.to_date)
402 Date.stubs(:today).returns('2011-12-15'.to_date)
381 get :index, :period => '30_days'
403 get :index, :period => '30_days'
382 assert_equal '2011-11-15'.to_date, assigns(:from)
404 assert_equal '2011-11-15'.to_date, assigns(:from)
383 assert_equal '2011-12-15'.to_date, assigns(:to)
405 assert_equal '2011-12-15'.to_date, assigns(:to)
384 end
406 end
385
407
386 def test_index_current_year
408 def test_index_current_year
387 Date.stubs(:today).returns('2011-12-15'.to_date)
409 Date.stubs(:today).returns('2011-12-15'.to_date)
388 get :index, :period => 'current_year'
410 get :index, :period => 'current_year'
389 assert_equal '2011-01-01'.to_date, assigns(:from)
411 assert_equal '2011-01-01'.to_date, assigns(:from)
390 assert_equal '2011-12-31'.to_date, assigns(:to)
412 assert_equal '2011-12-31'.to_date, assigns(:to)
391 end
413 end
392
414
393 def test_index_at_issue_level
415 def test_index_at_issue_level
394 get :index, :issue_id => 1
416 get :index, :issue_id => 1
395 assert_response :success
417 assert_response :success
396 assert_template 'index'
418 assert_template 'index'
397 assert_not_nil assigns(:entries)
419 assert_not_nil assigns(:entries)
398 assert_equal 2, assigns(:entries).size
420 assert_equal 2, assigns(:entries).size
399 assert_not_nil assigns(:total_hours)
421 assert_not_nil assigns(:total_hours)
400 assert_equal 154.25, assigns(:total_hours)
422 assert_equal 154.25, assigns(:total_hours)
401 # display all time
423 # display all time
402 assert_nil assigns(:from)
424 assert_nil assigns(:from)
403 assert_nil assigns(:to)
425 assert_nil assigns(:to)
404 # TODO: remove /projects/:project_id/issues/:issue_id/time_entries routes
426 # TODO: remove /projects/:project_id/issues/:issue_id/time_entries routes
405 # to use /issues/:issue_id/time_entries
427 # to use /issues/:issue_id/time_entries
406 assert_tag :form,
428 assert_tag :form,
407 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
429 :attributes => {:action => "/projects/ecookbook/issues/1/time_entries", :id => 'query_form'}
408 end
430 end
409
431
410 def test_index_atom_feed
432 def test_index_atom_feed
411 get :index, :project_id => 1, :format => 'atom'
433 get :index, :project_id => 1, :format => 'atom'
412 assert_response :success
434 assert_response :success
413 assert_equal 'application/atom+xml', @response.content_type
435 assert_equal 'application/atom+xml', @response.content_type
414 assert_not_nil assigns(:items)
436 assert_not_nil assigns(:items)
415 assert assigns(:items).first.is_a?(TimeEntry)
437 assert assigns(:items).first.is_a?(TimeEntry)
416 end
438 end
417
439
418 def test_index_all_projects_csv_export
440 def test_index_all_projects_csv_export
419 Setting.date_format = '%m/%d/%Y'
441 Setting.date_format = '%m/%d/%Y'
420 get :index, :format => 'csv'
442 get :index, :format => 'csv'
421 assert_response :success
443 assert_response :success
422 assert_equal 'text/csv', @response.content_type
444 assert_equal 'text/csv', @response.content_type
423 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
445 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
424 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
446 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
425 end
447 end
426
448
427 def test_index_csv_export
449 def test_index_csv_export
428 Setting.date_format = '%m/%d/%Y'
450 Setting.date_format = '%m/%d/%Y'
429 get :index, :project_id => 1, :format => 'csv'
451 get :index, :project_id => 1, :format => 'csv'
430 assert_response :success
452 assert_response :success
431 assert_equal 'text/csv', @response.content_type
453 assert_equal 'text/csv', @response.content_type
432 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
454 assert @response.body.include?("Date,User,Activity,Project,Issue,Tracker,Subject,Hours,Comment,Overtime\n")
433 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
455 assert @response.body.include?("\n04/21/2007,redMine Admin,Design,eCookbook,3,Bug,Error 281 when updating a recipe,1.0,\"\",\"\"\n")
434 end
456 end
435
457
436 def test_csv_big_5
458 def test_csv_big_5
437 user = User.find_by_id(3)
459 user = User.find_by_id(3)
438 user.language = "zh-TW"
460 user.language = "zh-TW"
439 assert user.save
461 assert user.save
440 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
462 str_utf8 = "\xe4\xb8\x80\xe6\x9c\x88"
441 str_big5 = "\xa4@\xa4\xeb"
463 str_big5 = "\xa4@\xa4\xeb"
442 if str_utf8.respond_to?(:force_encoding)
464 if str_utf8.respond_to?(:force_encoding)
443 str_utf8.force_encoding('UTF-8')
465 str_utf8.force_encoding('UTF-8')
444 str_big5.force_encoding('Big5')
466 str_big5.force_encoding('Big5')
445 end
467 end
446 @request.session[:user_id] = 3
468 @request.session[:user_id] = 3
447 post :create, :project_id => 1,
469 post :create, :project_id => 1,
448 :time_entry => {:comments => str_utf8,
470 :time_entry => {:comments => str_utf8,
449 # Not the default activity
471 # Not the default activity
450 :activity_id => '11',
472 :activity_id => '11',
451 :issue_id => '',
473 :issue_id => '',
452 :spent_on => '2011-11-10',
474 :spent_on => '2011-11-10',
453 :hours => '7.3'}
475 :hours => '7.3'}
454 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
476 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
455
477
456 t = TimeEntry.find_by_comments(str_utf8)
478 t = TimeEntry.find_by_comments(str_utf8)
457 assert_not_nil t
479 assert_not_nil t
458 assert_equal 11, t.activity_id
480 assert_equal 11, t.activity_id
459 assert_equal 7.3, t.hours
481 assert_equal 7.3, t.hours
460 assert_equal 3, t.user_id
482 assert_equal 3, t.user_id
461
483
462 get :index, :project_id => 1, :format => 'csv',
484 get :index, :project_id => 1, :format => 'csv',
463 :from => '2011-11-10', :to => '2011-11-10'
485 :from => '2011-11-10', :to => '2011-11-10'
464 assert_response :success
486 assert_response :success
465 assert_equal 'text/csv', @response.content_type
487 assert_equal 'text/csv', @response.content_type
466 ar = @response.body.chomp.split("\n")
488 ar = @response.body.chomp.split("\n")
467 s1 = "\xa4\xe9\xb4\xc1"
489 s1 = "\xa4\xe9\xb4\xc1"
468 if str_utf8.respond_to?(:force_encoding)
490 if str_utf8.respond_to?(:force_encoding)
469 s1.force_encoding('Big5')
491 s1.force_encoding('Big5')
470 end
492 end
471 assert ar[0].include?(s1)
493 assert ar[0].include?(s1)
472 assert ar[1].include?(str_big5)
494 assert ar[1].include?(str_big5)
473 end
495 end
474
496
475 def test_csv_cannot_convert_should_be_replaced_big_5
497 def test_csv_cannot_convert_should_be_replaced_big_5
476 user = User.find_by_id(3)
498 user = User.find_by_id(3)
477 user.language = "zh-TW"
499 user.language = "zh-TW"
478 assert user.save
500 assert user.save
479 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
501 str_utf8 = "\xe4\xbb\xa5\xe5\x86\x85"
480 if str_utf8.respond_to?(:force_encoding)
502 if str_utf8.respond_to?(:force_encoding)
481 str_utf8.force_encoding('UTF-8')
503 str_utf8.force_encoding('UTF-8')
482 end
504 end
483 @request.session[:user_id] = 3
505 @request.session[:user_id] = 3
484 post :create, :project_id => 1,
506 post :create, :project_id => 1,
485 :time_entry => {:comments => str_utf8,
507 :time_entry => {:comments => str_utf8,
486 # Not the default activity
508 # Not the default activity
487 :activity_id => '11',
509 :activity_id => '11',
488 :issue_id => '',
510 :issue_id => '',
489 :spent_on => '2011-11-10',
511 :spent_on => '2011-11-10',
490 :hours => '7.3'}
512 :hours => '7.3'}
491 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
513 assert_redirected_to :action => 'index', :project_id => 'ecookbook'
492
514
493 t = TimeEntry.find_by_comments(str_utf8)
515 t = TimeEntry.find_by_comments(str_utf8)
494 assert_not_nil t
516 assert_not_nil t
495 assert_equal 11, t.activity_id
517 assert_equal 11, t.activity_id
496 assert_equal 7.3, t.hours
518 assert_equal 7.3, t.hours
497 assert_equal 3, t.user_id
519 assert_equal 3, t.user_id
498
520
499 get :index, :project_id => 1, :format => 'csv',
521 get :index, :project_id => 1, :format => 'csv',
500 :from => '2011-11-10', :to => '2011-11-10'
522 :from => '2011-11-10', :to => '2011-11-10'
501 assert_response :success
523 assert_response :success
502 assert_equal 'text/csv', @response.content_type
524 assert_equal 'text/csv', @response.content_type
503 ar = @response.body.chomp.split("\n")
525 ar = @response.body.chomp.split("\n")
504 s1 = "\xa4\xe9\xb4\xc1"
526 s1 = "\xa4\xe9\xb4\xc1"
505 if str_utf8.respond_to?(:force_encoding)
527 if str_utf8.respond_to?(:force_encoding)
506 s1.force_encoding('Big5')
528 s1.force_encoding('Big5')
507 end
529 end
508 assert ar[0].include?(s1)
530 assert ar[0].include?(s1)
509 s2 = ar[1].split(",")[8]
531 s2 = ar[1].split(",")[8]
510 if s2.respond_to?(:force_encoding)
532 if s2.respond_to?(:force_encoding)
511 s3 = "\xa5H?"
533 s3 = "\xa5H?"
512 s3.force_encoding('Big5')
534 s3.force_encoding('Big5')
513 assert_equal s3, s2
535 assert_equal s3, s2
514 elsif RUBY_PLATFORM == 'java'
536 elsif RUBY_PLATFORM == 'java'
515 assert_equal "??", s2
537 assert_equal "??", s2
516 else
538 else
517 assert_equal "\xa5H???", s2
539 assert_equal "\xa5H???", s2
518 end
540 end
519 end
541 end
520
542
521 def test_csv_tw
543 def test_csv_tw
522 with_settings :default_language => "zh-TW" do
544 with_settings :default_language => "zh-TW" do
523 str1 = "test_csv_tw"
545 str1 = "test_csv_tw"
524 user = User.find_by_id(3)
546 user = User.find_by_id(3)
525 te1 = TimeEntry.create(:spent_on => '2011-11-10',
547 te1 = TimeEntry.create(:spent_on => '2011-11-10',
526 :hours => 999.9,
548 :hours => 999.9,
527 :project => Project.find(1),
549 :project => Project.find(1),
528 :user => user,
550 :user => user,
529 :activity => TimeEntryActivity.find_by_name('Design'),
551 :activity => TimeEntryActivity.find_by_name('Design'),
530 :comments => str1)
552 :comments => str1)
531 te2 = TimeEntry.find_by_comments(str1)
553 te2 = TimeEntry.find_by_comments(str1)
532 assert_not_nil te2
554 assert_not_nil te2
533 assert_equal 999.9, te2.hours
555 assert_equal 999.9, te2.hours
534 assert_equal 3, te2.user_id
556 assert_equal 3, te2.user_id
535
557
536 get :index, :project_id => 1, :format => 'csv',
558 get :index, :project_id => 1, :format => 'csv',
537 :from => '2011-11-10', :to => '2011-11-10'
559 :from => '2011-11-10', :to => '2011-11-10'
538 assert_response :success
560 assert_response :success
539 assert_equal 'text/csv', @response.content_type
561 assert_equal 'text/csv', @response.content_type
540
562
541 ar = @response.body.chomp.split("\n")
563 ar = @response.body.chomp.split("\n")
542 s2 = ar[1].split(",")[7]
564 s2 = ar[1].split(",")[7]
543 assert_equal '999.9', s2
565 assert_equal '999.9', s2
544
566
545 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
567 str_tw = "Traditional Chinese (\xe7\xb9\x81\xe9\xab\x94\xe4\xb8\xad\xe6\x96\x87)"
546 if str_tw.respond_to?(:force_encoding)
568 if str_tw.respond_to?(:force_encoding)
547 str_tw.force_encoding('UTF-8')
569 str_tw.force_encoding('UTF-8')
548 end
570 end
549 assert_equal str_tw, l(:general_lang_name)
571 assert_equal str_tw, l(:general_lang_name)
550 assert_equal ',', l(:general_csv_separator)
572 assert_equal ',', l(:general_csv_separator)
551 assert_equal '.', l(:general_csv_decimal_separator)
573 assert_equal '.', l(:general_csv_decimal_separator)
552 end
574 end
553 end
575 end
554
576
555 def test_csv_fr
577 def test_csv_fr
556 with_settings :default_language => "fr" do
578 with_settings :default_language => "fr" do
557 str1 = "test_csv_fr"
579 str1 = "test_csv_fr"
558 user = User.find_by_id(3)
580 user = User.find_by_id(3)
559 te1 = TimeEntry.create(:spent_on => '2011-11-10',
581 te1 = TimeEntry.create(:spent_on => '2011-11-10',
560 :hours => 999.9,
582 :hours => 999.9,
561 :project => Project.find(1),
583 :project => Project.find(1),
562 :user => user,
584 :user => user,
563 :activity => TimeEntryActivity.find_by_name('Design'),
585 :activity => TimeEntryActivity.find_by_name('Design'),
564 :comments => str1)
586 :comments => str1)
565 te2 = TimeEntry.find_by_comments(str1)
587 te2 = TimeEntry.find_by_comments(str1)
566 assert_not_nil te2
588 assert_not_nil te2
567 assert_equal 999.9, te2.hours
589 assert_equal 999.9, te2.hours
568 assert_equal 3, te2.user_id
590 assert_equal 3, te2.user_id
569
591
570 get :index, :project_id => 1, :format => 'csv',
592 get :index, :project_id => 1, :format => 'csv',
571 :from => '2011-11-10', :to => '2011-11-10'
593 :from => '2011-11-10', :to => '2011-11-10'
572 assert_response :success
594 assert_response :success
573 assert_equal 'text/csv', @response.content_type
595 assert_equal 'text/csv', @response.content_type
574
596
575 ar = @response.body.chomp.split("\n")
597 ar = @response.body.chomp.split("\n")
576 s2 = ar[1].split(";")[7]
598 s2 = ar[1].split(";")[7]
577 assert_equal '999,9', s2
599 assert_equal '999,9', s2
578
600
579 str_fr = "Fran\xc3\xa7ais"
601 str_fr = "Fran\xc3\xa7ais"
580 if str_fr.respond_to?(:force_encoding)
602 if str_fr.respond_to?(:force_encoding)
581 str_fr.force_encoding('UTF-8')
603 str_fr.force_encoding('UTF-8')
582 end
604 end
583 assert_equal str_fr, l(:general_lang_name)
605 assert_equal str_fr, l(:general_lang_name)
584 assert_equal ';', l(:general_csv_separator)
606 assert_equal ';', l(:general_csv_separator)
585 assert_equal ',', l(:general_csv_decimal_separator)
607 assert_equal ',', l(:general_csv_decimal_separator)
586 end
608 end
587 end
609 end
588 end
610 end
General Comments 0
You need to be logged in to leave comments. Login now