git-svn-id: svn+ssh://rubyforge.org/var/svn/redmine/trunk@8073 e93f8b46-1217-0410-a6f0-8f06a7374b81tags/1.4.0
@@ -44,6 +44,8 @@ module QueriesHelper | |||
when 'Fixnum', 'Float' | |||
if column.name == :done_ratio | |||
progress_bar(value, :width => '80px') | |||
elsif column.name == :spent_hours | |||
sprintf "%.2f", value | |||
else | |||
h(value.to_s) | |||
end |
@@ -499,13 +499,18 @@ class Issue < ActiveRecord::Base | |||
notified.collect(&:mail) | |||
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 | |||
# | |||
# Example: | |||
# spent_hours => 0.0 | |||
# spent_hours => 50.2 | |||
def spent_hours | |||
@spent_hours ||= self_and_descendants.sum("#{TimeEntry.table_name}.hours", :include => :time_entries).to_f || 0.0 | |||
def total_spent_hours | |||
@total_spent_hours ||= self_and_descendants.sum("#{TimeEntry.table_name}.hours", :include => :time_entries).to_f || 0.0 | |||
end | |||
def relations | |||
@@ -522,6 +527,16 @@ class Issue < ActiveRecord::Base | |||
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. | |||
def find_relation(relation_id) | |||
IssueRelation.find(relation_id, :conditions => ["issue_to_id = ? OR issue_from_id = ?", id, id]) |
@@ -356,11 +356,23 @@ class Query < ActiveRecord::Base | |||
def available_columns | |||
return @available_columns if @available_columns | |||
@available_columns = ::Query.available_columns | |||
@available_columns = ::Query.available_columns.dup | |||
@available_columns += (project ? | |||
project.all_issue_custom_fields : | |||
IssueCustomField.find(:all) | |||
).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 | |||
def self.available_columns=(v) | |||
@@ -412,7 +424,7 @@ class Query < ActiveRecord::Base | |||
end | |||
def has_column?(column) | |||
column_names && column_names.include?(column.name) | |||
column_names && column_names.include?(column.is_a?(QueryColumn) ? column.name : column) | |||
end | |||
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 | |||
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, | |||
:order => order_option, | |||
:joins => joins, | |||
:limit => options[:limit], | |||
:offset => options[:offset] | |||
if has_column?(:spent_hours) | |||
Issue.load_visible_spent_hours(issues) | |||
end | |||
issues | |||
rescue ::ActiveRecord::StatementInvalid => e | |||
raise StatementInvalid.new(e.message) | |||
end |
@@ -32,7 +32,7 @@ | |||
<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) %> | |||
<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 %> | |||
</tr> | |||
<tr> |
@@ -537,6 +537,13 @@ class IssuesControllerTest < ActionController::TestCase | |||
get :index, :group_by => 'author', :sort => 'priority' | |||
assert_response :success | |||
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 | |||
columns = ['tracker', 'subject', 'assigned_to'] | |||
@@ -615,6 +622,22 @@ class IssuesControllerTest < ActionController::TestCase | |||
} | |||
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 | |||
get :index, :set_filter => 1, :c => %w(fixed_version) | |||
assert_tag 'td', :attributes => {:class => /fixed_version/}, |
@@ -423,6 +423,13 @@ class QueryTest < ActiveSupport::TestCase | |||
assert q.has_column?(c) | |||
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 | |||
q = Query.new | |||
assert q.groupable_columns.detect {|c| c.is_a? QueryCustomFieldColumn} |