Chris@1464: # Redmine - project management software Chris@1494: # Copyright (C) 2006-2014 Jean-Philippe Lang Chris@1464: # Chris@1464: # This program is free software; you can redistribute it and/or Chris@1464: # modify it under the terms of the GNU General Public License Chris@1464: # as published by the Free Software Foundation; either version 2 Chris@1464: # of the License, or (at your option) any later version. Chris@1464: # Chris@1464: # This program is distributed in the hope that it will be useful, Chris@1464: # but WITHOUT ANY WARRANTY; without even the implied warranty of Chris@1464: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Chris@1464: # GNU General Public License for more details. Chris@1464: # Chris@1464: # You should have received a copy of the GNU General Public License Chris@1464: # along with this program; if not, write to the Free Software Chris@1464: # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Chris@1464: Chris@1464: class TimeEntryQuery < Query Chris@1464: Chris@1464: self.queried_class = TimeEntry Chris@1464: Chris@1464: self.available_columns = [ Chris@1464: QueryColumn.new(:project, :sortable => "#{Project.table_name}.name", :groupable => true), Chris@1464: QueryColumn.new(:spent_on, :sortable => ["#{TimeEntry.table_name}.spent_on", "#{TimeEntry.table_name}.created_on"], :default_order => 'desc', :groupable => true), Chris@1464: QueryColumn.new(:user, :sortable => lambda {User.fields_for_order_statement}, :groupable => true), Chris@1464: QueryColumn.new(:activity, :sortable => "#{TimeEntryActivity.table_name}.position", :groupable => true), Chris@1464: QueryColumn.new(:issue, :sortable => "#{Issue.table_name}.id"), Chris@1464: QueryColumn.new(:comments), Chris@1464: QueryColumn.new(:hours, :sortable => "#{TimeEntry.table_name}.hours"), Chris@1464: ] Chris@1464: Chris@1464: def initialize(attributes=nil, *args) Chris@1464: super attributes Chris@1464: self.filters ||= {} Chris@1464: add_filter('spent_on', '*') unless filters.present? Chris@1464: end Chris@1464: Chris@1464: def initialize_available_filters Chris@1464: add_available_filter "spent_on", :type => :date_past Chris@1464: Chris@1464: principals = [] Chris@1464: if project Chris@1464: principals += project.principals.sort Chris@1464: unless project.leaf? Chris@1464: subprojects = project.descendants.visible.all Chris@1464: if subprojects.any? Chris@1464: add_available_filter "subproject_id", Chris@1464: :type => :list_subprojects, Chris@1464: :values => subprojects.collect{|s| [s.name, s.id.to_s] } Chris@1464: principals += Principal.member_of(subprojects) Chris@1464: end Chris@1464: end Chris@1464: else Chris@1464: if all_projects.any? Chris@1464: # members of visible projects Chris@1464: principals += Principal.member_of(all_projects) Chris@1464: # project filter Chris@1464: project_values = [] Chris@1464: if User.current.logged? && User.current.memberships.any? Chris@1464: project_values << ["<< #{l(:label_my_projects).downcase} >>", "mine"] Chris@1464: end Chris@1464: project_values += all_projects_values Chris@1464: add_available_filter("project_id", Chris@1464: :type => :list, :values => project_values Chris@1464: ) unless project_values.empty? Chris@1464: end Chris@1464: end Chris@1464: principals.uniq! Chris@1464: principals.sort! Chris@1464: users = principals.select {|p| p.is_a?(User)} Chris@1464: Chris@1464: users_values = [] Chris@1464: users_values << ["<< #{l(:label_me)} >>", "me"] if User.current.logged? Chris@1464: users_values += users.collect{|s| [s.name, s.id.to_s] } Chris@1464: add_available_filter("user_id", Chris@1464: :type => :list_optional, :values => users_values Chris@1464: ) unless users_values.empty? Chris@1464: Chris@1464: activities = (project ? project.activities : TimeEntryActivity.shared.active) Chris@1464: add_available_filter("activity_id", Chris@1464: :type => :list, :values => activities.map {|a| [a.name, a.id.to_s]} Chris@1464: ) unless activities.empty? Chris@1464: Chris@1464: add_available_filter "comments", :type => :text Chris@1464: add_available_filter "hours", :type => :float Chris@1464: Chris@1464: add_custom_fields_filters(TimeEntryCustomField) Chris@1464: add_associations_custom_fields_filters :project, :issue, :user Chris@1464: end Chris@1464: Chris@1464: def available_columns Chris@1464: return @available_columns if @available_columns Chris@1464: @available_columns = self.class.available_columns.dup Chris@1464: @available_columns += TimeEntryCustomField.visible.all.map {|cf| QueryCustomFieldColumn.new(cf) } Chris@1464: @available_columns += IssueCustomField.visible.all.map {|cf| QueryAssociationCustomFieldColumn.new(:issue, cf) } Chris@1464: @available_columns Chris@1464: end Chris@1464: Chris@1464: def default_columns_names Chris@1464: @default_columns_names ||= [:project, :spent_on, :user, :activity, :issue, :comments, :hours] Chris@1464: end Chris@1464: Chris@1464: def results_scope(options={}) Chris@1464: order_option = [group_by_sort_order, options[:order]].flatten.reject(&:blank?) Chris@1464: Chris@1464: TimeEntry.visible. Chris@1464: where(statement). Chris@1464: order(order_option). Chris@1464: joins(joins_for_order_statement(order_option.join(','))). Chris@1464: includes(:activity) Chris@1464: end Chris@1464: Chris@1464: def sql_for_activity_id_field(field, operator, value) Chris@1464: condition_on_id = sql_for_field(field, operator, value, Enumeration.table_name, 'id') Chris@1464: condition_on_parent_id = sql_for_field(field, operator, value, Enumeration.table_name, 'parent_id') Chris@1464: ids = value.map(&:to_i).join(',') Chris@1464: table_name = Enumeration.table_name Chris@1464: if operator == '=' Chris@1464: "(#{table_name}.id IN (#{ids}) OR #{table_name}.parent_id IN (#{ids}))" Chris@1464: else Chris@1464: "(#{table_name}.id NOT IN (#{ids}) AND (#{table_name}.parent_id IS NULL OR #{table_name}.parent_id NOT IN (#{ids})))" Chris@1464: end Chris@1464: end Chris@1464: Chris@1464: # Accepts :from/:to params as shortcut filters Chris@1464: def build_from_params(params) Chris@1464: super Chris@1464: if params[:from].present? && params[:to].present? Chris@1464: add_filter('spent_on', '><', [params[:from], params[:to]]) Chris@1464: elsif params[:from].present? Chris@1464: add_filter('spent_on', '>=', [params[:from]]) Chris@1464: elsif params[:to].present? Chris@1464: add_filter('spent_on', '<=', [params[:to]]) Chris@1464: end Chris@1464: self Chris@1464: end Chris@1464: end