From 6fa12a95abaff2e5422588a69b8abf4f2ba28d74 Mon Sep 17 00:00:00 2001 From: Marius Balteanu Date: Wed, 1 May 2024 07:49:29 +0000 Subject: [PATCH] Adds estimated remaining hours issue query column calculated based on estimated time and done ratio (#37862). MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Patch by Jens Krämer (@jkraemer). git-svn-id: https://svn.redmine.org/redmine/trunk@22800 e93f8b46-1217-0410-a6f0-8f06a7374b81 --- app/helpers/queries_helper.rb | 4 ++-- app/models/issue_query.rb | 25 ++++++++++++++++++++++++- config/locales/de.yml | 1 + config/locales/en.yml | 1 + test/unit/query_test.rb | 28 ++++++++++++++++++++++++++++ 5 files changed, 56 insertions(+), 3 deletions(-) diff --git a/app/helpers/queries_helper.rb b/app/helpers/queries_helper.rb index b9234bd1f..160473ae8 100644 --- a/app/helpers/queries_helper.rb +++ b/app/helpers/queries_helper.rb @@ -190,7 +190,7 @@ module QueriesHelper def total_tag(column, value) label = content_tag('span', "#{column.caption}:") value = - if [:hours, :spent_hours, :total_spent_hours, :estimated_hours, :total_estimated_hours].include? column.name + if [:hours, :spent_hours, :total_spent_hours, :estimated_hours, :total_estimated_hours, :estimated_remaining_hours].include? column.name format_hours(value) else format_object(value) @@ -269,7 +269,7 @@ module QueriesHelper 'span', value.to_s(item) {|other| link_to_issue(other, :subject => false, :tracker => false)}.html_safe, :class => value.css_classes_for(item)) - when :hours, :estimated_hours, :total_estimated_hours + when :hours, :estimated_hours, :total_estimated_hours, :estimated_remaining_hours format_hours(value) when :spent_hours link_to_if(value > 0, format_hours(value), project_time_entries_path(item.project, :issue_id => "#{item.id}")) diff --git a/app/models/issue_query.rb b/app/models/issue_query.rb index 91678874e..6a2771343 100644 --- a/app/models/issue_query.rb +++ b/app/models/issue_query.rb @@ -18,6 +18,22 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class IssueQuery < Query + class EstimatedRemainingHoursColumn < QueryColumn + COLUMN_SQL = Arel.sql("COALESCE(#{Issue.table_name}.estimated_hours, 0) * (100 - COALESCE(#{Issue.table_name}.done_ratio, 0)) / 100") + + def initialize + super :estimated_remaining_hours, totalable: true, sortable: COLUMN_SQL + end + + def value(object) + (object.estimated_hours || 0) * (100 - (object.done_ratio || 0)) / 100 + end + + def value_object(object) + value(object) + end + end + self.queried_class = Issue self.view_permission = :view_issues @@ -50,6 +66,7 @@ class IssueQuery < Query QueryColumn.new(:due_date, :sortable => "#{Issue.table_name}.due_date", :groupable => true), QueryColumn.new(:estimated_hours, :sortable => "#{Issue.table_name}.estimated_hours", :totalable => true), + EstimatedRemainingHoursColumn.new, QueryColumn.new( :total_estimated_hours, :sortable => @@ -330,7 +347,9 @@ class IssueQuery < Query end disabled_fields = Tracker.disabled_core_fields(trackers).map {|field| field.delete_suffix('_id')} - disabled_fields << "total_estimated_hours" if disabled_fields.include?("estimated_hours") + if disabled_fields.include?("estimated_hours") + disabled_fields += %w[total_estimated_hours estimated_remaining_hours] + end @available_columns.reject! do |column| disabled_fields.include?(column.name.to_s) end @@ -370,6 +389,10 @@ class IssueQuery < Query map_total(scope.sum(:estimated_hours)) {|t| t.to_f.round(2)} end + def total_for_estimated_remaining_hours(scope) + map_total(scope.sum(EstimatedRemainingHoursColumn::COLUMN_SQL)) {|t| t.to_f.round(2)} + end + # Returns sum of all the issue's time entries hours def total_for_spent_hours(scope) total = scope.joins(:time_entries). diff --git a/config/locales/de.yml b/config/locales/de.yml index 086c2979c..f85345122 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -399,6 +399,7 @@ de: field_watcher: Beobachter field_default_assigned_to: Standardbearbeiter field_unique_id: Eindeutige ID + field_estimated_remaining_hours: Geschätzter verbleibender Aufwand general_csv_decimal_separator: ',' general_csv_encoding: ISO-8859-1 diff --git a/config/locales/en.yml b/config/locales/en.yml index ecda509ec..3ae06f745 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -418,6 +418,7 @@ en: field_default_project_query: Default project query field_default_time_entry_activity: Default spent time activity field_any_searchable: Any searchable text + field_estimated_remaining_hours: Estimated remaining time setting_app_title: Application title setting_welcome_text: Welcome text diff --git a/test/unit/query_test.rb b/test/unit/query_test.rb index 78205013a..8ea079764 100644 --- a/test/unit/query_test.rb +++ b/test/unit/query_test.rb @@ -2388,6 +2388,11 @@ class QueryTest < ActiveSupport::TestCase assert_include :estimated_hours, q.available_totalable_columns.map(&:name) end + def test_available_totalable_columns_should_include_estimated_remaining_hours + q = IssueQuery.new + assert_include :estimated_remaining_hours, q.available_totalable_columns.map(&:name) + end + def test_available_totalable_columns_should_include_spent_hours User.current = User.find(1) @@ -2461,6 +2466,29 @@ class QueryTest < ActiveSupport::TestCase ) end + def test_total_for_estimated_remaining_hours + Issue.delete_all + Issue.generate!(:estimated_hours => 5.5, :done_ratio => 50) + Issue.generate!(:estimated_hours => 1.1, :done_ratio => 100) + Issue.generate! + + q = IssueQuery.new + assert_equal 2.75, q.total_for(:estimated_remaining_hours) + end + + def test_total_by_group_for_estimated_remaining_hours + Issue.delete_all + Issue.generate!(:estimated_hours => 5.5, :assigned_to_id => 2, :done_ratio => 50) + Issue.generate!(:estimated_hours => 1.1, :assigned_to_id => 3, :done_ratio => 100) + Issue.generate!(:estimated_hours => 3.5, :done_ratio => 0) + + q = IssueQuery.new(:group_by => 'assigned_to') + assert_equal( + {nil => 3.5, User.find(2) => 2.75, User.find(3) => 0}, + q.total_by_group_for(:estimated_remaining_hours) + ) + end + def test_total_for_spent_hours TimeEntry.delete_all TimeEntry.generate!(:hours => 5.5) -- 2.39.5