Project

General

Profile

Feature #29286 » 0001-Option-to-set-default-time-entry-activity-per-role-r21737.patch

Mizuki ISHIKAWA, 2022-07-22 03:46

View differences:

app/helpers/timelog_helper.rb
48 48
    principals_options_for_select(collection, time_entry.user_id.to_s)
49 49
  end
50 50

  
51
  def default_activity(time_entry)
52
    if @project
53
      time_entry.activity_id
54
    else
55
      TimeEntryActivity.default_activity_id(User.current, time_entry.project)
56
    end
57
  end
58

  
51 59
  def select_hours(data, criteria, value)
52 60
    if value.to_s.empty?
53 61
      data.select {|row| row[criteria].blank?}
app/models/role.rb
60 60
    where("#{compare} builtin = 0")
61 61
  end)
62 62

  
63
  belongs_to :default_time_entry_activity, :class_name => "TimeEntryActivity"
64

  
63 65
  before_destroy :check_deletable
64 66
  has_many :workflow_rules, :dependent => :delete_all
65 67
  has_and_belongs_to_many :custom_fields, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "role_id"
......
104 106
    'managed_role_ids',
105 107
    'permissions',
106 108
    'permissions_all_trackers',
107
    'permissions_tracker_ids'
109
    'permissions_tracker_ids',
110
    'default_time_entry_activity_id'
108 111
  )
109 112

  
110 113
  # Copies attributes from another role, arg can be an id or a Role
app/models/time_entry.rb
107 107
  def initialize(attributes=nil, *args)
108 108
    super
109 109
    if new_record? && self.activity.nil?
110
      if default_activity = TimeEntryActivity.default(self.project)
111
        self.activity_id = default_activity.id
112
      elsif (activities = TimeEntryActivity.available_activities(self.project)) && activities.count == 1
113
        self.activity_id = activities.first.id
114
      end
110
      self.activity_id = TimeEntryActivity.default_activity_id(User.current, self.project)
115 111
      self.hours = nil if hours == 0
116 112
    end
117 113
  end
app/models/time_entry_activity.rb
32 32
    project.activities.detect { |activity| activity.parent_id == default_activity.id }
33 33
  end
34 34

  
35
  # Returns the available activities for the time entry
35 36
  def self.available_activities(project=nil)
36 37
    if project.nil?
37 38
      TimeEntryActivity.shared.active
......
55 56
  def transfer_relations(to)
56 57
    objects.update_all(:activity_id => to.id)
57 58
  end
59

  
60
  def self.default_activity_id(user=nil, project=nil)
61
    default_activities = []
62
    default_activity = nil
63
    available_activities = self.available_activities(project)
64

  
65
    if project && user
66
      user_membership = user.membership(project)
67
      if user_membership
68
        default_activities = user_membership.roles.where.not(:default_time_entry_activity_id => nil).sort.pluck(:default_time_entry_activity_id)
69
      end
70

  
71
      project_default_activity = self.default(project)
72
      if project_default_activity
73
        default_activities << project_default_activity.id unless default_activities.include?(project_default_activity.id)
74
      end
75
    end
76

  
77
    global_activity = self.default
78
    if global_activity
79
      default_activities << global_activity.id unless default_activities.include?(global_activity.id)
80
    end
81

  
82
    if available_activities.count == 1
83
      default_activities << available_activities.first.id unless default_activities.include?(available_activities.first.id)
84
    end
85

  
86
    default_activities.each do |id|
87
      default_activity = available_activities.detect{ |a| a.id == id || a.parent_id == id }
88
      break unless default_activity.nil?
89
    end
90

  
91
    default_activity.id unless default_activity.nil?
92
  end
58 93
end
app/views/roles/_form.html.erb
39 39
    </p>
40 40
  <% end %>
41 41

  
42
  <% unless @role.anonymous? %>
43
    <p><%= f.select :default_time_entry_activity_id, options_from_collection_for_select(TimeEntryActivity.active.shared, :id, :name, @role.default_time_entry_activity_id), :include_blank => l(:label_none) %></p>
44
  <% end %>
45

  
42 46
  <% if @role.new_record? && @roles.any? %>
43 47
    <p><label for="copy_workflow_from"><%= l(:label_copy_workflow_from) %></label>
44 48
    <%= select_tag(:copy_workflow_from, content_tag("option") + options_from_collection_for_select(@roles, :id, :name, params[:copy_workflow_from] || @copy_from.try(:id))) %></p>
app/views/timelog/new.js.erb
1
$('#time_entry_activity_id').html('<%= escape_javascript options_for_select(activity_collection_for_select_options(@time_entry), @time_entry.activity_id) %>');
1
$('#time_entry_activity_id').html('<%= escape_javascript options_for_select(activity_collection_for_select_options(@time_entry), default_activity(@time_entry)) %>');
2 2
$('#time_entry_issue').html('<%= escape_javascript link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %>');
db/migrate/20180806215628_add_default_time_entry_activity_to_roles.rb
1
class AddDefaultTimeEntryActivityToRoles < ActiveRecord::Migration[5.2]
2
  def change
