From 329b1ac6e0573abd72def933c6a6d0f2f57a39c6 Mon Sep 17 00:00:00 2001 From: Aaron Contreras Date: Tue, 10 Sep 2024 09:39:14 -0500 Subject: [PATCH] {WIP} add simple average mode --- .../work_packages/update_ancestors/loader.rb | 1 + .../work_packages/update_ancestors_service.rb | 19 ++-- .../update_ancestors_service_spec.rb | 90 +++++++++++++++++++ 3 files changed, 105 insertions(+), 5 deletions(-) diff --git a/app/services/work_packages/update_ancestors/loader.rb b/app/services/work_packages/update_ancestors/loader.rb index 966a04fd4620..0852fe7279da 100644 --- a/app/services/work_packages/update_ancestors/loader.rb +++ b/app/services/work_packages/update_ancestors/loader.rb @@ -30,6 +30,7 @@ class WorkPackages::UpdateAncestors::Loader parent_id: "parent_id", estimated_hours: "estimated_hours", remaining_hours: "remaining_hours", + done_ratio: "done_ratio", status_excluded_from_totals: "statuses.excluded_from_totals", schedule_manually: "schedule_manually", ignore_non_working_days: "ignore_non_working_days" diff --git a/app/services/work_packages/update_ancestors_service.rb b/app/services/work_packages/update_ancestors_service.rb index b6f0c3e0d78f..6a6179d46ec3 100644 --- a/app/services/work_packages/update_ancestors_service.rb +++ b/app/services/work_packages/update_ancestors_service.rb @@ -120,13 +120,22 @@ def derive_done_ratio(ancestor, loader) end def compute_derived_done_ratio(work_package, loader) - return if work_package.derived_estimated_hours.nil? || work_package.derived_remaining_hours.nil? - return if work_package.derived_estimated_hours.zero? return if no_children?(work_package, loader) - work_done = (work_package.derived_estimated_hours - work_package.derived_remaining_hours) - progress = (work_done.to_f / work_package.derived_estimated_hours) * 100 - progress.round + if Setting.total_percent_complete_mode == "simple_average" + children_done_ratios = loader.descendants_of(work_package).map(&:done_ratio) + all_done_ratios = children_done_ratios + [work_package.done_ratio] + all_done_ratios.compact! + progress = all_done_ratios.sum.to_f / all_done_ratios.count + progress.round + elsif Setting.total_percent_complete_mode == "work_weighted_average" + return if work_package.derived_estimated_hours.nil? || work_package.derived_remaining_hours.nil? + return if work_package.derived_estimated_hours.zero? + + work_done = (work_package.derived_estimated_hours - work_package.derived_remaining_hours) + progress = (work_done.to_f / work_package.derived_estimated_hours) * 100 + progress.round + end end # Sets the ignore_non_working_days to true if any descendant has its value set to true. diff --git a/spec/services/work_packages/update_ancestors_service_spec.rb b/spec/services/work_packages/update_ancestors_service_spec.rb index 98f6fb1ec3fc..be8776465c49 100644 --- a/spec/services/work_packages/update_ancestors_service_spec.rb +++ b/spec/services/work_packages/update_ancestors_service_spec.rb @@ -795,6 +795,96 @@ def call_update_ancestors_service(work_package) end end + describe "simple average mode for total % complete calculation", + with_settings: { total_percent_complete_mode: "simple_average" } do + subject(:call_result) do + described_class.new(user:, work_package: parent) + .call(%i(remaining_hours)) + end + + context "with parent and all children having work, remaining work and % complete set" do + let_work_packages(<<~TABLE) + hierarchy | work | remaining work | % complete + parent | 10h | 10h | 0% + child1 | 15h | 10h | 33% + child2 | 5h | 2.5h | 50% + child3 | 10h | 2.5h | 75% + TABLE + + it "sets the total % complete accordingly accounting for " \ + "the parent's % complete and all children" do + expect(call_result).to be_success + updated_work_packages = call_result.all_results + expect_work_packages(updated_work_packages, <<~TABLE) + subject | total % complete + parent | 40% + TABLE + end + end + + context "with parent having no values set but all children having " \ + "work, remaining work and % complete set" do + let_work_packages(<<~TABLE) + hierarchy | work | remaining work | % complete + parent | | | + child1 | 15h | 10h | 33% + child2 | 5h | 2.5h | 50% + child3 | 10h | 2.5h | 75% + TABLE + + it "sets the total % complete accordingly accounting for the children only" do + expect(call_result).to be_success + updated_work_packages = call_result.all_results + expect_work_packages(updated_work_packages, <<~TABLE) + subject | total % complete + parent | 53% + TABLE + end + end + + context "with parent having no values set but some children having all the " \ + "work, remaining work and % complete set" do + let_work_packages(<<~TABLE) + hierarchy | work | remaining work | % complete + parent | | | + child1 | 15h | 10h | 33% + child2 | | | 75% + child3 | 10h | 2.5h | 75% + TABLE + + it "sets the total % complete accordingly accounting for the children only " \ + "based on the % complete values" do + expect(call_result).to be_success + updated_work_packages = call_result.all_results + expect_work_packages(updated_work_packages, <<~TABLE) + subject | total % complete + parent | 61% + TABLE + end + end + + context "with parent having no values set and all children having " \ + "solely % complete set" do + let_work_packages(<<~TABLE) + hierarchy | work | remaining work | % complete + parent | | | + child1 | | | 25% + child2 | | | 50% + child3 | | | 75% + TABLE + + it "sets the total % complete accordingly accounting for the children only " \ + "based on the % complete values" do + expect(call_result).to be_success + updated_work_packages = call_result.all_results + expect_work_packages(updated_work_packages, <<~TABLE) + subject | total % complete + parent | 50% + TABLE + end + end + end + describe "ignore_non_working_days propagation" do shared_let(:grandgrandparent) do create(:work_package,