From 2c01c064ecec17043941ae02cd18b939cfda1b67 Mon Sep 17 00:00:00 2001 From: Marius BALTEANU Date: Mon, 6 Aug 2018 22:44:49 +0000 Subject: [PATCH] Option to set default time entry activity per role --- app/helpers/timelog_helper.rb | 15 ++++--- app/models/role.rb | 5 ++- app/models/time_entry.rb | 4 +- app/models/time_entry_activity.rb | 34 ++++++++++++++ app/views/roles/_form.html.erb | 4 ++ app/views/timelog/new.js.erb | 2 +- ...628_add_default_time_entry_activity_to_roles.rb | 5 +++ test/functional/timelog_controller_test.rb | 15 ++++++- test/unit/time_entry_activity_test.rb | 52 ++++++++++++++++++++++ 9 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 db/migrate/20180806215628_add_default_time_entry_activity_to_roles.rb diff --git a/app/helpers/timelog_helper.rb b/app/helpers/timelog_helper.rb index 89ed996..180574f 100644 --- a/app/helpers/timelog_helper.rb +++ b/app/helpers/timelog_helper.rb @@ -26,11 +26,8 @@ module TimelogHelper def activity_collection_for_select_options(time_entry=nil, project=nil) project ||= time_entry.try(:project) project ||= @project - if project.nil? - activities = TimeEntryActivity.shared.active - else - activities = project.activities - end + + activities = TimeEntryActivity.allowed_activities(project) collection = [] if time_entry && time_entry.activity && !time_entry.activity.active? @@ -42,6 +39,14 @@ module TimelogHelper collection end + def default_activity(time_entry) + if @project + time_entry.activity_id + else + TimeEntryActivity.default_activity_id(User.current, time_entry.project) + end + end + def select_hours(data, criteria, value) if value.to_s.empty? data.select {|row| row[criteria].blank? } diff --git a/app/models/role.rb b/app/models/role.rb index bf987e2..2e80b33 100644 --- a/app/models/role.rb +++ b/app/models/role.rb @@ -58,6 +58,8 @@ class Role < ActiveRecord::Base where("#{compare} builtin = 0") } + belongs_to :default_time_entry_activity, :class_name => "TimeEntryActivity" + before_destroy :check_deletable has_many :workflow_rules, :dependent => :delete_all has_and_belongs_to_many :custom_fields, :join_table => "#{table_name_prefix}custom_fields_roles#{table_name_suffix}", :foreign_key => "role_id" @@ -96,7 +98,8 @@ class Role < ActiveRecord::Base 'managed_role_ids', 'permissions', 'permissions_all_trackers', - 'permissions_tracker_ids' + 'permissions_tracker_ids', + 'default_time_entry_activity_id' # Copies attributes from another role, arg can be an id or a Role def copy_from(arg, options={}) diff --git a/app/models/time_entry.rb b/app/models/time_entry.rb index d64c311..642a07f 100644 --- a/app/models/time_entry.rb +++ b/app/models/time_entry.rb @@ -91,9 +91,7 @@ class TimeEntry < ActiveRecord::Base def initialize(attributes=nil, *args) super if new_record? && self.activity.nil? - if default_activity = TimeEntryActivity.default - self.activity_id = default_activity.id - end + self.activity_id = TimeEntryActivity.default_activity_id(self.user, self.project) self.hours = nil if hours == 0 end end diff --git a/app/models/time_entry_activity.rb b/app/models/time_entry_activity.rb index 62fd18b..2712c09 100644 --- a/app/models/time_entry_activity.rb +++ b/app/models/time_entry_activity.rb @@ -35,4 +35,38 @@ class TimeEntryActivity < Enumeration def transfer_relations(to) objects.update_all(:activity_id => to.id) end + + def self.default_activity_id(user=nil, project=nil) + default_activities = [] + default_activity = nil + allowed_activities = self.allowed_activities(project) + + if project && user + user_membership = user.membership(project) + if user_membership + default_activities = user_membership.roles.where.not(:default_time_entry_activity_id => nil).sort.pluck(:default_time_entry_activity_id) + end + end + + global_activity = self.default + if global_activity + default_activities << global_activity.id unless default_activities.include?(global_activity.id) + end + + default_activities.each do |id| + default_activity = allowed_activities.detect{ |a| a.id == id || a.parent_id == id } + break unless default_activity.nil? + end + + default_activity.id unless default_activity.nil? + end + + # Returns the available activities for the time entry + def self.allowed_activities(project=nil) + if project.nil? + activities = TimeEntryActivity.shared.active + else + activities = project.activities + end + end end diff --git a/app/views/roles/_form.html.erb b/app/views/roles/_form.html.erb index d87c6a1..34662e6 100644 --- a/app/views/roles/_form.html.erb +++ b/app/views/roles/_form.html.erb @@ -38,6 +38,10 @@ <%= hidden_field_tag 'role[managed_role_ids][]', '' %> <% end %> + <% unless @role.anonymous? %> +

