diff options
-rw-r--r-- | app/helpers/queries_helper.rb | 2 | ||||
-rw-r--r-- | app/models/issue.rb | 19 | ||||
-rw-r--r-- | app/models/query.rb | 23 | ||||
-rw-r--r-- | app/views/issues/show.html.erb | 2 | ||||
-rw-r--r-- | test/functional/issues_controller_test.rb | 23 | ||||
-rw-r--r-- | test/unit/query_test.rb | 7 |
6 files changed, 70 insertions, 6 deletions
diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index 862bc71b6..7c43be12c 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -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 diff --git a/app/models/issue.rb b/app/models/issue.rb index cbe3dd659..9eb567c9e 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -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]) diff --git a/app/models/query.rb b/app/models/query.rb index f3b83d0f4..f41c1cdaa 100644 --- a/app/models/query.rb +++ b/app/models/query.rb @@ -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 diff --git a/app/views/issues/show.html.erb b/app/views/issues/show.html.erb index 491966b22..3302bbb7a 100644 --- a/app/views/issues/show.html.erb +++ b/app/views/issues/show.html.erb @@ -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> diff --git a/test/functional/issues_controller_test.rb b/test/functional/issues_controller_test.rb index f6821284d..24e16e788 100644 --- a/test/functional/issues_controller_test.rb +++ b/test/functional/issues_controller_test.rb @@ -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/}, diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index e3f3c9249..f298dd59f 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -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} |