mirror of
https://github.com/redmine/redmine.git
synced 2025-11-09 14:56:01 +01:00
Allow issues grouping by creation, update and closing date (#13803).
Implemented for PostgreSQL only for now. git-svn-id: http://svn.redmine.org/redmine/trunk@17724 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
@@ -128,7 +128,7 @@ module QueriesHelper
|
|||||||
items.each do |item|
|
items.each do |item|
|
||||||
group_name = group_count = nil
|
group_name = group_count = nil
|
||||||
if query.grouped?
|
if query.grouped?
|
||||||
group = query.group_by_column.value(item)
|
group = query.group_by_column.group_value(item)
|
||||||
if first || group != previous_group
|
if first || group != previous_group
|
||||||
if group.blank? && group != false
|
if group.blank? && group != false
|
||||||
group_name = "(#{l(:label_blank_value)})"
|
group_name = "(#{l(:label_blank_value)})"
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ class IssueQuery < Query
|
|||||||
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
|
QueryColumn.new(:subject, :sortable => "#{Issue.table_name}.subject"),
|
||||||
QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true),
|
QueryColumn.new(:author, :sortable => lambda {User.fields_for_order_statement("authors")}, :groupable => true),
|
||||||
QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
|
QueryColumn.new(:assigned_to, :sortable => lambda {User.fields_for_order_statement}, :groupable => true),
|
||||||
QueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc'),
|
TimestampQueryColumn.new(:updated_on, :sortable => "#{Issue.table_name}.updated_on", :default_order => 'desc', :groupable => true),
|
||||||
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
|
QueryColumn.new(:category, :sortable => "#{IssueCategory.table_name}.name", :groupable => true),
|
||||||
QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
|
QueryColumn.new(:fixed_version, :sortable => lambda {Version.fields_for_order_statement}, :groupable => true),
|
||||||
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date", :groupable => true),
|
QueryColumn.new(:start_date, :sortable => "#{Issue.table_name}.start_date", :groupable => true),
|
||||||
@@ -41,8 +41,8 @@ class IssueQuery < Query
|
|||||||
" WHERE subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)",
|
" WHERE subtasks.root_id = #{Issue.table_name}.root_id AND subtasks.lft >= #{Issue.table_name}.lft AND subtasks.rgt <= #{Issue.table_name}.rgt), 0)",
|
||||||
:default_order => 'desc'),
|
:default_order => 'desc'),
|
||||||
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
|
QueryColumn.new(:done_ratio, :sortable => "#{Issue.table_name}.done_ratio", :groupable => true),
|
||||||
QueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc'),
|
TimestampQueryColumn.new(:created_on, :sortable => "#{Issue.table_name}.created_on", :default_order => 'desc', :groupable => true),
|
||||||
QueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc'),
|
TimestampQueryColumn.new(:closed_on, :sortable => "#{Issue.table_name}.closed_on", :default_order => 'desc', :groupable => true),
|
||||||
QueryColumn.new(:last_updated_by, :sortable => lambda {User.fields_for_order_statement("last_journal_user")}),
|
QueryColumn.new(:last_updated_by, :sortable => lambda {User.fields_for_order_statement("last_journal_user")}),
|
||||||
QueryColumn.new(:relations, :caption => :label_related_issues),
|
QueryColumn.new(:relations, :caption => :label_related_issues),
|
||||||
QueryColumn.new(:attachments, :caption => :label_attachment_plural),
|
QueryColumn.new(:attachments, :caption => :label_attachment_plural),
|
||||||
|
|||||||
@@ -71,11 +71,31 @@ class QueryColumn
|
|||||||
object.send name
|
object.send name
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the group that object belongs to when grouping query results
|
||||||
|
def group_value(object)
|
||||||
|
value(object)
|
||||||
|
end
|
||||||
|
|
||||||
def css_classes
|
def css_classes
|
||||||
name
|
name
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class TimestampQueryColumn < QueryColumn
|
||||||
|
|
||||||
|
def groupable
|
||||||
|
if @groupable
|
||||||
|
Redmine::Database.timestamp_to_date(sortable, User.current.time_zone)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_value(object)
|
||||||
|
if time = value(object)
|
||||||
|
User.current.time_to_date(time)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class QueryAssociationColumn < QueryColumn
|
class QueryAssociationColumn < QueryColumn
|
||||||
|
|
||||||
def initialize(association, attribute, options={})
|
def initialize(association, attribute, options={})
|
||||||
|
|||||||
@@ -64,6 +64,19 @@ module Redmine
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns a SQL statement to cast a timestamp column to a date given a time zone
|
||||||
|
# Returns nil if not implemented for the current database
|
||||||
|
def timestamp_to_date(column, time_zone)
|
||||||
|
if postgresql?
|
||||||
|
if time_zone
|
||||||
|
identifier = ActiveSupport::TimeZone.find_tzinfo(time_zone.name).identifier
|
||||||
|
"(#{column}::timestamptz AT TIME ZONE '#{identifier}')::date"
|
||||||
|
else
|
||||||
|
"#{column}::date"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Resets database information
|
# Resets database information
|
||||||
def reset
|
def reset
|
||||||
@postgresql_unaccent = nil
|
@postgresql_unaccent = nil
|
||||||
|
|||||||
@@ -303,7 +303,7 @@ module Redmine
|
|||||||
|
|
||||||
issue_list(issues) do |issue, level|
|
issue_list(issues) do |issue, level|
|
||||||
if query.grouped? &&
|
if query.grouped? &&
|
||||||
(group = query.group_by_column.value(issue)) != previous_group
|
(group = query.group_by_column.group_value(issue)) != previous_group
|
||||||
pdf.SetFontStyle('B',10)
|
pdf.SetFontStyle('B',10)
|
||||||
group_label = group.blank? ? 'None' : group.to_s.dup
|
group_label = group.blank? ? 'None' : group.to_s.dup
|
||||||
group_label << " (#{result_count_by_group[group]})"
|
group_label << " (#{result_count_by_group[group]})"
|
||||||
|
|||||||
@@ -352,6 +352,32 @@ class IssuesControllerTest < Redmine::ControllerTest
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_index_grouped_by_created_on
|
||||||
|
skip unless IssueQuery.new.groupable_columns.detect {|c| c.name == :created_on}
|
||||||
|
|
||||||
|
get :index, :params => {
|
||||||
|
:set_filter => 1,
|
||||||
|
:group_by => 'created_on'
|
||||||
|
}
|
||||||
|
assert_response :success
|
||||||
|
|
||||||
|
assert_select 'tr.group span.name', :text => '07/19/2006' do
|
||||||
|
assert_select '+ span.count', :text => '2'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_index_grouped_by_created_on_as_pdf
|
||||||
|
skip unless IssueQuery.new.groupable_columns.detect {|c| c.name == :created_on}
|
||||||
|
|
||||||
|
get :index, :params => {
|
||||||
|
:set_filter => 1,
|
||||||
|
:group_by => 'created_on',
|
||||||
|
:format => 'pdf'
|
||||||
|
}
|
||||||
|
assert_response :success
|
||||||
|
assert_equal 'application/pdf', response.content_type
|
||||||
|
end
|
||||||
|
|
||||||
def test_index_with_query_grouped_by_list_custom_field
|
def test_index_with_query_grouped_by_list_custom_field
|
||||||
get :index, :params => {
|
get :index, :params => {
|
||||||
:project_id => 1,
|
:project_id => 1,
|
||||||
|
|||||||
Reference in New Issue
Block a user