<%= 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) %>

+ <% end %> + <% if @role.new_record? && @roles.any? %>

<%= select_tag(:copy_workflow_from, content_tag("option") + options_from_collection_for_select(@roles, :id, :name, params[:copy_workflow_from] || @copy_from.try(:id))) %>

diff --git a/app/views/timelog/new.js.erb b/app/views/timelog/new.js.erb index 4cba8cf..70b731e 100644 --- a/app/views/timelog/new.js.erb +++ b/app/views/timelog/new.js.erb @@ -1,2 +1,2 @@ -$('#time_entry_activity_id').html('<%= escape_javascript options_for_select(activity_collection_for_select_options(@time_entry), @time_entry.activity_id) %>'); +$('#time_entry_activity_id').html('<%= escape_javascript options_for_select(activity_collection_for_select_options(@time_entry), default_activity(@time_entry)) %>'); $('#time_entry_issue').html('<%= escape_javascript link_to_issue(@time_entry.issue) if @time_entry.issue.try(:visible?) %>'); diff --git a/db/migrate/20180806215628_add_default_time_entry_activity_to_roles.rb b/db/migrate/20180806215628_add_default_time_entry_activity_to_roles.rb new file mode 100644 index 0000000..1afc016 --- /dev/null +++ b/db/migrate/20180806215628_add_default_time_entry_activity_to_roles.rb @@ -0,0 +1,5 @@ +class AddDefaultTimeEntryActivityToRoles < ActiveRecord::Migration[5.2] + def change + add_column :roles, :default_time_entry_activity_id, :int + end +end diff --git a/test/functional/timelog_controller_test.rb b/test/functional/timelog_controller_test.rb index 943d69c..a1c5b83 100644 --- a/test/functional/timelog_controller_test.rb +++ b/test/functional/timelog_controller_test.rb @@ -86,7 +86,20 @@ class TimelogControllerTest < Redmine::ControllerTest assert_response 403 end - def test_new_should_select_default_activity + def test_new_should_select_default_role_activity + developer = Role.find(2) + developer.default_time_entry_activity_id = 9 + developer.save! + + @request.session[:user_id] = 3 + get :new, :params => {:project_id => 1} + assert_response :success + assert_select 'select[name=?]', 'time_entry[activity_id]' do + assert_select 'option[selected=selected]', :text => 'Design' + end + end + + def test_new_should_select_default_global_activity_for_user_roles_without_default_activities @request.session[:user_id] = 3 get :new, :params => {:project_id => 1} assert_response :success diff --git a/test/unit/time_entry_activity_test.rb b/test/unit/time_entry_activity_test.rb index c1d7613..2b8554e 100644 --- a/test/unit/time_entry_activity_test.rb +++ b/test/unit/time_entry_activity_test.rb @@ -124,4 +124,56 @@ class TimeEntryActivityTest < ActiveSupport::TestCase assert_include activity, project.activities assert_include TimeEntryActivity.find(9), project.activities end + + def test_default_activity_id_without_user_and_project_should_return_global_default_activity + assert_equal 10, TimeEntryActivity.default_activity_id + end + + def test_default_activity_id_with_user_and_project_should_return_role_default_activity + # set a default activity for Manager role + manager = Role.find(1) + manager.default_time_entry_activity_id = 9 + manager.save + + assert_equal 9, TimeEntryActivity.default_activity_id(User.find(2), Project.find(1)) + end + + def test_default_activity_id_with_user_and_project_should_consider_role_position + project = Project.find(1) + user = User.find(2) + + # set a default activity for Manager role + manager = Role.find(1) + manager.default_time_entry_activity_id = 9 + manager.save! + + # set a default activity for Developer role + # and set the role position first + developer = Role.find(2) + developer.default_time_entry_activity_id = 11 + developer.position = 1 + developer.save! + + member = Member.find_or_new(project, user) + member.role_ids = [1, 2] + member.save! + + assert_equal 11, TimeEntryActivity.default_activity_id(user, project) + end + + + def test_default_activity_id_should_include_only_allowed_activities + # set a default activity for Manager role + manager = Role.find(1) + manager.default_time_entry_activity_id = 9 + manager.save! + + project = Project.find(1) + + # disable role default activity + disable_activity = TimeEntryActivity.new({:name => "QA", :project => project, :parent => TimeEntryActivity.find(9), :active => false}) + disable_activity.save! + + assert_equal 10, TimeEntryActivity.default_activity_id(User.find(2), project) + end end -- 2.1.4