[COMMS-916] Read cost report version filter and group-by from target_versions - #24432
[COMMS-916] Read cost report version filter and group-by from target_versions#24432akabiru wants to merge 8 commits into
Conversation
Reach a work package's versions through the work_package_versions join instead of the deprecated version_id column, so a work package with several target versions is matched by the filter and listed under each when grouping. Overlapping totals from multi-value work packages are accepted for now; deduplication is separate work.
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
There was a problem hiding this comment.
Pull request overview
This PR updates the reporting cost query’s Version filter and group-by to use the work_package_versions “target” association instead of the deprecated work_packages.version_id column, allowing work packages with multiple target versions to match via any of them and to appear under each when grouped.
Changes:
- Introduces a shared SQL join fragment to reach
work_package_versions(kind:target) from reportingentries. - Switches Version group-by and filter to use that join and
work_package_versions.version_id. - Adds specs covering grouping a work package under multiple target versions and filtering via a non-primary target version.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| modules/reporting/app/models/cost_query/work_package_target_version_join.rb | Adds a shared LEFT OUTER JOIN fragment to connect entries to target version associations. |
| modules/reporting/app/models/cost_query/group_by/version_id.rb | Updates Version group-by to use the target-version join and group on work_package_versions.version_id. |
| modules/reporting/app/models/cost_query/filter/version_id.rb | Updates Version filter to use the target-version join and filter on work_package_versions.version_id. |
| modules/reporting/spec/models/cost_query/group_by_spec.rb | Adds coverage for grouping a work package under each of its target versions. |
| modules/reporting/spec/models/cost_query/filter_spec.rb | Adds coverage for matching a work package through a non-primary target version. |
entries.entity_id is polymorphic, so a Meeting time entry whose id collides with a work package's was inheriting that work package's target versions in cost report filtering and grouping. Guarding on entity_type in the ON clause keeps the group-by's LEFT semantics for non-work-package entries instead of dropping them.
The target-version join is one-to-many, so filtering or grouping by version counts a work package once per matching target version. This over-count is accepted for now; the specs assert the inflated totals and reference OPEN POINT FND-178 so the behavior is clearly tracked rather than mistaken for an untested bug.
With the multiple-versions feature on, version filtering and grouping read every target version (one-to-many, the accepted FND-178 over-count). With it off a work package is single-version, so the join is narrowed to its primary target version (target_versions.first) and the label reads 'Version' instead of 'Target versions'. Both paths read the new target_versions model rather than the deprecated WorkPackage#version_id. The join is resolved per report-build instead of via the join_table DSL so the flag takes effect without a restart.
With every target version joined, the default row-by-row negation let a work package slip through on a non-excluded row (targets [1,2] still matched 'is not 1' via the 2 row). Exclude on the work package as a whole with a NOT EXISTS subquery, guarded by entity_type. Only needed while multiple versions is on; the primary-only join is already one-to-one.
target_versions has no declared ordering, so spell out that the primary target version is the lowest version id (what target_versions.first returns), matching the join's MIN(version_id).
The multi-version NOT EXISTS override is skipped while the feature is off, so pin that the default 'is not' operator negates correctly on the primary-only join: 'is not primary' drops the work package, 'is not secondary' keeps it.
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
With multiple target versions on, the versions filter is a multi-value attribute, so render it as a chip autocompleter (like the project filter) that runs an IN check instead of the single-select-plus-toggle widget. With the flag off a work package is single-version, so keep a strictly singular select with no add-value toggle.
|
Warning Flaky specs
🤖 Ask Copilot to investigateCopy the prompt below into a new comment on this PR to delegate the investigation to GitHub Copilot. It will look into the flakiness and open a separate pull request with you as reviewer. |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 9 changed files in this pull request and generated 5 comments.
Comments suppressed due to low confidence (2)
modules/reporting/spec/models/cost_query/group_by_spec.rb:129
- This spec hard-codes the English label ("Target versions"), which is brittle across translation updates/locales. Consider asserting against the same I18n-backed source as the implementation (WorkPackage.human_attribute_name).
it "labels the group 'Target versions'" do
expect(CostQuery::GroupBy::VersionId.label).to eq("Target versions")
end
modules/reporting/spec/models/cost_query/filter_spec.rb:352
- This spec hard-codes the English label ("Target versions"), which is brittle across translation changes/locales. Consider asserting against WorkPackage.human_attribute_name(:target_versions) to match the implementation.
it "labels the filter 'Target versions'" do
expect(CostQuery::Filter::VersionId.label).to eq("Target versions")
end
| end | ||
|
|
||
| it "labels the group 'Version' while multiple versions is off" do | ||
| expect(CostQuery::GroupBy::VersionId.label).to eq("Version") |
| end | ||
|
|
||
| it "labels the filter 'Version' while multiple versions is off" do | ||
| expect(CostQuery::Filter::VersionId.label).to eq("Version") |
| def selected_version_ids | ||
| expand_comma_separated_values! | ||
|
|
||
| filter.values.map(&:to_i) |
| # The whole set of selectable versions, project-qualified so identically named | ||
| # versions in different projects stay distinguishable. The list is small, so we | ||
| # hand it to the autocompleter up front and let it filter client-side. | ||
| def available_versions | ||
| filter_class.available_values.map { |label, id| { id:, name: label } } | ||
| end |
| JOIN_PRIMARY = <<~SQL.squish | ||
| #{JOIN_ALL} | ||
| AND work_package_versions.version_id = ( | ||
| SELECT MIN(primary_target.version_id) | ||
| FROM work_package_versions primary_target | ||
| WHERE primary_target.work_package_id = entries.entity_id | ||
| AND primary_target.kind = 'target' | ||
| ) | ||
| SQL |
Ticket: https://community.openproject.org/wp/COMMS-916
Moves the cost report's Version filter and Version group-by off the deprecated
work_packages.version_idcolumn onto thework_package_versionstarget association. A work package with several target versions is now matched by the filter through any of them and listed under each of them when grouping.Important
Following the team decision, the overlapping totals a multi-value work package produces are accepted for consistency with how multi-value custom fields already behave here; deduplication is a separate piece of work.
See OPEN POINT FND-178 Cost reports over-count totals when grouping or filtering by a multi-value attribute
Screenshots
Feature flag ON 🌞
Group by Version
Filter by Version
Feature flag OFF 🌚