Project

General

Profile

Feature #41053 » 41053.patch

Takenori TAKAKI, 2024-08-05 07:15

View differences:

app/helpers/queries_helper.rb
37 37
        group = query.is_a?(IssueQuery) ? :label_relations : nil
38 38
      elsif %w(member_of_group assigned_to_role).include?(field)
39 39
        group = :field_assigned_to
40
      elsif %(user_group user_role).include?(field)
41
        group = :field_user
40 42
      elsif field_options[:type] == :date_past || field_options[:type] == :date
41 43
        group = :label_date
42 44
      elsif %w(estimated_hours spent_time).include?(field)
app/models/time_entry_query.rb
100 100
      "activity_id",
101 101
      :type => :list, :values => activities.map {|a| [a.name, (a.parent_id || a.id).to_s]}
102 102
    )
103
    add_available_filter(
104
      "user_group",
105
      :type => :list_optional,
106
      :values => lambda {Group.givable.visible.pluck(:name, :id).map {|name, id| [name, id.to_s]}}
107
    )
108
    add_available_filter(
109
      "user_role",
110
      :type => :list_optional,
111
      :values => lambda {Role.givable.pluck(:name, :id).map {|name, id| [name, id.to_s]}}
112
    )
103 113
    add_available_filter(
104 114
      "project.status",
105 115
      :type => :list,
......
264 274
    sql_for_field(field, operator, value, Project.table_name, "status")
265 275
  end
266 276

  
277
  def sql_for_user_group_field(field, operator, value)
278
    if operator == '*' # Any group
279
      groups = Group.givable
280
      operator = '='
281
    elsif operator == '!*'
282
      groups = Group.givable
283
      operator = '!'
284
    else
285
      groups = Group.where(:id => value).to_a
286
    end
287
    groups ||= []
288

  
289
    members_of_groups = groups.inject([]) do |user_ids, group|
290
      user_ids + group.user_ids
291
    end.uniq.compact.sort.collect(&:to_s)
292

  
293
    '(' + sql_for_field('user_id', operator, members_of_groups, TimeEntry.table_name, "user_id", false) + ')'
294
  end
295

  
296
  def sql_for_user_role_field(field, operator, value)
297
    case operator
298
    when "*", "!*"
299
      sw = operator == "!*" ? "NOT" : ""
300
      nl = operator == "!*" ? "#{TimeEntry.table_name}.user_id IS NULL OR" : ""
301

  
302
      subquery =
303
        "SELECT 1" +
304
        " FROM #{Member.table_name}" +
305
        " WHERE #{TimeEntry.table_name}.project_id = #{Member.table_name}.project_id AND #{Member.table_name}.user_id = #{TimeEntry.table_name}.user_id"
306
      "(#{nl} #{sw} EXISTS (#{subquery}))"
307
    when "=", "!"
308
      role_cond =
309
        if value.any?
310
          "#{MemberRole.table_name}.role_id IN (" + value.collect{|val| "'#{self.class.connection.quote_string(val)}'"}.join(",") + ")"
311
        else
312
          "1=0"
313
        end
314
      sw = operator == "!" ? 'NOT' : ''
315
      nl = operator == "!" ? "#{TimeEntry.table_name}.user_id IS NULL OR" : ''
316
      subquery =
317
        "SELECT 1" +
318
        " FROM #{Member.table_name} inner join #{MemberRole.table_name} on members.id = member_roles.member_id" +
319
        " 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}"
320
      "(#{nl} #{sw} EXISTS (#{subquery}))"
321
    end
322
  end
323

  
267 324
  # Accepts :from/:to params as shortcut filters
268 325
  def build_from_params(params, defaults={})
269 326
    super
config/locales/en.yml
377 377
  field_parent_issue_subject: Parent task subject
378 378
  field_member_of_group: "Assignee's group"
379 379
  field_assigned_to_role: "Assignee's role"
380
  field_user_group: "User's group"
381
  field_user_role: "User's role"
380 382
  field_text: Text field
381 383
  field_visible: Visible
382 384
  field_warn_on_leaving_unsaved: "Warn me when leaving a page with unsaved text"
config/locales/ja.yml
335 335
  field_parent_issue: 親チケット
336 336
  field_member_of_group: 担当者のグループ
337 337
  field_assigned_to_role: 担当者のロール
338
  field_user_group: ユーザーのグループ
339
  field_user_role: ユーザーのロール
338 340
  field_text: テキスト
339 341
  field_visible: 表示
340 342
  field_warn_on_leaving_unsaved: データを保存せずにページから移動するときに警告
test/unit/time_entry_query_test.rb
136 136
    assert !query.available_filters.has_key?('project.status')
137 137
  end
138 138

  
139
  def test_user_group_filter_should_consider_spacified_groups_time_entries
140
    Group.find(10).users << User.find(2)
141
    Group.find(11).users << User.find(3)
142

  
143
    TimeEntry.delete_all
144
    t1 = TimeEntry.generate!(:hours => 1.0, :user_id => 2)
145
    t2 = TimeEntry.generate!(:hours => 2.0, :user_id => 2)
146
    t3 = TimeEntry.generate!(:hours => 4.0, :user_id => 3)
147

  
148
    query = TimeEntryQuery.new(:name => '_')
149
    result = query.base_scope.to_a
150
    assert result.include?(t1)
151
    assert result.include?(t2)
152
    assert result.include?(t3)
153
    assert_equal 7.0, query.results_scope.sum(:hours)
154

  
155
    query.add_filter('user_group', '=', ['10'])
156
    result = query.base_scope.to_a
157
    assert result.include?(t1)
158
    assert result.include?(t2)
159
    assert !result.include?(t3)
160
    assert_equal 3.0, query.results_scope.sum(:hours)
161

  
162
    query.add_filter('user_group', '=', ['10', '11'])
163
    result = query.base_scope.to_a
164
    assert result.include?(t1)
165
    assert result.include?(t2)
166
    assert result.include?(t3)
167
    assert_equal 7.0, query.results_scope.sum(:hours)
168
  end
169

  
170
  def test_user_role_filter_should_consider_spacified_roles_time_entries
171
    project = Project.find(1)
172
    project.members << Member.new(:user_id => 2, :roles => [Role.find(1)])
173
    project.members << Member.new(:user_id => 3, :roles => [Role.find(2)])
174

  
175
    TimeEntry.delete_all
176
    t1 = TimeEntry.generate!(:project => project, :hours => 1.0, :user_id => 2)
177
    t2 = TimeEntry.generate!(:project => project, :hours => 2.0, :user_id => 2)
178
    t3 = TimeEntry.generate!(:project => project, :hours => 4.0, :user_id => 3)
179

  
180
    query = TimeEntryQuery.new(:project => project, :name => '_')
181
    result = query.base_scope.to_a
182
    assert result.include?(t1)
183
    assert result.include?(t2)
184
    assert result.include?(t3)
185
    assert_equal 7.0, query.results_scope.sum(:hours)
186

  
187
    query.add_filter('user_role', '=', ['1'])
188
    result = query.base_scope.to_a
189
    assert result.include?(t1)
190
    assert result.include?(t2)
191
    assert !result.include?(t3)
192
    assert_equal 3.0, query.results_scope.sum(:hours)
193

  
194
    query.add_filter('user_role', '=', ['1', '2'])
195
    result = query.base_scope.to_a
196
    assert result.include?(t1)
197
    assert result.include?(t2)
198
    assert result.include?(t3)
199
    assert_equal 7.0, query.results_scope.sum(:hours)
200
  end
201

  
139 202
  def test_results_scope_should_be_in_the_same_order_when_paginating
140 203
    4.times {TimeEntry.generate!}
141 204
    q = TimeEntryQuery.new
(2-2/3)