3
    add_column :roles, :default_time_entry_activity_id, :int
4
  end
5
end
test/functional/timelog_controller_test.rb
89 89
    assert_response 403
90 90
  end
91 91

  
92
  def test_new_should_select_default_activity
92
  def test_new_should_select_default_role_activity
93
    developer = Role.find(2)
94
    developer.default_time_entry_activity_id = 9
95
    developer.save!
96

  
97
    @request.session[:user_id] = 3
98
    get :new, :params => {:project_id => 1}
99
    assert_response :success
100
    assert_select 'select[name=?]', 'time_entry[activity_id]' do
101
      assert_select 'option[selected=selected]', :text => 'Design'
102
    end
103
  end
104

  
105
  def test_new_should_select_default_global_activity_for_user_roles_without_default_activities
93 106
    @request.session[:user_id] = 3
94 107
    get :new, :params => {:project_id => 1}
95 108
    assert_response :success
test/unit/time_entry_activity_test.rb
231 231
    assert_not_equal TimeEntryActivity.default(project).id, 10
232 232
    assert_equal TimeEntryActivity.default(project).id, project_specific_default_activity.id
233 233
  end
234

  
235
  def test_default_activity_id_without_user_and_project_should_return_global_default_activity
236
    assert_equal 10, TimeEntryActivity.default_activity_id
237
  end
238

  
239
  def test_default_activity_id_with_user_and_project_should_return_role_default_activity
240
    # set a default activity for Manager role
241
    manager = Role.find(1)
242
    manager.default_time_entry_activity_id = 9
243
    manager.save
244

  
245
    assert_equal 9, TimeEntryActivity.default_activity_id(User.find(2), Project.find(1))
246
  end
247

  
248
  def test_default_activity_id_with_user_and_project_should_consider_role_position
249
    project = Project.find(1)
250
    user = User.find(2)
251

  
252
    # set a default activity for Manager role
253
    manager = Role.find(1)
254
    manager.default_time_entry_activity_id = 9
255
    manager.save!
256

  
257
    # set a default activity for Developer role
258
    # and set the role position first
259
    developer = Role.find(2)
260
    developer.default_time_entry_activity_id = 11
261
    developer.position = 1
262
    developer.save!
263

  
264
    member = Member.find_or_initialize_by(:project_id => project.id, :user_id => user.id)
265
    member.role_ids = [1, 2]
266
    member.save!
267

  
268
    assert_equal 11, TimeEntryActivity.default_activity_id(user, project)
269
  end
270

  
271
  def test_default_activity_id_should_include_only_available_activities
272
    # set a default activity for Manager role
273
    manager = Role.find(1)
274
    manager.default_time_entry_activity_id = 9
275
    manager.save!
276

  
277
    project = Project.find(1)
278

  
279
    # disable role default activity
280
    disable_activity = TimeEntryActivity.new({:name => "QA", :project => project, :parent => TimeEntryActivity.find(9), :active => false})
281
    disable_activity.save!
282

  
283
    assert_equal 10, TimeEntryActivity.default_activity_id(User.find(2), project)
284
  end
285

  
286
  def test_default_activity_id_should_selected_from_highest_priority_of_multiple_default_activity_candidates
287
    project = Project.find(1)
288

  
289
    manager = Role.find(1)
290
    manager.default_time_entry_activity_id = 9
291
    manager.save
292

  
293
    # Returns the role_default_activity with the highest priority
294
    assert_equal 9, TimeEntryActivity.default_activity_id(User.find(2), project)
295

  
296
    # Returns the child activity of role_default_activity if there is an activity that has the id of role_default_activity as parent_id
297
    design_project_activity = TimeEntryActivity.create!(name: 'Design', parent_id: 9, project_id: project.id, is_default: false)
298
    development_project_activity = TimeEntryActivity.create!(name: 'Development', parent_id: 10, project_id: project.id, is_default: true)
299
    qa_project_activity = TimeEntryActivity.create!(name: 'QA', parent_id: 11, project_id: project.id, is_default: false)
300
    assert_equal design_project_activity.id, TimeEntryActivity.default_activity_id(User.find(2), project)
301

  
302
    # Returns default project activity if role_default_activity is not present
303
    manager.default_time_entry_activity_id = nil
304
    manager.save
305
    assert_equal development_project_activity.id, TimeEntryActivity.default_activity_id(User.find(2), project)
306

  
307
    # Returns global default activity if role_default_activity and project activities are not present
308
    [design_project_activity, development_project_activity, qa_project_activity].each {|activity| activity.destroy}
309
    TimeEntryActivity.find(11).update(is_default: true)
310
    assert_equal 11, TimeEntryActivity.default_activity_id(User.find(2), project)
311

  
312
    # If there is only one activity available, it returns that activity.
313
    [TimeEntryActivity.find(10), TimeEntryActivity.find(11)].each {|a| a.update(active: false)}
314
    assert_equal 9, TimeEntryActivity.default_activity_id(User.find(2), project)
315
  end
234 316
end
(3-3/3)