diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index b5ce6addd..d0eb0b61f 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -37,6 +37,8 @@ module QueriesHelper group = query.is_a?(IssueQuery) ? :label_relations : nil elsif %w(member_of_group assigned_to_role).include?(field) group = :field_assigned_to + elsif %(user_group user_role).include?(field) + group = :field_user elsif field_options[:type] == :date_past || field_options[:type] == :date group = :label_date elsif %w(estimated_hours spent_time).include?(field) diff --git a/app/models/time_entry_query.rb b/app/models/time_entry_query.rb index fc5f222cf..ed7cc1576 100644 --- a/app/models/time_entry_query.rb +++ b/app/models/time_entry_query.rb @@ -100,6 +100,16 @@ class TimeEntryQuery < Query "activity_id", :type => :list, :values => activities.map {|a| [a.name, (a.parent_id || a.id).to_s]} ) + add_available_filter( + "user_group", + :type => :list_optional, + :values => lambda {Group.givable.visible.pluck(:name, :id).map {|name, id| [name, id.to_s]}} + ) + add_available_filter( + "user_role", + :type => :list_optional, + :values => lambda {Role.givable.pluck(:name, :id).map {|name, id| [name, id.to_s]}} + ) add_available_filter( "project.status", :type => :list, @@ -264,6 +274,53 @@ class TimeEntryQuery < Query sql_for_field(field, operator, value, Project.table_name, "status") end + def sql_for_user_group_field(field, operator, value) + if operator == '*' # Any group + groups = Group.givable + operator = '=' + elsif operator == '!*' + groups = Group.givable + operator = '!' + else + groups = Group.where(:id => value).to_a + end + groups ||= [] + + members_of_groups = groups.inject([]) do |user_ids, group| + user_ids + group.user_ids + end.uniq.compact.sort.collect(&:to_s) + + '(' + sql_for_field('user_id', operator, members_of_groups, TimeEntry.table_name, "user_id", false) + ')' + end + + def sql_for_user_role_field(field, operator, value) + case operator + when "*", "!*" + sw = operator == "!*" ? "NOT" : "" + nl = operator == "!*" ? "#{TimeEntry.table_name}.user_id IS NULL OR" : "" + + subquery = + "SELECT 1" + + " FROM #{Member.table_name}" + + " WHERE #{TimeEntry.table_name}.project_id = #{Member.table_name}.project_id AND #{Member.table_name}.user_id = #{TimeEntry.table_name}.user_id" + "(#{nl} #{sw} EXISTS (#{subquery}))" + when "=", "!" + role_cond = + if value.any? + "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")" + else + "1=0" + end + sw = operator == "!" ? 'NOT' : '' + nl = operator == "!" ? "#{TimeEntry.table_name}.user_id IS NULL OR" : '' + subquery = + "SELECT 1" + + " FROM #{Member.table_name} inner join #{MemberRole.table_name} on members.id = member_roles.member_id" + + " WHERE #{TimeEntry.table_name}.project_id = #{Member.table_name}.project_id AND #{Member.table_name}.user_id = #{TimeEntry.table_name}.user_id AND #{role_cond}" + "(#{nl} #{sw} EXISTS (#{subquery}))" + end + end + # Accepts :from/:to params as shortcut filters def build_from_params(params, defaults={}) super diff --git a/config/locales/en.yml b/config/locales/en.yml index 1584f5ef2..d731000bf 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -377,6 +377,8 @@ en: field_parent_issue_subject: Parent task subject field_member_of_group: "Assignee's group" field_assigned_to_role: "Assignee's role" + field_user_group: "User's group" + field_user_role: "User's role" field_text: Text field field_visible: Visible field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text" diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 8a6757757..f0da46589 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -335,6 +335,8 @@ ja: field_parent_issue: 親チケット field_member_of_group: 担当者のグループ field_assigned_to_role: 担当者のロール + field_user_group: ユーザーのグループ + field_user_role: ユーザーのロール field_text: テキスト field_visible: 表示 field_warn_on_leaving_unsaved: データを保存せずにページから移動するときに警告 diff --git a/test/unit/time_entry_query_test.rb b/test/unit/time_entry_query_test.rb index 2ecca1231..ccb4a3fad 100644 --- a/test/unit/time_entry_query_test.rb +++ b/test/unit/time_entry_query_test.rb @@ -136,6 +136,69 @@ class TimeEntryQueryTest < ActiveSupport::TestCase assert !query.available_filters.has_key?('project.status') end + def test_user_group_filter_should_consider_spacified_groups_time_entries + Group.find(10).users << User.find(2) + Group.find(11).users << User.find(3) + + TimeEntry.delete_all + t1 = TimeEntry.generate!(:hours => 1.0, :user_id => 2) + t2 = TimeEntry.generate!(:hours => 2.0, :user_id => 2) + t3 = TimeEntry.generate!(:hours => 4.0, :user_id => 3) + + query = TimeEntryQuery.new(:name => '_') + result = query.base_scope.to_a + assert result.include?(t1) + assert result.include?(t2) + assert result.include?(t3) + assert_equal 7.0, query.results_scope.sum(:hours) + + query.add_filter('user_group', '=', ['10']) + result = query.base_scope.to_a + assert result.include?(t1) + assert result.include?(t2) + assert !result.include?(t3) + assert_equal 3.0, query.results_scope.sum(:hours) + + query.add_filter('user_group', '=', ['10', '11']) + result = query.base_scope.to_a + assert result.include?(t1) + assert result.include?(t2) + assert result.include?(t3) + assert_equal 7.0, query.results_scope.sum(:hours) + end + + def test_user_role_filter_should_consider_spacified_roles_time_entries + project = Project.find(1) + project.members << Member.new(:user_id => 2, :roles => [Role.find(1)]) + project.members << Member.new(:user_id => 3, :roles => [Role.find(2)]) + + TimeEntry.delete_all + t1 = TimeEntry.generate!(:project => project, :hours => 1.0, :user_id => 2) + t2 = TimeEntry.generate!(:project => project, :hours => 2.0, :user_id => 2) + t3 = TimeEntry.generate!(:project => project, :hours => 4.0, :user_id => 3) + + query = TimeEntryQuery.new(:project => project, :name => '_') + result = query.base_scope.to_a + assert result.include?(t1) + assert result.include?(t2) + assert result.include?(t3) + assert_equal 7.0, query.results_scope.sum(:hours) + + query.add_filter('user_role', '=', ['1']) + result = query.base_scope.to_a + assert result.include?(t1) + assert result.include?(t2) + assert !result.include?(t3) + assert_equal 3.0, query.results_scope.sum(:hours) + + query.add_filter('user_role', '=', ['1', '2']) + result = query.base_scope.to_a + assert result.include?(t1) + assert result.include?(t2) + assert result.include?(t3) + assert_equal 7.0, query.results_scope.sum(:hours) + end + def test_results_scope_should_be_in_the_same_order_when_paginating 4.times {TimeEntry.generate!} q = TimeEntryQuery.new