additionals/app/models/additionals_query.rb

295 lines
10 KiB
Ruby
Raw Normal View History

# frozen_string_literal: true
2018-12-22 14:26:14 +01:00
module AdditionalsQuery
NO_RESULT_CONDITION = '1=0'
WITH_TRUE_CONDITION = '1=1'
def label_me_value
self.class.label_me_value
end
2020-08-06 08:18:30 +02:00
def column_with_prefix?(prefix)
columns.detect { |c| c.name.to_s.start_with? "#{prefix}." }.present?
2018-12-22 14:26:14 +01:00
end
def available_column_names(only_sortable: false, only_groupable: false, only_totalable: false, type: nil)
2022-01-01 18:24:42 +01:00
method_name = ['available_']
if type
method_name << type
method_name << '_'
end
method_name << 'columns'
names = send(method_name.join).dup
2020-08-06 08:18:30 +02:00
names.flatten!
names.select! { |col| col.sortable.present? } if only_sortable
names.select!(&:groupable?) if only_groupable
names.select!(&:totalable) if only_totalable
2020-08-06 08:18:30 +02:00
names.map(&:name)
end
2020-08-06 08:18:30 +02:00
def sql_for_enabled_module(table_field, module_names)
module_names = Array module_names
2019-12-20 12:58:12 +01:00
2020-08-06 08:18:30 +02:00
sql = []
module_names.each do |module_name|
sql << "EXISTS(SELECT 1 FROM #{EnabledModule.table_name} WHERE #{EnabledModule.table_name}.project_id=#{table_field}" \
" AND #{EnabledModule.table_name}.name='#{module_name}')"
end
2019-05-22 12:26:14 +02:00
sql.join ' AND '
2020-08-06 08:18:30 +02:00
end
2019-05-22 12:26:14 +02:00
def fix_sql_for_text_field(field, operator, value, table_name = nil, target_field = nil)
table_name = queried_table_name if table_name.blank?
target_field = field if target_field.blank?
sql = []
sql << "(#{sql_for_field field, operator, value, table_name, target_field})"
sql << "#{table_name}.#{target_field} != ''" if operator == '*'
sql.join ' AND '
end
def initialize_ids_filter(label: nil)
if label
add_available_filter 'ids', type: :integer, label: label
2020-08-06 08:18:30 +02:00
else
add_available_filter 'ids', type: :integer, name: '#'
2019-05-22 12:26:14 +02:00
end
2020-08-06 08:18:30 +02:00
end
2019-05-22 12:26:14 +02:00
2020-08-06 08:18:30 +02:00
def sql_for_ids_field(_field, operator, value)
if operator == '='
# accepts a comma separated list of ids
2023-01-21 10:42:53 +01:00
ids = ids_from_string value.first
if ids.any?
2020-08-08 18:44:40 +02:00
"#{queried_table_name}.id IN (#{ids.join ','})"
2019-02-09 16:39:12 +01:00
else
NO_RESULT_CONDITION
2019-02-09 16:39:12 +01:00
end
2020-08-06 08:18:30 +02:00
else
2020-08-09 12:51:01 +02:00
sql_for_field 'id', operator, value, queried_table_name, 'id'
2018-12-22 14:26:14 +01:00
end
2020-08-06 08:18:30 +02:00
end
2018-12-22 14:26:14 +01:00
2021-03-09 06:04:39 +01:00
def sql_for_project_identifier_field(field, operator, values)
value = values.first
values = value.strip_split if ['=', '!'].include?(operator) && value.include?(',')
2021-03-09 06:04:39 +01:00
sql_for_field field, operator, values, Project.table_name, 'identifier'
end
2020-08-06 08:18:30 +02:00
def sql_for_project_status_field(field, operator, value)
2020-08-09 12:51:01 +02:00
sql_for_field field, operator, value, Project.table_name, 'status'
2020-08-06 08:18:30 +02:00
end
2018-12-22 14:26:14 +01:00
2021-03-09 06:04:39 +01:00
def initialize_project_identifier_filter
return if project
add_available_filter 'project.identifier',
type: :string,
name: l(:label_attribute_of_project, name: l(:field_identifier))
end
2020-08-06 08:18:30 +02:00
def initialize_project_status_filter
return if project
2020-12-02 19:06:53 +01:00
add_available_filter 'project.status',
2020-08-06 08:18:30 +02:00
type: :list,
name: l(:label_attribute_of_project, name: l(:field_status)),
2020-12-02 19:06:53 +01:00
values: -> { project_statuses_values }
2020-08-06 08:18:30 +02:00
end
def initialize_project_filter(always: false, position: nil, without_subprojects: false)
if project.nil? || always
add_available_filter 'project_id', order: position,
2020-08-06 08:18:30 +02:00
type: :list,
2020-12-02 19:06:53 +01:00
values: -> { project_values }
end
return if without_subprojects || project.nil? || project.leaf? || subproject_values.empty?
add_available_filter 'subproject_id', order: position,
2020-08-06 08:18:30 +02:00
type: :list_subprojects,
2020-12-02 19:06:53 +01:00
values: -> { subproject_values }
2020-08-06 08:18:30 +02:00
end
2019-02-09 16:39:12 +01:00
def initialize_created_filter(position: nil, label: nil)
add_available_filter 'created_on', order: position,
2020-08-06 08:18:30 +02:00
type: :date_past,
label: label
2020-08-06 08:18:30 +02:00
end
2019-02-09 16:39:12 +01:00
def initialize_updated_filter(position: nil, label: nil)
add_available_filter 'updated_on', order: position,
2020-08-06 08:18:30 +02:00
type: :date_past,
label: label
2020-08-06 08:18:30 +02:00
end
2019-02-09 16:39:12 +01:00
2020-08-06 08:18:30 +02:00
def initialize_approved_filter
add_available_filter 'approved',
type: :list,
values: [[l(:label_hrm_approved), '1'],
[l(:label_hrm_not_approved), '0'],
[l(:label_hrm_to_approval), '2'],
[l(:label_hrm_without_approval), '3']],
label: :field_approved
end
2019-11-19 10:02:07 +01:00
def initialize_author_filter(position: nil)
add_available_filter 'author_id', order: position,
type: :author
2020-08-06 08:18:30 +02:00
end
def initialize_assignee_filter(position: nil)
add_available_filter 'assigned_to_id', order: position,
type: :assignee
2020-08-06 08:18:30 +02:00
end
2019-02-09 16:39:12 +01:00
def initialize_watcher_filter(position: nil)
return unless User.current.logged?
2019-02-09 16:39:12 +01:00
add_available_filter 'watcher_id', order: position,
type: :user_with_me
2020-08-06 08:18:30 +02:00
end
2019-11-15 18:27:16 +01:00
2022-12-07 15:25:45 +01:00
def initialize_last_notes_filter(order: nil)
options = { type: :date_past,
label: :label_last_notes }
options[:order] = order if order
add_available_filter 'last_notes', **options
end
# not required for: assigned_to_id author_id user_id watcher_id updated_by last_updated_by
# this fields are replaced by Query::statement
def values_without_me(values)
return values unless values.delete 'me'
values << if User.current.logged?
User.current.id.to_s
else
'0'
end
values
end
2022-06-13 07:42:26 +02:00
def initialize_notes_filter(position: nil)
add_available_filter 'notes', type: :text, order: position
end
def sql_for_notes_field(field, operator, value)
subquery = "SELECT 1 FROM #{Journal.table_name}" \
" WHERE #{Journal.table_name}.journalized_type='#{queried_class}'" \
" AND #{Journal.table_name}.journalized_id=#{queried_table_name}.id" \
" AND (#{sql_for_field field, operator.sub(/^!/, ''), value, Journal.table_name, 'notes'})" \
" AND (#{Journal.visible_notes_condition User.current, skip_pre_condition: true})"
"#{/^!/.match?(operator) ? 'NOT EXISTS' : 'EXISTS'} (#{subquery})"
end
2022-12-07 15:25:45 +01:00
def sql_for_last_notes_field(field, operator, value)
journalized_type = queried_class.name
journal_table = Journal.table_name
case operator
when '*', '!*'
op = operator == '*' ? 'EXISTS' : 'NOT EXISTS'
"#{op}(SELECT 1 FROM #{queried_table_name} AS ii INNER JOIN #{journal_table}" \
" ON #{journal_table}.journalized_id = ii.id AND #{journal_table}.journalized_type = '#{journalized_type}'" \
" WHERE #{queried_table_name}.id = ii.id AND #{journal_table}.notes IS NOT NULL AND #{journal_table}.notes != '')"
2022-12-07 15:25:45 +01:00
else
"#{queried_table_name}.id IN (" \
" SELECT #{journal_table}.journalized_id" \
" FROM #{journal_table}" \
" WHERE #{journal_table}.journalized_type='#{journalized_type}' AND #{journal_table}.id IN" \
2022-12-07 15:25:45 +01:00
" (SELECT MAX(#{journal_table}.id)" \
" FROM #{journal_table}" \
" WHERE #{journal_table}.journalized_type='#{journalized_type}'" \
" AND #{journal_table}.notes IS NOT NULL AND #{journal_table}.notes != ''" \
2022-12-07 15:25:45 +01:00
" GROUP BY #{journal_table}.journalized_id)" \
" AND #{sql_for_field field, operator, value, journal_table, 'created_on'})"
end
end
2020-08-06 08:18:30 +02:00
def sql_for_watcher_id_field(field, operator, value)
watchable_type = queried_class == User ? 'Principal' : queried_class.to_s
2018-12-22 14:26:14 +01:00
2020-08-06 08:18:30 +02:00
db_table = Watcher.table_name
"#{queried_table_name}.id #{operator == '=' ? 'IN' : 'NOT IN'}" \
2021-06-30 19:30:58 +02:00
" (SELECT #{db_table}.watchable_id FROM #{db_table} WHERE #{db_table}.watchable_type='#{watchable_type}' AND" \
" #{sql_for_field field, '=', value, db_table, 'user_id'})"
2020-08-06 08:18:30 +02:00
end
2018-12-22 14:26:14 +01:00
2020-08-06 08:18:30 +02:00
def sql_for_is_private_field(_field, operator, value)
if bool_operator operator, value
2020-08-06 08:18:30 +02:00
return '' if value.count > 1
2018-12-22 14:26:14 +01:00
2020-08-06 08:18:30 +02:00
"#{queried_table_name}.is_private = #{self.class.connection.quoted_true}"
else
return NO_RESULT_CONDITION if value.count > 1
2018-12-22 14:26:14 +01:00
2020-08-06 08:18:30 +02:00
"#{queried_table_name}.is_private = #{self.class.connection.quoted_false}"
2018-12-22 14:26:14 +01:00
end
2020-08-06 08:18:30 +02:00
end
2018-12-22 14:26:14 +01:00
2020-08-06 08:18:30 +02:00
# use for list fields with to values 1 (true) and 0 (false)
def bool_operator(operator, values)
operator == '=' && values.first == '1' || operator != '=' && values.first != '1'
end
2018-12-22 14:26:14 +01:00
2020-08-06 08:18:30 +02:00
# use for list
def bool_values
[[l(:general_text_yes), '1'], [l(:general_text_no), '0']]
end
2018-12-22 14:26:14 +01:00
2022-03-14 18:42:32 +01:00
# all results (without search_string limit)
2020-08-06 08:18:30 +02:00
def query_count
2022-03-14 18:42:32 +01:00
@query_count ||= search_string.present? ? objects_scope(search: search_string).count : objects_scope.count
2020-08-06 08:18:30 +02:00
rescue ::ActiveRecord::StatementInvalid => e
raise queried_class::StatementInvalid, e.message if defined? queried_class::StatementInvalid
2019-09-08 21:53:27 +02:00
2020-08-06 08:18:30 +02:00
raise ::Query::StatementInvalid, e.message
end
2018-12-22 14:26:14 +01:00
2022-03-14 18:42:32 +01:00
def entries_init_options(**options)
# set default limit to export limit, if limit is not set
options[:limit] = export_limit unless options.key? :limit
options[:search] = search_string if search_string
options
end
# query results
def entries(**_)
raise 'overwrite it'
end
def results_scope(**options)
order_option = [group_by_sort_order, (options[:order] || sort_clause)].flatten!.to_a.compact_blank
2018-12-22 14:26:14 +01:00
objects_scope(**options.except(:order, :limit, :offset))
2020-08-06 08:18:30 +02:00
.order(order_option)
.joins(joins_for_order_statement(order_option.join(',')))
.limit(options[:limit])
.offset(options[:offset])
rescue ::ActiveRecord::StatementInvalid => e
raise queried_class::StatementInvalid, e.message if defined? queried_class::StatementInvalid
2019-09-08 21:53:27 +02:00
2020-08-06 08:18:30 +02:00
raise ::Query::StatementInvalid, e.message
2018-12-22 14:26:14 +01:00
end
2020-09-25 15:16:03 +02:00
# NOTE: alias can be used, if results_scope is not usable (because it was overwritten)
alias additionals_results_scope results_scope
2020-09-25 15:16:03 +02:00
def grouped_name_for(group_name, replace_fields = {})
return unless group_name
if grouped? && group_by_column.present?
replace_fields.each do |field, new_name|
return new_name.presence || group_name if group_by_column.name == field
end
end
group_name
end
2018-12-22 14:26:14 +01:00
end