Project

General

Profile

Feature #37623 » 37623-v2.patch

Go MAEDA, 2023-04-08 09:14

View differences:

app/helpers/queries_helper.rb
26 26
    ungrouped = []
27 27
    grouped = {label_string: [], label_date: [], label_time_tracking: [], label_attachment: []}
28 28
    query.available_filters.map do |field, field_options|
29
      if field_options[:type] == :relation
29
      if field =~ /^(.+)\./
30
        # association filters
31
        group = "field_#{$1}".to_sym
32
      elsif field_options[:type] == :relation
30 33
        group = :label_relations
31 34
      elsif field_options[:type] == :tree
32 35
        group = query.is_a?(IssueQuery) ? :label_relations : nil
33 36
      elsif /^cf_\d+\./.match?(field)
34 37
        group = (field_options[:through] || field_options[:field]).try(:name)
35
      elsif field =~ /^(.+)\./
36
        # association filters
37
        group = "field_#{$1}".to_sym
38 38
      elsif %w(member_of_group assigned_to_role).include?(field)
39 39
        group = :field_assigned_to
40 40
      elsif field_options[:type] == :date_past || field_options[:type] == :date
......
256 256
        link_to value, issue_path(item)
257 257
      when :subject
258 258
        link_to value, issue_path(item)
259
      when :parent
259
      when :parent, :'issue.parent'
260 260
        value ? (value.visible? ? link_to_issue(value, :subject => false) : "##{value.id}") : ''
261 261
      when :description
262 262
        item.description? ? content_tag('div', textilizable(item, :description), :class => "wiki") : ''
app/models/time_entry_query.rb
31 31
    QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true),
32 32
    QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id", :groupable => true),
33 33
    QueryAssociationColumn.new(:issue, :tracker, :caption => :field_tracker, :sortable => "#{Tracker.table_name}.position"),
34
    QueryAssociationColumn.new(:issue, :parent, :caption => :field_parent_issue, :sortable => ["#{Issue.table_name}.root_id", "#{Issue.table_name}.lft ASC"], :default_order => 'desc'),
34 35
    QueryAssociationColumn.new(:issue, :status, :caption => :field_status, :sortable => "#{IssueStatus.table_name}.position"),
35 36
    QueryAssociationColumn.new(:issue, :category, :caption => :field_category, :sortable => "#{IssueCategory.table_name}.name"),
36 37
    QueryAssociationColumn.new(:issue, :fixed_version, :caption => :field_fixed_version, :sortable => Version.fields_for_order_statement),
......
61 62
      :type => :list,
62 63
      :name => l("label_attribute_of_issue", :name => l(:field_tracker)),
63 64
      :values => lambda {trackers.map {|t| [t.name, t.id.to_s]}})
65
    add_available_filter(
66
      "issue.parent_id",
67
      :type => :tree,
68
      :name => l("label_attribute_of_issue", :name => l(:field_parent_issue))) 
64 69
    add_available_filter(
65 70
      "issue.status_id",
66 71
      :type => :list,
67 72
      :name => l("label_attribute_of_issue", :name => l(:field_status)),
68 73
      :values => lambda {issue_statuses_values})
74
    add_available_filter(
75
      "issue.subject",
76
      :type => :text,
77
      :name => l("label_attribute_of_issue", :name => l(:field_subject))
78
    )
69 79
    add_available_filter(
70 80
      "issue.fixed_version_id",
71 81
      :type => :list,
......
200 210
    end
201 211
  end
202 212

  
213
  def sql_for_issue_parent_id_field(field, operator, value)
214
    case operator
215
    when "="
216
      # accepts a comma separated list of ids
217
      parent_ids = value.first.to_s.scan(/\d+/).map(&:to_i).uniq
218
      issue_ids = Issue.where(:parent_id => parent_ids).pluck(:id)
219
      if issue_ids.present?
220
        "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})"
221
      else
222
        "1=0"
223
      end
224
    when "~"
225
      root_id, lft, rgt = Issue.where(:id => value.first.to_i).pick(:root_id, :lft, :rgt)
226
      issue_ids = Issue.where("#{Issue.table_name}.root_id = ? AND #{Issue.table_name}.lft > ? AND #{Issue.table_name}.rgt < ?", root_id, lft, rgt).pluck(:id) if root_id && lft && rgt
227
      if issue_ids.present?
228
        "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})"
229
      else
230
        "1=0"
231
      end
232
    else
233
      sql_for_field("parent_id", operator, value, Issue.table_name, "parent_id")
234
    end
235
  end
236

  
203 237
  def sql_for_activity_id_field(field, operator, value)
204 238
    ids = value.map(&:to_i).join(',')
205 239
    table_name = Enumeration.table_name
......
222 256
    sql_for_field("category_id", operator, value, Issue.table_name, "category_id")
223 257
  end
224 258

  
259
  def sql_for_issue_subject_field(field, operator, value)
260
    sql_for_field("subject", operator, value, Issue.table_name, "subject")
261
  end
262

  
225 263
  def sql_for_project_status_field(field, operator, value, options={})
226 264
    sql_for_field(field, operator, value, Project.table_name, "status")
227 265
  end
test/functional/timelog_controller_test.rb
1374 1374
    assert_select 'td.issue-category', :text => 'Printing'
1375 1375
  end
1376 1376

  
1377
  def test_index_with_issue_parent_filter
1378
    issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
1379
    entry1 = TimeEntry.generate!(issue: issue1, hours: 2.5)
1380
    issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5)
