##// END OF EJS Templates
Consider custom fields that correspond to the project only (#24014)....
Jean-Philippe Lang -
r15508:960b32238830
parent child
Show More
@@ -1,236 +1,246
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 class TimeEntryQuery < Query
18 class TimeEntryQuery < Query
19
19
20 self.queried_class = TimeEntry
20 self.queried_class = TimeEntry
21 self.view_permission = :view_time_entries
21 self.view_permission = :view_time_entries
22
22
23 self.available_columns = [
23 self.available_columns = [
24 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
24 QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true),
25 QueryColumn.new(:spent_on, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :default_order => 'desc', :groupable => true),
25 QueryColumn.new(:spent_on, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :default_order => 'desc', :groupable => true),
26 QueryColumn.new(:tweek, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :caption => l(:label_week)),
26 QueryColumn.new(:tweek, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :caption => l(:label_week)),
27 QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
27 QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
28 QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true),
28 QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true),
29 QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"),
29 QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"),
30 QueryAssociationColumn.new(:issue, :tracker, :caption => :field_tracker, :sortable => "#{Tracker.table_name}.position"),
30 QueryAssociationColumn.new(:issue, :tracker, :caption => :field_tracker, :sortable => "#{Tracker.table_name}.position"),
31 QueryAssociationColumn.new(:issue, :status, :caption => :field_status, :sortable => "#{IssueStatus.table_name}.position"),
31 QueryAssociationColumn.new(:issue, :status, :caption => :field_status, :sortable => "#{IssueStatus.table_name}.position"),
32 QueryColumn.new(:comments),
32 QueryColumn.new(:comments),
33 QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours", :totalable => true),
33 QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours", :totalable => true),
34 ]
34 ]
35
35
36 def initialize(attributes=nil, *args)
36 def initialize(attributes=nil, *args)
37 super attributes
37 super attributes
38 self.filters ||= {}
38 self.filters ||= {}
39 add_filter('spent_on', '*') unless filters.present?
39 add_filter('spent_on', '*') unless filters.present?
40 end
40 end
41
41
42 def initialize_available_filters
42 def initialize_available_filters
43 add_available_filter "spent_on", :type => :date_past
43 add_available_filter "spent_on", :type => :date_past
44
44
45 principals = []
45 principals = []
46 versions = []
46 versions = []
47 if project
47 if project
48 principals += project.principals.visible.sort
48 principals += project.principals.visible.sort
49 unless project.leaf?
49 unless project.leaf?
50 subprojects = project.descendants.visible.to_a
50 subprojects = project.descendants.visible.to_a
51 if subprojects.any?
51 if subprojects.any?
52 add_available_filter "subproject_id",
52 add_available_filter "subproject_id",
53 :type => :list_subprojects,
53 :type => :list_subprojects,
54 :values => subprojects.collect{|s| [s.name, s.id.to_s] }
54 :values => subprojects.collect{|s| [s.name, s.id.to_s] }
55 principals += Principal.member_of(subprojects).visible
55 principals += Principal.member_of(subprojects).visible
56 end
56 end
57 end
57 end
58 versions = project.shared_versions.to_a
58 versions = project.shared_versions.to_a
59 else
59 else
60 if all_projects.any?
60 if all_projects.any?
61 # members of visible projects
61 # members of visible projects
62 principals += Principal.member_of(all_projects).visible
62 principals += Principal.member_of(all_projects).visible
63 # project filter
63 # project filter
64 project_values = []
64 project_values = []
65 if User.current.logged? && User.current.memberships.any?
65 if User.current.logged? && User.current.memberships.any?
66 project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
66 project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"]
67 end
67 end
68 project_values += all_projects_values
68 project_values += all_projects_values
69 add_available_filter("project_id",
69 add_available_filter("project_id",
70 :type => :list, :values => project_values
70 :type => :list, :values => project_values
71 ) unless project_values.empty?
71 ) unless project_values.empty?
72 end
72 end
73 end
73 end
74
74
75 add_available_filter("issue_id", :type => :tree, :label => :label_issue)
75 add_available_filter("issue_id", :type => :tree, :label => :label_issue)
76 add_available_filter("issue.tracker_id",
76 add_available_filter("issue.tracker_id",
77 :type => :list,
77 :type => :list,
78 :name => l("label_attribute_of_issue", :name => l(:field_tracker)),
78 :name => l("label_attribute_of_issue", :name => l(:field_tracker)),
79 :values => Tracker.sorted.map {|t| [t.name, t.id.to_s]})
79 :values => Tracker.sorted.map {|t| [t.name, t.id.to_s]})
80 add_available_filter("issue.status_id",
80 add_available_filter("issue.status_id",
81 :type => :list,
81 :type => :list,
82 :name => l("label_attribute_of_issue", :name => l(:field_status)),
82 :name => l("label_attribute_of_issue", :name => l(:field_status)),
83 :values => IssueStatus.sorted.map {|s| [s.name, s.id.to_s]})
83 :values => IssueStatus.sorted.map {|s| [s.name, s.id.to_s]})
84 add_available_filter("issue.fixed_version_id",
84 add_available_filter("issue.fixed_version_id",
85 :type => :list,
85 :type => :list,
86 :name => l("label_attribute_of_issue", :name => l(:field_fixed_version)),
86 :name => l("label_attribute_of_issue", :name => l(:field_fixed_version)),
87 :values => Version.sort_by_status(versions).collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")] })
87 :values => Version.sort_by_status(versions).collect{|s| ["#{s.project.name} - #{s.name}", s.id.to_s, l("version_status_#{s.status}")] })
88
88
89 principals.uniq!
89 principals.uniq!
90 principals.sort!
90 principals.sort!
91 users = principals.select {|p| p.is_a?(User)}
91 users = principals.select {|p| p.is_a?(User)}
92
92
93 users_values = []
93 users_values = []
94 users_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
94 users_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged?
95 users_values += users.collect{|s| [s.name, s.id.to_s] }
95 users_values += users.collect{|s| [s.name, s.id.to_s] }
96 add_available_filter("user_id",
96 add_available_filter("user_id",
97 :type => :list_optional, :values => users_values
97 :type => :list_optional, :values => users_values
98 ) unless users_values.empty?
98 ) unless users_values.empty?
99
99
100 activities = (project ? project.activities : TimeEntryActivity.shared)
100 activities = (project ? project.activities : TimeEntryActivity.shared)
101 add_available_filter("activity_id",
101 add_available_filter("activity_id",
102 :type => :list, :values => activities.map {|a| [a.name, a.id.to_s]}
102 :type => :list, :values => activities.map {|a| [a.name, a.id.to_s]}
103 ) unless activities.empty?
103 ) unless activities.empty?
104
104
105 add_available_filter "comments", :type => :text
105 add_available_filter "comments", :type => :text
106 add_available_filter "hours", :type => :float
106 add_available_filter "hours", :type => :float
107
107
108 add_custom_fields_filters(TimeEntryCustomField)
108 add_custom_fields_filters(TimeEntryCustomField)
109 add_associations_custom_fields_filters :project, :issue, :user
109 add_associations_custom_fields_filters :project
110 add_custom_fields_filters(issue_custom_fields, :issue)
111 add_associations_custom_fields_filters :user
110 end
112 end
111
113
112 def available_columns
114 def available_columns
113 return @available_columns if @available_columns
115 return @available_columns if @available_columns
114 @available_columns = self.class.available_columns.dup
116 @available_columns = self.class.available_columns.dup
115 @available_columns += TimeEntryCustomField.visible.
117 @available_columns += TimeEntryCustomField.visible.
116 map {|cf| QueryCustomFieldColumn.new(cf) }
118 map {|cf| QueryCustomFieldColumn.new(cf) }
117 @available_columns += IssueCustomField.visible.
119 @available_columns += issue_custom_fields.visible.
118 map {|cf| QueryAssociationCustomFieldColumn.new(:issue, cf, :totalable => false) }
120 map {|cf| QueryAssociationCustomFieldColumn.new(:issue, cf, :totalable => false) }
119 @available_columns
121 @available_columns
120 end
122 end
121
123
122 def default_columns_names
124 def default_columns_names
123 @default_columns_names ||= [:project, :spent_on, :user, :activity, :issue, :comments, :hours]
125 @default_columns_names ||= [:project, :spent_on, :user, :activity, :issue, :comments, :hours]
124 end
126 end
125
127
126 def default_totalable_names
128 def default_totalable_names
127 [:hours]
129 [:hours]
128 end
130 end
129
131
130 def base_scope
132 def base_scope
131 TimeEntry.visible.
133 TimeEntry.visible.
132 joins(:project, :user).
134 joins(:project, :user).
133 joins("LEFT OUTER JOIN issues ON issues.id = time_entries.issue_id").
135 joins("LEFT OUTER JOIN issues ON issues.id = time_entries.issue_id").
134 where(statement)
136 where(statement)
135 end
137 end
136
138
137 def results_scope(options={})
139 def results_scope(options={})
138 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
140 order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?)
139
141
140 base_scope.
142 base_scope.
141 order(order_option).
143 order(order_option).
142 joins(joins_for_order_statement(order_option.join(','))).
144 joins(joins_for_order_statement(order_option.join(','))).
143 includes(:activity).
145 includes(:activity).
144 references(:activity)
146 references(:activity)
145 end
147 end
146
148
147 # Returns sum of all the spent hours
149 # Returns sum of all the spent hours
148 def total_for_hours(scope)
150 def total_for_hours(scope)
149 map_total(scope.sum(:hours)) {|t| t.to_f.round(2)}
151 map_total(scope.sum(:hours)) {|t| t.to_f.round(2)}
150 end
152 end
151
153
152 def sql_for_issue_id_field(field, operator, value)
154 def sql_for_issue_id_field(field, operator, value)
153 case operator
155 case operator
154 when "="
156 when "="
155 "#{TimeEntry.table_name}.issue_id = #{value.first.to_i}"
157 "#{TimeEntry.table_name}.issue_id = #{value.first.to_i}"
156 when "~"
158 when "~"
157 issue = Issue.where(:id => value.first.to_i).first
159 issue = Issue.where(:id => value.first.to_i).first
158 if issue && (issue_ids = issue.self_and_descendants.pluck(:id)).any?
160 if issue && (issue_ids = issue.self_and_descendants.pluck(:id)).any?
159 "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})"
161 "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})"
160 else
162 else
161 "1=0"
163 "1=0"
162 end
164 end
163 when "!*"
165 when "!*"
164 "#{TimeEntry.table_name}.issue_id IS NULL"
166 "#{TimeEntry.table_name}.issue_id IS NULL"
165 when "*"
167 when "*"
166 "#{TimeEntry.table_name}.issue_id IS NOT NULL"
168 "#{TimeEntry.table_name}.issue_id IS NOT NULL"
167 end
169 end
168 end
170 end
169
171
170 def sql_for_issue_fixed_version_id_field(field, operator, value)
172 def sql_for_issue_fixed_version_id_field(field, operator, value)
171 issue_ids = Issue.where(:fixed_version_id => value.first.to_i).pluck(:id)
173 issue_ids = Issue.where(:fixed_version_id => value.first.to_i).pluck(:id)
172 case operator
174 case operator
173 when "="
175 when "="
174 if issue_ids.any?
176 if issue_ids.any?
175 "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})"
177 "#{TimeEntry.table_name}.issue_id IN (#{issue_ids.join(',')})"
176 else
178 else
177 "1=0"
179 "1=0"
178 end
180 end
179 when "!"
181 when "!"
180 if issue_ids.any?
182 if issue_ids.any?
181 "#{TimeEntry.table_name}.issue_id NOT IN (#{issue_ids.join(',')})"
183 "#{TimeEntry.table_name}.issue_id NOT IN (#{issue_ids.join(',')})"
182 else
184 else
183 "1=1"
185 "1=1"
184 end
186 end
185 end
187 end
186 end
188 end
187
189
188 def sql_for_activity_id_field(field, operator, value)
190 def sql_for_activity_id_field(field, operator, value)
189 condition_on_id = sql_for_field(field, operator, value, Enumeration.table_name, 'id')
191 condition_on_id = sql_for_field(field, operator, value, Enumeration.table_name, 'id')
190 condition_on_parent_id = sql_for_field(field, operator, value, Enumeration.table_name, 'parent_id')
192 condition_on_parent_id = sql_for_field(field, operator, value, Enumeration.table_name, 'parent_id')
191 ids = value.map(&:to_i).join(',')
193 ids = value.map(&:to_i).join(',')
192 table_name = Enumeration.table_name
194 table_name = Enumeration.table_name
193 if operator == '='
195 if operator == '='
194 "(#{table_name}.id IN (#{ids}) OR #{table_name}.parent_id IN (#{ids}))"
196 "(#{table_name}.id IN (#{ids}) OR #{table_name}.parent_id IN (#{ids}))"
195 else
197 else
196 "(#{table_name}.id NOT IN (#{ids}) AND (#{table_name}.parent_id IS NULL OR #{table_name}.parent_id NOT IN (#{ids})))"
198 "(#{table_name}.id NOT IN (#{ids}) AND (#{table_name}.parent_id IS NULL OR #{table_name}.parent_id NOT IN (#{ids})))"
197 end
199 end
198 end
200 end
199
201
200 def sql_for_issue_tracker_id_field(field, operator, value)
202 def sql_for_issue_tracker_id_field(field, operator, value)
201 sql_for_field("tracker_id", operator, value, Issue.table_name, "tracker_id")
203 sql_for_field("tracker_id", operator, value, Issue.table_name, "tracker_id")
202 end
204 end
203
205
204 def sql_for_issue_status_id_field(field, operator, value)
206 def sql_for_issue_status_id_field(field, operator, value)
205 sql_for_field("status_id", operator, value, Issue.table_name, "status_id")
207 sql_for_field("status_id", operator, value, Issue.table_name, "status_id")
206 end
208 end
207
209
208 # Accepts :from/:to params as shortcut filters
210 # Accepts :from/:to params as shortcut filters
209 def build_from_params(params)
211 def build_from_params(params)
210 super
212 super
211 if params[:from].present? && params[:to].present?
213 if params[:from].present? && params[:to].present?
212 add_filter('spent_on', '><', [params[:from], params[:to]])
214 add_filter('spent_on', '><', [params[:from], params[:to]])
213 elsif params[:from].present?
215 elsif params[:from].present?
214 add_filter('spent_on', '>=', [params[:from]])
216 add_filter('spent_on', '>=', [params[:from]])
215 elsif params[:to].present?
217 elsif params[:to].present?
216 add_filter('spent_on', '<=', [params[:to]])
218 add_filter('spent_on', '<=', [params[:to]])
217 end
219 end
218 self
220 self
219 end
221 end
220
222
221 def joins_for_order_statement(order_options)
223 def joins_for_order_statement(order_options)
222 joins = [super]
224 joins = [super]
223
225
224 if order_options
226 if order_options
225 if order_options.include?('issue_statuses')
227 if order_options.include?('issue_statuses')
226 joins << "LEFT OUTER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id"
228 joins << "LEFT OUTER JOIN #{IssueStatus.table_name} ON #{IssueStatus.table_name}.id = #{Issue.table_name}.status_id"
227 end
229 end
228 if order_options.include?('trackers')
230 if order_options.include?('trackers')
229 joins << "LEFT OUTER JOIN #{Tracker.table_name} ON #{Tracker.table_name}.id = #{Issue.table_name}.tracker_id"
231 joins << "LEFT OUTER JOIN #{Tracker.table_name} ON #{Tracker.table_name}.id = #{Issue.table_name}.tracker_id"
230 end
232 end
231 end
233 end
232
234
233 joins.compact!
235 joins.compact!
234 joins.any? ? joins.join(' ') : nil
236 joins.any? ? joins.join(' ') : nil
235 end
237 end
238
239 def issue_custom_fields
240 if project
241 project.all_issue_custom_fields
242 else
243 IssueCustomField.where(:is_for_all => true)
244 end
245 end
236 end
246 end
@@ -1,57 +1,81
1 # Redmine - project management software
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
3 #
4 # This program is free software; you can redistribute it and/or
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
7 # of the License, or (at your option) any later version.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU General Public License
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
17
18 require File.expand_path('../../test_helper', __FILE__)
18 require File.expand_path('../../test_helper', __FILE__)
19
19
20 class TimeEntryQueryTest < ActiveSupport::TestCase
20 class TimeEntryQueryTest < ActiveSupport::TestCase
21 fixtures :issues, :projects, :users,
21 fixtures :issues, :projects, :users,
22 :members, :roles, :member_roles,
22 :members, :roles, :member_roles,
23 :trackers, :issue_statuses,
23 :trackers, :issue_statuses,
24 :projects_trackers,
24 :projects_trackers,
25 :journals, :journal_details,
25 :journals, :journal_details,
26 :issue_categories, :enumerations,
26 :issue_categories, :enumerations,
27 :groups_users,
27 :groups_users,
28 :enabled_modules
28 :enabled_modules
29
29
30 def test_cross_project_activity_filter_should_propose_non_active_activities
30 def test_cross_project_activity_filter_should_propose_non_active_activities
31 activity = TimeEntryActivity.create!(:name => 'Disabled', :active => false)
31 activity = TimeEntryActivity.create!(:name => 'Disabled', :active => false)
32 assert !activity.active?
32 assert !activity.active?
33
33
34 query = TimeEntryQuery.new(:name => '_')
34 query = TimeEntryQuery.new(:name => '_')
35 assert options = query.available_filters['activity_id']
35 assert options = query.available_filters['activity_id']
36 assert values = options[:values]
36 assert values = options[:values]
37 assert_include ["Disabled", activity.id.to_s], values
37 assert_include ["Disabled", activity.id.to_s], values
38 end
38 end
39
39
40 def test_activity_filter_should_consider_system_and_project_activities
40 def test_activity_filter_should_consider_system_and_project_activities
41 TimeEntry.delete_all
41 TimeEntry.delete_all
42 system = TimeEntryActivity.create!(:name => 'Foo')
42 system = TimeEntryActivity.create!(:name => 'Foo')
43 TimeEntry.generate!(:activity => system, :hours => 1.0)
43 TimeEntry.generate!(:activity => system, :hours => 1.0)
44 override = TimeEntryActivity.create!(:name => 'Foo', :parent_id => system.id, :project_id => 1)
44 override = TimeEntryActivity.create!(:name => 'Foo', :parent_id => system.id, :project_id => 1)
45 other = TimeEntryActivity.create!(:name => 'Bar')
45 other = TimeEntryActivity.create!(:name => 'Bar')
46 TimeEntry.generate!(:activity => override, :hours => 2.0)
46 TimeEntry.generate!(:activity => override, :hours => 2.0)
47 TimeEntry.generate!(:activity => other, :hours => 4.0)
47 TimeEntry.generate!(:activity => other, :hours => 4.0)
48
48
49 query = TimeEntryQuery.new(:name => '_')
49 query = TimeEntryQuery.new(:name => '_')
50 query.add_filter('activity_id', '=', [system.id.to_s])
50 query.add_filter('activity_id', '=', [system.id.to_s])
51 assert_equal 3.0, query.results_scope.sum(:hours)
51 assert_equal 3.0, query.results_scope.sum(:hours)
52
52
53 query = TimeEntryQuery.new(:name => '_')
53 query = TimeEntryQuery.new(:name => '_')
54 query.add_filter('activity_id', '!', [system.id.to_s])
54 query.add_filter('activity_id', '!', [system.id.to_s])
55 assert_equal 4.0, query.results_scope.sum(:hours)
55 assert_equal 4.0, query.results_scope.sum(:hours)
56 end
56 end
57
58 def test_project_query_should_include_project_issue_custom_fields_only_as_filters
59 global = IssueCustomField.generate!(:is_for_all => true, :is_filter => true)
60 field_on_project = IssueCustomField.generate!(:is_for_all => false, :project_ids => [3], :is_filter => true)
61 field_not_on_project = IssueCustomField.generate!(:is_for_all => false, :project_ids => [1,2], :is_filter => true)
62
63 query = TimeEntryQuery.new(:project => Project.find(3))
64
65 assert_include "issue.cf_#{global.id}", query.available_filters.keys
66 assert_include "issue.cf_#{field_on_project.id}", query.available_filters.keys
67 assert_not_include "issue.cf_#{field_not_on_project.id}", query.available_filters.keys
68 end
69
70 def test_project_query_should_include_project_issue_custom_fields_only_as_columns
71 global = IssueCustomField.generate!(:is_for_all => true, :is_filter => true)
72 field_on_project = IssueCustomField.generate!(:is_for_all => false, :project_ids => [3], :is_filter => true)
73 field_not_on_project = IssueCustomField.generate!(:is_for_all => false, :project_ids => [1,2], :is_filter => true)
74
75 query = TimeEntryQuery.new(:project => Project.find(3))
76
77 assert_include "issue.cf_#{global.id}", query.available_columns.map(&:name).map(&:to_s)
78 assert_include "issue.cf_#{field_on_project.id}", query.available_columns.map(&:name).map(&:to_s)
79 assert_not_include "issue.cf_#{field_not_on_project.id}", query.available_columns.map(&:name).map(&:to_s)
80 end
57 end
81 end
General Comments 0
You need to be logged in to leave comments. Login now