Makes spent time column available on the issue list (#971).
git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@8073 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
1 parent
bddd19c1e6
commit
6d498a3c46
6 files changed
+70
-6
No files matched your search
@@ -44,6 +44,8 @@ module QueriesHelper
|
|||||||
when 'Fixnum', 'Float'
|
when 'Fixnum', 'Float'
|
||||||
if column.name == :done_ratio
|
if column.name == :done_ratio
|
||||||
progress_bar(value, :width => '80px')
|
progress_bar(value, :width => '80px')
|
||||||
|
elsif column.name == :spent_hours
|
||||||
|
sprintf "%.2f", value
|
||||||
else
|
else
|
||||||
h(value.to_s)
|
h(value.to_s)
|
||||||
end
|
end
|
||||||
|
|||||||
+17
-2
@@ -499,13 +499,18 @@ class Issue < ActiveRecord::Base
|
|||||||
notified.collect(&:mail)
|
notified.collect(&:mail)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns the number of hours spent on this issue
|
||||||
|
def spent_hours
|
||||||
|
@spent_hours ||= time_entries.sum(:hours) || 0
|
||||||
|
end
|
||||||
|
|
||||||
# Returns the total number of hours spent on this issue and its descendants
|
# Returns the total number of hours spent on this issue and its descendants
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
# spent_hours => 0.0
|
# spent_hours => 0.0
|
||||||
# spent_hours => 50.2
|
# spent_hours => 50.2
|
||||||
def spent_hours
|
def total_spent_hours
|
||||||
@spent_hours ||= self_and_descendants.sum("#{TimeEntry.table_name}.hours", :include => :time_entries).to_f || 0.0
|
@total_spent_hours ||= self_and_descendants.sum("#{TimeEntry.table_name}.hours", :include => :time_entries).to_f || 0.0
|
||||||
end
|
end
|
||||||
|
|
||||||
def relations
|
def relations
|
||||||
@@ -522,6 +527,16 @@ class Issue < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Preloads visible spent time for a collection of issues
|
||||||
|
def self.load_visible_spent_hours(issues, user=User.current)
|
||||||
|
if issues.any?
|
||||||
|
hours_by_issue_id = TimeEntry.visible(user).sum(:hours, :group => :issue_id)
|
||||||
|
issues.each do |issue|
|
||||||
|
issue.instance_variable_set "@spent_hours", (hours_by_issue_id[issue.id] || 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Finds an issue relation given its id.
|
# Finds an issue relation given its id.
|
||||||
def find_relation(relation_id)
|
def find_relation(relation_id)
|
||||||
IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id])
|
IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id])
|
||||||
|
|||||||
+20
-3
@@ -356,11 +356,23 @@ class Query < ActiveRecord::Base
|
|||||||
|
|
||||||
def available_columns
|
def available_columns
|
||||||
return @available_columns if @available_columns
|
return @available_columns if @available_columns
|
||||||
@available_columns = ::Query.available_columns
|
@available_columns = ::Query.available_columns.dup
|
||||||
@available_columns += (project ?
|
@available_columns += (project ?
|
||||||
project.all_issue_custom_fields :
|
project.all_issue_custom_fields :
|
||||||
IssueCustomField.find(:all)
|
IssueCustomField.find(:all)
|
||||||
).collect {|cf| QueryCustomFieldColumn.new(cf) }
|
).collect {|cf| QueryCustomFieldColumn.new(cf) }
|
||||||
|
|
||||||
|
if User.current.allowed_to?(:view_time_entries, project, :global => true)
|
||||||
|
index = @available_columns.index {|column| column.name == :estimated_hours}
|
||||||
|
index = (index ? index + 1 : -1)
|
||||||
|
# insert the column after estimated_hours or at the end
|
||||||
|
@available_columns.insert index, QueryColumn.new(:spent_hours,
|
||||||
|
:sortable => "(SELECT SUM(hours) FROM #{TimeEntry.table_name} WHERE #{TimeEntry.table_name}.issue_id = #{Issue.table_name}.id)",
|
||||||
|
:default_order => 'desc',
|
||||||
|
:caption => :label_spent_time
|
||||||
|
)
|
||||||
|
end
|
||||||
|
@available_columns
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.available_columns=(v)
|
def self.available_columns=(v)
|
||||||
@@ -412,7 +424,7 @@ class Query < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
|
|
||||||
def has_column?(column)
|
def has_column?(column)
|
||||||
column_names && column_names.include?(column.name)
|
column_names && column_names.include?(column.is_a?(QueryColumn) ? column.name : column)
|
||||||
end
|
end
|
||||||
|
|
||||||
def has_default_columns?
|
def has_default_columns?
|
||||||
@@ -561,12 +573,17 @@ class Query < ActiveRecord::Base
|
|||||||
|
|
||||||
joins = (order_option && order_option.include?('authors')) ? "LEFT OUTER JOIN users authors ON authors.id = #{Issue.table_name}.author_id" : nil
|
joins = (order_option && order_option.include?('authors')) ? "LEFT OUTER JOIN users authors ON authors.id = #{Issue.table_name}.author_id" : nil
|
||||||
|
|
||||||
Issue.visible.scoped(:conditions => options[:conditions]).find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
|
issues = Issue.visible.scoped(:conditions => options[:conditions]).find :all, :include => ([:status, :project] + (options[:include] || [])).uniq,
|
||||||
:conditions => statement,
|
:conditions => statement,
|
||||||
:order => order_option,
|
:order => order_option,
|
||||||
:joins => joins,
|
:joins => joins,
|
||||||
:limit => options[:limit],
|
:limit => options[:limit],
|
||||||
:offset => options[:offset]
|
:offset => options[:offset]
|
||||||
|
|
||||||
|
if has_column?(:spent_hours)
|
||||||
|
Issue.load_visible_spent_hours(issues)
|
||||||
|
end
|
||||||
|
issues
|
||||||
rescue ::ActiveRecord::StatementInvalid => e
|
rescue ::ActiveRecord::StatementInvalid => e
|
||||||
raise StatementInvalid.new(e.message)
|
raise StatementInvalid.new(e.message)
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
<th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h(@issue.category ? @issue.category.name : "-") %></td>
|
<th class="category"><%=l(:field_category)%>:</th><td class="category"><%=h(@issue.category ? @issue.category.name : "-") %></td>
|
||||||
<% if User.current.allowed_to?(:view_time_entries, @project) %>
|
<% if User.current.allowed_to?(:view_time_entries, @project) %>
|
||||||
<th class="spent-time"><%=l(:label_spent_time)%>:</th>
|
<th class="spent-time"><%=l(:label_spent_time)%>:</th>
|
||||||
<td class="spent-time"><%= @issue.spent_hours > 0 ? (link_to l_hours(@issue.spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
|
<td class="spent-time"><%= @issue.total_spent_hours > 0 ? (link_to l_hours(@issue.total_spent_hours), {:controller => 'timelog', :action => 'index', :project_id => @project, :issue_id => @issue}) : "-" %></td>
|
||||||
<% end %>
|
<% end %>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
|||||||
@@ -538,6 +538,13 @@ class IssuesControllerTest < ActionController::TestCase
|
|||||||
assert_response :success
|
assert_response :success
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_index_group_by_spent_hours
|
||||||
|
get :index, :group_by => 'author', :sort => 'spent_hours:desc'
|
||||||
|
assert_response :success
|
||||||
|
hours = assigns(:issues).collect(&:spent_hours)
|
||||||
|
assert_equal hours.sort.reverse, hours
|
||||||
|
end
|
||||||
|
|
||||||
def test_index_with_columns
|
def test_index_with_columns
|
||||||
columns = ['tracker', 'subject', 'assigned_to']
|
columns = ['tracker', 'subject', 'assigned_to']
|
||||||
get :index, :set_filter => 1, :c => columns
|
get :index, :set_filter => 1, :c => columns
|
||||||
@@ -615,6 +622,22 @@ class IssuesControllerTest < ActionController::TestCase
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_index_with_spent_hours_column
|
||||||
|
get :index, :set_filter => 1, :c => %w(subject spent_hours)
|
||||||
|
|
||||||
|
assert_tag 'tr', :attributes => {:id => 'issue-3'},
|
||||||
|
:child => {
|
||||||
|
:tag => 'td', :attributes => {:class => /spent_hours/}, :content => '1.00'
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_index_should_not_show_spent_hours_column_without_permission
|
||||||
|
Role.anonymous.remove_permission! :view_time_entries
|
||||||
|
get :index, :set_filter => 1, :c => %w(subject spent_hours)
|
||||||
|
|
||||||
|
assert_no_tag 'td', :attributes => {:class => /spent_hours/}
|
||||||
|
end
|
||||||
|
|
||||||
def test_index_with_fixed_version
|
def test_index_with_fixed_version
|
||||||
get :index, :set_filter => 1, :c => %w(fixed_version)
|
get :index, :set_filter => 1, :c => %w(fixed_version)
|
||||||
assert_tag 'td', :attributes => {:class => /fixed_version/},
|
assert_tag 'td', :attributes => {:class => /fixed_version/},
|
||||||
|
|||||||
@@ -423,6 +423,13 @@ class QueryTest < ActiveSupport::TestCase
|
|||||||
assert q.has_column?(c)
|
assert q.has_column?(c)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_query_should_preload_spent_hours
|
||||||
|
q = Query.new(:name => '_', :column_names => [:subject, :spent_hours])
|
||||||
|
assert q.has_column?(:spent_hours)
|
||||||
|
issues = q.issues
|
||||||
|
assert_not_nil issues.first.instance_variable_get("@spent_hours")
|
||||||
|
end
|
||||||
|
|
||||||
def test_groupable_columns_should_include_custom_fields
|
def test_groupable_columns_should_include_custom_fields
|
||||||
q = Query.new
|
q = Query.new
|
||||||
assert q.groupable_columns.detect {|c| c.is_a? QueryCustomFieldColumn}
|
assert q.groupable_columns.detect {|c| c.is_a? QueryCustomFieldColumn}
|
||||||
|
|||||||
Reference in new issue
Block a user