1381
    entry2 = TimeEntry.generate!(issue: issue2, hours: 5.0)
1382

  
1383
    get :index, params: {
1384
      project_id: 'ecookbook',
1385
      f: ['issue.parent_id'],
1386
      op: {'issue.parent_id' => '='},
1387
      v: {'issue.parent_id' => ['2,5']}
1388
    }
1389
    assert_response :success
1390
    assert_equal [entry1.id, entry2.id].sort, css_select('input[name="ids[]"]').map {|e| e.attr(:value).to_i}.sort
1391
  end
1392

  
1393
  def test_index_with_issue_parent_column
1394
    issue = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
1395
    entry = TimeEntry.generate!(issue: issue, hours: 2.5)
1396

  
1397
    get :index, params: {
1398
      project_id: 'ecookbook',
1399
      c: %w(project spent_on issue comments hours issue.parent)
1400
    }
1401

  
1402
    assert_response :success
1403
    assert_select 'td.issue-parent', text: "#{issue.parent.tracker} ##{issue.parent.id}"
1404
  end
1405

  
1406
  def test_index_with_issue_parent_sort
1407
    issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
1408
    entry1 = TimeEntry.generate!(issue: issue1, hours: 2.5)
1409
    issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5)
1410
    entry2 = TimeEntry.generate!(issue: issue2, hours: 5.0)
1411

  
1412
    get :index, :params => {
1413
      :c => ["hours", 'issue.parent'],
1414
      :sort => 'issue.parent'
1415
    }
1416
    assert_response :success
1417

  
1418
    # Make sure that values are properly sorted
1419
    values = css_select("td.issue-parent").map(&:text).reject(&:blank?)
1420
    assert_equal ["#{issue1.parent.tracker} ##{issue1.parent.id}", "#{issue2.parent.tracker} ##{issue2.parent.id}"].sort, values.sort
1421
  end
1422

  
1377 1423
  def test_index_with_issue_fixed_version_column
1378 1424
    issue = Issue.find(1)
1379 1425
    issue.fixed_version = Version.find(3)
test/unit/query_test.rb
504 504
    find_issues_with_query(query)
505 505
  end
506 506

  
507
  def test_time_entry_operator_is_on_issue_parent_id_should_accept_comma_separated_values
508
    issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
509
    entry1 = TimeEntry.generate!(issue: issue1)
510
    issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: 5)
511
    entry2 = TimeEntry.generate!(issue: issue2)
512

  
513
    query = TimeEntryQuery.new(:name => '_')
514
    query.add_filter("issue.parent_id", '=', ['2,5'])
515
    entries = TimeEntry.where(query.statement).to_a
516
    assert_equal 2, entries.size
517
    assert_equal [entry1.id, entry2.id].sort, entries.map(&:id).sort
518
  end
519

  
520
  def test_time_entry_contains_operator_is_on_issue_parent_id
521
    issue1 = Issue.generate!(project_id: 'ecookbook', parent_id: 2)
522
    entry1 = TimeEntry.generate!(issue: issue1)
523
    issue2 = Issue.generate!(project_id: 'ecookbook', parent_id: issue1.id)
524
    entry2 = TimeEntry.generate!(issue: issue2)
525

  
526
    query = TimeEntryQuery.new(:name => '_')
527
    query.add_filter("issue.parent_id", '~', ['2'])
528
    entries = TimeEntry.where(query.statement).to_a
529
    assert_equal 2, entries.size
530
    assert_equal [entry1.id, entry2.id].sort, entries.map(&:id).sort
531
  end
532

  
507 533
  def test_date_filter_should_not_accept_non_date_values
508 534
    query = IssueQuery.new(:name => '_')
509 535
    query.add_filter('created_on', '=', ['a'])
(5-5/5)