Feature #3848 » 0001-Allow-the-current-user-to-log-time-for-other-users.patch
| app/controllers/timelog_controller.rb | ||
|---|---|---|
| 26 | 26 |
before_action :find_optional_issue, :only => [:new, :create] |
| 27 | 27 |
before_action :find_optional_project, :only => [:index, :report] |
| 28 | 28 | |
| 29 |
before_action :authorize_logging_time_for_other_users, :only => [:create, :update] |
|
| 30 | ||
| 29 | 31 |
accept_rss_auth :index |
| 30 | 32 |
accept_api_auth :index, :show, :create, :update, :destroy |
| 31 | 33 | |
| ... | ... | |
| 90 | 92 |
end |
| 91 | 93 | |
| 92 | 94 |
def new |
| 93 |
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today)
|
|
| 95 |
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :author => User.current, :spent_on => User.current.today)
|
|
| 94 | 96 |
@time_entry.safe_attributes = params[:time_entry] |
| 95 | 97 |
end |
| 96 | 98 | |
| 97 | 99 |
def create |
| 98 |
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :user => User.current, :spent_on => User.current.today) |
|
| 100 |
@time_entry ||= TimeEntry.new(:project => @project, :issue => @issue, :author => User.current, :user => User.current, :spent_on => User.current.today)
|
|
| 99 | 101 |
@time_entry.safe_attributes = params[:time_entry] |
| 100 | 102 |
if @time_entry.project && !User.current.allowed_to?(:log_time, @time_entry.project) |
| 101 | 103 |
render_403 |
| ... | ... | |
| 144 | 146 | |
| 145 | 147 |
def update |
| 146 | 148 |
@time_entry.safe_attributes = params[:time_entry] |
| 147 | ||
| 148 | 149 |
call_hook(:controller_timelog_edit_before_save, { :params => params, :time_entry => @time_entry })
|
| 149 | 150 | |
| 150 | 151 |
if @time_entry.save |
| ... | ... | |
| 243 | 244 |
end |
| 244 | 245 |
end |
| 245 | 246 | |
| 247 |
def authorize_logging_time_for_other_users |
|
| 248 |
if !User.current.allowed_to?(:log_time_for_other_users, @project) && params['time_entry'].present? && params['time_entry']['user_id'].present? && params['time_entry']['user_id'].to_i != User.current.id |
|
| 249 |
render_error :message => l(:error_not_allowed_to_log_time_for_other_users), :status => 403 |
|
| 250 |
return false |
|
| 251 |
end |
|
| 252 |
end |
|
| 253 | ||
| 246 | 254 |
def find_time_entries |
| 247 | 255 |
@time_entries = TimeEntry.where(:id => params[:id] || params[:ids]). |
| 248 | 256 |
preload(:project => :time_entry_activities). |
| app/helpers/timelog_helper.rb | ||
|---|---|---|
| 42 | 42 |
collection |
| 43 | 43 |
end |
| 44 | 44 | |
| 45 |
def user_collection_for_select_options(time_entry) |
|
| 46 |
collection = time_entry.assignable_users |
|
| 47 |
principals_options_for_select(collection, time_entry.user_id) |
|
| 48 |
end |
|
| 49 | ||
| 45 | 50 |
def select_hours(data, criteria, value) |
| 46 | 51 |
if value.to_s.empty? |
| 47 | 52 |
data.select {|row| row[criteria].blank? }
|
| app/models/time_entry.rb | ||
|---|---|---|
| 22 | 22 |
belongs_to :project |
| 23 | 23 |
belongs_to :issue |
| 24 | 24 |
belongs_to :user |
| 25 |
belongs_to :author, :class_name => 'User' |
|
| 25 | 26 |
belongs_to :activity, :class_name => 'TimeEntryActivity' |
| 26 | 27 | |
| 27 | 28 |
acts_as_customizable |
| ... | ... | |
| 39 | 40 |
:author_key => :user_id, |
| 40 | 41 |
:scope => joins(:project).preload(:project) |
| 41 | 42 | |
| 42 |
validates_presence_of :user_id, :activity_id, :project_id, :hours, :spent_on |
|
| 43 |
validates_presence_of :author_id, :user_id, :activity_id, :project_id, :hours, :spent_on
|
|
| 43 | 44 |
validates_presence_of :issue_id, :if => lambda { Setting.timelog_required_fields.include?('issue_id') }
|
| 44 | 45 |
validates_presence_of :comments, :if => lambda { Setting.timelog_required_fields.include?('comments') }
|
| 45 | 46 |
validates_numericality_of :hours, :allow_nil => true, :message => :invalid |
| 46 | 47 |
validates_length_of :comments, :maximum => 1024, :allow_nil => true |
| 47 | 48 |
validates :spent_on, :date => true |
| 48 | 49 |
before_validation :set_project_if_nil |
| 50 |
before_validation :set_author_if_nil |
|
| 49 | 51 |
validate :validate_time_entry |
| 50 | 52 | |
| 51 | 53 |
scope :visible, lambda {|*args|
|
| ... | ... | |
| 60 | 62 |
where("#{Issue.table_name}.root_id = #{issue.root_id} AND #{Issue.table_name}.lft >= #{issue.lft} AND #{Issue.table_name}.rgt <= #{issue.rgt}")
|
| 61 | 63 |
} |
| 62 | 64 | |
| 63 |
safe_attributes 'hours', 'comments', 'project_id', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields' |
|
| 65 |
safe_attributes 'user_id', 'hours', 'comments', 'project_id', 'issue_id', 'activity_id', 'spent_on', 'custom_field_values', 'custom_fields'
|
|
| 64 | 66 | |
| 65 | 67 |
# Returns a SQL conditions string used to find all time entries visible by the specified user |
| 66 | 68 |
def self.visible_condition(user, options={})
|
| ... | ... | |
| 119 | 121 |
self.project = issue.project if issue && project.nil? |
| 120 | 122 |
end |
| 121 | 123 | |
| 124 |
def set_author_if_nil |
|
| 125 |
self.author = User.current if author.nil? |
|
| 126 |
end |
|
| 127 | ||
| 122 | 128 |
def validate_time_entry |
| 123 | 129 |
if hours |
| 124 | 130 |
errors.add :hours, :invalid if hours < 0 |
| ... | ... | |
| 134 | 140 |
end |
| 135 | 141 |
end |
| 136 | 142 |
errors.add :project_id, :invalid if project.nil? |
| 143 |
errors.add :user_id, :invalid if (user_id != author_id && !self.assignable_users.map(&:id).include?(user_id)) |
|
| 137 | 144 |
errors.add :issue_id, :invalid if (issue_id && !issue) || (issue && project!=issue.project) || @invalid_issue_id |
| 138 | 145 |
errors.add :activity_id, :inclusion if activity_id_changed? && project && !project.activities.include?(activity) |
| 139 | 146 |
end |
| ... | ... | |
| 177 | 184 |
editable_custom_field_values(user).map(&:custom_field).uniq |
| 178 | 185 |
end |
| 179 | 186 | |
| 187 |
def assignable_users |
|
| 188 |
users = [] |
|
| 189 |
if project |
|
| 190 |
users = project.members.active.preload(:user) |
|
| 191 |
users = users.map(&:user).select{ |u| u.allowed_to?(:log_time, project) }
|
|
| 192 |
end |
|
| 193 |
users << User.current if User.current.logged? && !users.include?(User.current) |
|
| 194 |
users |
|
| 195 |
end |
|
| 196 | ||
| 180 | 197 |
private |
| 181 | 198 | |
| 182 | 199 |
# Returns the hours that were logged in other time entries for the same user and the same day |
| app/models/time_entry_query.rb | ||
|---|---|---|
| 25 | 25 |
QueryColumn.new(:spent_on, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :default_order => 'desc', :groupable => true),
|
| 26 | 26 |
QueryColumn.new(:created_on, :sortable => "#{TimeEntry.table_name}.created_on", :default_order => 'desc'),
|
| 27 | 27 |
QueryColumn.new(:tweek, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :caption => :label_week),
|
| 28 |
QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement}),
|
|
| 28 | 29 |
QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
|
| 29 | 30 |
QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true),
|
| 30 | 31 |
QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"),
|
| ... | ... | |
| 75 | 76 |
:type => :list_optional, :values => lambda { author_values }
|
| 76 | 77 |
) |
| 77 | 78 | |
| 79 |
add_available_filter("author_id",
|
|
| 80 |
:type => :list_optional, :values => lambda { author_values }
|
|
| 81 |
) |
|
| 82 | ||
| 78 | 83 |
activities = (project ? project.activities : TimeEntryActivity.shared) |
| 79 | 84 |
add_available_filter("activity_id",
|
| 80 | 85 |
:type => :list, :values => activities.map {|a| [a.name, a.id.to_s]}
|
| app/views/timelog/_form.html.erb | ||
|---|---|---|
| 16 | 16 |
<%= link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %> |
| 17 | 17 |
</span> |
| 18 | 18 |
</p> |
| 19 |
<% if User.current.allowed_to?(:log_time_for_other_users, @project) %> |
|
| 20 |
<p><%= f.select :user_id, user_collection_for_select_options(@time_entry), :required => true %></p> |
|
| 21 |
<% end %> |
|
| 19 | 22 |
<p><%= f.date_field :spent_on, :size => 10, :required => true %><%= calendar_for('time_entry_spent_on') %></p>
|
| 20 | 23 |
<p><%= f.hours_field :hours, :size => 6, :required => true %></p> |
| 21 | 24 |
<p><%= f.text_field :comments, :size => 100, :maxlength => 1024, :required => Setting.timelog_required_fields.include?('comments') %></p>
|
| config/locales/en.yml | ||
|---|---|---|
| 222 | 222 |
warning_fields_cleared_on_bulk_edit: "Changes will result in the automatic deletion of values from one or more fields on the selected objects" |
| 223 | 223 |
error_exceeds_maximum_hours_per_day: "Cannot log more than %{max_hours} hours on the same day (%{logged_hours} hours have already been logged)"
|
| 224 | 224 |
error_can_not_delete_auth_source: "This authentication mode is in use and cannot be deleted." |
| 225 |
error_not_allowed_to_log_time_for_other_users: "Your role is not allowed to log time for other users" |
|
| 225 | 226 | |
| 226 | 227 |
mail_subject_lost_password: "Your %{value} password"
|
| 227 | 228 |
mail_body_lost_password: 'To change your password, click on the following link:' |
| ... | ... | |
| 534 | 535 |
permission_manage_subtasks: Manage subtasks |
| 535 | 536 |
permission_manage_related_issues: Manage related issues |
| 536 | 537 |
permission_import_issues: Import issues |
| 538 |
permission_log_foreign_time: Log spent time for other users |
|
| 537 | 539 | |
| 538 | 540 |
project_module_issue_tracking: Issue tracking |
| 539 | 541 |
project_module_time_tracking: Time tracking |
| db/migrate/20180501132547_add_author_id_to_time_entries.rb | ||
|---|---|---|
| 1 |
class AddAuthorIdToTimeEntries < ActiveRecord::Migration[5.1] |
|
| 2 |
def up |
|
| 3 |
add_column :time_entries, :author_id, :integer, :default => nil, :after => :project_id |
|
| 4 |
# Copy existing user_id to author_id |
|
| 5 |
TimeEntry.update_all('author_id = user_id')
|
|
| 6 |
end |
|
| 7 | ||
| 8 |
def down |
|
| 9 |
remove_column :time_entries, :author_id |
|
| 10 |
end |
|
| 11 |
end |
|
| lib/redmine.rb | ||
|---|---|---|
| 126 | 126 |
map.permission :edit_time_entries, {:timelog => [:edit, :update, :destroy, :bulk_edit, :bulk_update]}, :require => :member
|
| 127 | 127 |
map.permission :edit_own_time_entries, {:timelog => [:edit, :update, :destroy,:bulk_edit, :bulk_update]}, :require => :loggedin
|
| 128 | 128 |
map.permission :manage_project_activities, {:projects => :settings, :project_enumerations => [:update, :destroy]}, :require => :member
|
| 129 |
map.permission :log_time_for_other_users, :require => :member |
|
| 129 | 130 |
end |
| 130 | 131 | |
| 131 | 132 |
map.project_module :news do |map| |
| test/fixtures/time_entries.yml | ||
|---|---|---|
| 1 |
---
|
|
| 2 |
time_entries_001:
|
|
| 1 |
--- |
|
| 2 |
time_entries_001: |
|
| 3 | 3 |
created_on: 2007-03-23 12:54:18 +01:00 |
| 4 | 4 |
tweek: 12 |
| 5 | 5 |
tmonth: 3 |
| ... | ... | |
| 12 | 12 |
id: 1 |
| 13 | 13 |
hours: 4.25 |
| 14 | 14 |
user_id: 2 |
| 15 |
author_id: 2 |
|
| 15 | 16 |
tyear: 2007 |
| 16 |
time_entries_002:
|
|
| 17 |
time_entries_002: |
|
| 17 | 18 |
created_on: 2007-03-23 14:11:04 +01:00 |
| 18 | 19 |
tweek: 11 |
| 19 | 20 |
tmonth: 3 |
| ... | ... | |
| 26 | 27 |
id: 2 |
| 27 | 28 |
hours: 150.0 |
| 28 | 29 |
user_id: 1 |
| 30 |
author_id: 1 |
|
| 29 | 31 |
tyear: 2007 |
| 30 |
time_entries_003:
|
|
| 32 |
time_entries_003: |
|
| 31 | 33 |
created_on: 2007-04-21 12:20:48 +02:00 |
| 32 | 34 |
tweek: 16 |
| 33 | 35 |
tmonth: 4 |
| ... | ... | |
| 40 | 42 |
id: 3 |
| 41 | 43 |
hours: 1.0 |
| 42 | 44 |
user_id: 1 |
| 45 |
author_id: 1 |
|
| 43 | 46 |
tyear: 2007 |
| 44 |
time_entries_004:
|
|
| 47 |
time_entries_004: |
|
| 45 | 48 |
created_on: 2007-04-22 12:20:48 +02:00 |
| 46 | 49 |
tweek: 16 |
| 47 | 50 |
tmonth: 4 |
| ... | ... | |
| 50 | 53 |
updated_on: 2007-04-22 12:20:48 +02:00 |
| 51 | 54 |
activity_id: 10 |
| 52 | 55 |
spent_on: 2007-04-22 |
| 53 |
issue_id:
|
|
| 56 |
issue_id: |
|
| 54 | 57 |
id: 4 |
| 55 | 58 |
hours: 7.65 |
| 56 | 59 |
user_id: 1 |
| 60 |
author_id: 1 |
|
| 57 | 61 |
tyear: 2007 |
| 58 |
time_entries_005:
|
|
| 62 |
time_entries_005: |
|
| 59 | 63 |
created_on: 2011-03-22 12:20:48 +02:00 |
| 60 | 64 |
tweek: 12 |
| 61 | 65 |
tmonth: 3 |
| ... | ... | |
| 64 | 68 |
updated_on: 2011-03-22 12:20:48 +02:00 |
| 65 | 69 |
activity_id: 10 |
| 66 | 70 |
spent_on: 2011-03-22 |
| 67 |
issue_id:
|
|
| 71 |
issue_id: |
|
| 68 | 72 |
id: 5 |
| 69 | 73 |
hours: 7.65 |
| 70 | 74 |
user_id: 1 |
| 75 |
author_id: 1 |
|
| 71 | 76 |
tyear: 2011 |
| 72 |
|
|
| 77 | ||
| test/functional/project_enumerations_controller_test.rb | ||
|---|---|---|
| 143 | 143 |
:enumerations => {
|
| 144 | 144 |
"9"=> {
|
| 145 | 145 |
"parent_id"=>"9", "custom_field_values"=> {
|
| 146 |
"7" => "1"}, "active"=>"0"} # Design, De-activate
|
|
| 147 |
|
|
| 146 |
"7" => "1"}, "active"=>"0"} # Design, De-activate |
|
| 147 | ||
| 148 | 148 |
} |
| 149 | 149 |
} |
| 150 | 150 |
assert_response :redirect |
| ... | ... | |
| 163 | 163 |
# TODO: Need to cause an exception on create but these tests |
| 164 | 164 |
# aren't setup for mocking. Just create a record now so the |
| 165 | 165 |
# second one is a dupicate |
| 166 |
user = User.find(1) |
|
| 166 | 167 |
parent = TimeEntryActivity.find(9) |
| 167 | 168 |
TimeEntryActivity.create!({:name => parent.name, :project_id => 1,
|
| 168 | 169 |
:position => parent.position, :active => true, :parent_id => 9}) |
| 169 |
TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => User.find(1),
|
|
| 170 |
TimeEntry.create!({:project_id => 1, :hours => 1.0, :user => user, :author => user,
|
|
| 170 | 171 |
:issue_id => 3, :activity_id => 10, :spent_on => '2009-01-01'}) |
| 171 | 172 |
assert_equal 3, TimeEntry.where(:activity_id => 9, :project_id => 1).count |
| 172 | 173 |
assert_equal 1, TimeEntry.where(:activity_id => 10, :project_id => 1).count |
| test/functional/timelog_controller_test.rb | ||
|---|---|---|
| 102 | 102 |
assert_select 'option', :text => 'Inactive Activity', :count => 0 |
| 103 | 103 |
end |
| 104 | 104 | |
| 105 |
def test_new_should_show_user_select_if_user_has_permission |
|
| 106 |
Role.find_by_name('Manager').add_permission! :log_time_for_other_users
|
|
| 107 |
@request.session[:user_id] = 2 |
|
| 108 | ||
| 109 |
get :new, :params => {:project_id => 1}
|
|
| 110 |
assert_response :success |
|
| 111 |
assert_select 'select[name=?]', 'time_entry[user_id]' do |
|
| 112 |
assert_select 'option', 3 |
|
| 113 |
assert_select 'option[value=?]', '2', 2 |
|
| 114 |
assert_select 'option[value=?]', '3', 1 |
|
| 115 |
# locked members should not be available |
|
| 116 |
assert_select 'option[value=?]', '4', 0 |
|
| 117 |
end |
|
| 118 |
end |
|
| 119 | ||
| 120 |
def test_new_user_select_should_include_current_user_if_is_logged |
|
| 121 |
@request.session[:user_id] = 1 |
|
| 122 | ||
| 123 |
get :new, :params => {:project_id => 1}
|
|
| 124 |
assert_response :success |
|
| 125 |
assert_select 'select[name=?]', 'time_entry[user_id]' do |
|
| 126 |
assert_select 'option[value=?]', '1', :text => '<< me >>' |
|
| 127 |
assert_select 'option[value=?]', '1', :text => 'Redmine Admin' |
|
| 128 |
end |
|
| 129 |
end |
|
| 130 | ||
| 131 |
def test_new_should_not_show_user_select_if_user_does_not_have_permission |
|
| 132 |
@request.session[:user_id] = 2 |
|
| 133 | ||
| 134 |
get :new, :params => {:project_id => 1}
|
|
| 135 |
assert_response :success |
|
| 136 |
assert_select 'select[name=?]', 'time_entry[user_id]', 0 |
|
| 137 |
end |
|
| 138 | ||
| 105 | 139 |
def test_post_new_as_js_should_update_activity_options |
| 106 | 140 |
@request.session[:user_id] = 3 |
| 107 | 141 |
post :new, :params => {:time_entry => {:project_id => 1}, :format => 'js'}
|
| ... | ... | |
| 268 | 302 |
assert !response.body.include?('issue_that_is_not_visible')
|
| 269 | 303 |
end |
| 270 | 304 | |
| 305 |
def test_create_for_other_user |
|
| 306 |
Role.find_by_name('Manager').add_permission! :log_time_for_other_users
|
|
| 307 |
@request.session[:user_id] = 2 |
|
| 308 | ||
| 309 |
post :create, :params => {
|
|
| 310 |
:project_id => 1, |
|
| 311 |
:time_entry => {:comments => 'Some work on TimelogControllerTest',
|
|
| 312 |
# Not the default activity |
|
| 313 |
:activity_id => '11', |
|
| 314 |
:spent_on => '2008-03-14', |
|
| 315 |
:issue_id => '1', |
|
| 316 |
:hours => '7.3', |
|
| 317 |
:user_id => '3' |
|
| 318 |
} |
|
| 319 |
} |
|
| 320 | ||
| 321 |
assert_redirected_to '/projects/ecookbook/time_entries' |
|
| 322 | ||
| 323 |
t = TimeEntry.last |
|
| 324 |
assert_equal 3, t.user_id |
|
| 325 |
assert_equal 2, t.author_id |
|
| 326 |
end |
|
| 327 | ||
| 328 |
def test_create_for_other_user_should_deny_for_user_without_permission |
|
| 329 |
Role.find_by_name('Manager').remove_permission! :log_time_for_other_users
|
|
| 330 |
@request.session[:user_id] = 2 |
|
| 331 | ||
| 332 |
post :create, :params => {
|
|
| 333 |
:project_id => 1, |
|
| 334 |
:time_entry => {:comments => 'Some work on TimelogControllerTest',
|
|
| 335 |
# Not the default activity |
|
| 336 |
:activity_id => '11', |
|
| 337 |
:spent_on => '2008-03-14', |
|
| 338 |
:issue_id => '1', |
|
| 339 |
:hours => '7.3', |
|
| 340 |
:user_id => '3' |
|
| 341 |
} |
|
| 342 |
} |
|
| 343 | ||
| 344 |
assert_response 403 |
|
| 345 |
assert_select 'p[id=?]', 'errorExplanation', :text => 'Your role is not allowed to log time for other users' |
|
| 346 |
end |
|
| 347 | ||
| 271 | 348 |
def test_create_and_continue_at_project_level |
| 272 | 349 |
@request.session[:user_id] = 2 |
| 273 | 350 |
assert_difference 'TimeEntry.count' do |
| ... | ... | |
| 533 | 610 |
assert_select_error /Issue is invalid/ |
| 534 | 611 |
end |
| 535 | 612 | |
| 613 |
def test_update_should_deny_changing_user_for_user_without_permission |
|
| 614 |
Role.find_by_name('Manager').remove_permission! :log_time_for_other_users
|
|
| 615 |
@request.session[:user_id] = 2 |
|
| 616 | ||
| 617 |
put :update, :params => {
|
|
| 618 |
:id => 3, |
|
| 619 |
:time_entry => {
|
|
| 620 |
:user_id => '3' |
|
| 621 |
} |
|
| 622 |
} |
|
| 623 | ||
| 624 |
assert_response 403 |
|
| 625 |
assert_select 'p[id=?]', 'errorExplanation', :text => 'Your role is not allowed to log time for other users' |
|
| 626 |
end |
|
| 627 | ||
| 536 | 628 |
def test_get_bulk_edit |
| 537 | 629 |
@request.session[:user_id] = 2 |
| 538 | 630 | |
| ... | ... | |
| 899 | 991 |
end |
| 900 | 992 | |
| 901 | 993 |
def test_index_should_sort_by_spent_on_and_created_on |
| 902 |
t1 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10) |
|
| 903 |
t2 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10) |
|
| 904 |
t3 = TimeEntry.create!(:user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10) |
|
| 994 |
t1 = TimeEntry.create!(:author => User.find(1), :user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:00:00', :activity_id => 10)
|
|
| 995 |
t2 = TimeEntry.create!(:author => User.find(1), :user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-16', :created_on => '2012-06-16 20:05:00', :activity_id => 10)
|
|
| 996 |
t3 = TimeEntry.create!(:author => User.find(1), :user => User.find(1), :project => Project.find(1), :hours => 1, :spent_on => '2012-06-15', :created_on => '2012-06-16 20:10:00', :activity_id => 10)
|
|
| 905 | 997 | |
| 906 | 998 |
get :index, :params => {
|
| 907 | 999 |
:project_id => 1, |
| ... | ... | |
| 1046 | 1138 |
assert_select 'td.issue-category', :text => 'Printing' |
| 1047 | 1139 |
end |
| 1048 | 1140 | |
| 1141 |
def test_index_with_author_filter |
|
| 1142 |
get :index, :params => {
|
|
| 1143 |
:project_id => 'ecookbook', |
|
| 1144 |
:f => ['author_id'], |
|
| 1145 |
:op => {'author_id' => '='},
|
|
| 1146 |
:v => {'author_id' => ['2']}
|
|
| 1147 |
} |
|
| 1148 |
assert_response :success |
|
| 1149 |
assert_equal ['1'], css_select('input[name="ids[]"]').map {|e| e.attr('value')}
|
|
| 1150 |
end |
|
| 1151 | ||
| 1152 |
def test_index_with_author_column |
|
| 1153 |
get :index, :params => {
|
|
| 1154 |
:project_id => 'ecookbook', |
|
| 1155 |
:c => %w(project spent_on issue comments hours author) |
|
| 1156 |
} |
|
| 1157 | ||
| 1158 |
assert_response :success |
|
| 1159 |
assert_select 'td.author', :text => 'Redmine Admin' |
|
| 1160 |
end |
|
| 1161 | ||
| 1049 | 1162 |
def test_index_with_issue_category_sort |
| 1050 | 1163 |
issue = Issue.find(3) |
| 1051 | 1164 |
issue.category_id = 2 |
| test/object_helpers.rb | ||
|---|---|---|
| 142 | 142 |
def TimeEntry.generate(attributes={})
|
| 143 | 143 |
entry = TimeEntry.new(attributes) |
| 144 | 144 |
entry.user ||= User.find(2) |
| 145 |
entry.author ||= entry.user |
|
| 145 | 146 |
entry.issue ||= Issue.find(1) unless entry.project |
| 146 | 147 |
entry.project ||= entry.issue.project |
| 147 | 148 |
entry.activity ||= TimeEntryActivity.first |
| test/unit/lib/redmine/export/pdf/issues_pdf_test.rb | ||
|---|---|---|
| 27 | 27 |
query = IssueQuery.new(:project => Project.find(1), :name => '_') |
| 28 | 28 |
query.column_names = [:subject, :spent_hours] |
| 29 | 29 |
issue = Issue.find(2) |
| 30 |
TimeEntry.create(:spent_on => Date.today, :hours => 4.3432, :user => User.find(1), |
|
| 30 |
user = User.find(1) |
|
| 31 |
time_entry = TimeEntry.create!(:spent_on => Date.today, :hours => 4.3432, :user => user, :author => user, |
|
| 31 | 32 |
:project_id => 1, :issue => issue, :activity => TimeEntryActivity.first) |
| 33 | ||
| 32 | 34 |
results = fetch_row_values(issue, query, 0) |
| 33 | 35 |
assert_equal ["2", "Add ingredients categories", "4.34"], results |
| 34 | 36 |
end |
| test/unit/time_entry_test.rb | ||
|---|---|---|
| 168 | 168 |
:issue => issue, |
| 169 | 169 |
:project => project, |
| 170 | 170 |
:user => anon, |
| 171 |
:author => anon, |
|
| 171 | 172 |
:activity => activity) |
| 172 | 173 |
assert_equal 1, te.errors.count |
| 173 | 174 |
end |
| ... | ... | |
| 206 | 207 |
def test_create_with_required_issue_id_and_comment_should_be_validated |
| 207 | 208 |
set_language_if_valid 'en' |
| 208 | 209 |
with_settings :timelog_required_fields => ['issue_id' , 'comments'] do |
| 209 |
entry = TimeEntry.new(:project => Project.find(1), :spent_on => Date.today, :user => User.find(1), :activity => TimeEntryActivity.first, :hours => 1) |
|
| 210 |
entry = TimeEntry.new(:project => Project.find(1), :spent_on => Date.today, :author => User.find(1), :user => User.find(1), :activity => TimeEntryActivity.first, :hours => 1)
|
|
| 210 | 211 | |
| 211 | 212 |
assert !entry.save |
| 212 | 213 |
assert_equal ["Comment cannot be blank", "Issue cannot be blank"], entry.errors.full_messages.sort |
| 213 | 214 |
end |
| 214 | 215 |
end |
| 216 | ||
| 217 |
def test_create_should_validate_user_id |
|
| 218 |
entry = TimeEntry.new(:spent_on => '2010-01-01', |
|
| 219 |
:hours => 10, |
|
| 220 |
:project_id => 1, |
|
| 221 |
:user_id => 4) |
|
| 222 | ||
| 223 |
assert !entry.save |
|
| 224 |
assert_equal ["User is invalid"], entry.errors.full_messages.sort |
|
| 225 |
end |
|
| 226 | ||
| 227 |
def test_assignable_users_should_include_active_project_members_with_log_time_permission |
|
| 228 |
Role.find(2).remove_permission! :log_time |
|
| 229 |
time_entry = TimeEntry.find(1) |
|
| 230 | ||
| 231 |
assert_equal [2], time_entry.assignable_users.map(&:id) |
|
| 232 |
end |
|
| 215 | 233 |
end |