Skip to content

[#73104] Add Sprint column to work package table#22706

Merged
EinLama merged 17 commits intodevfrom
feature/73104-sprint-column-sort-and-group-for-work-packages-table
Apr 21, 2026
Merged

[#73104] Add Sprint column to work package table#22706
EinLama merged 17 commits intodevfrom
feature/73104-sprint-column-sort-and-group-for-work-packages-table

Conversation

@EinLama
Copy link
Copy Markdown
Contributor

@EinLama EinLama commented Apr 9, 2026

Ticket

https://community.openproject.org/wp/73104

What are you trying to accomplish?

Allows you to select the sprint column within the work package table and sort and group it.

Requires the view_sprints permission in a project to be available.

Screenshots

image

What approach did you choose and why?

There are some notable points here:

  • Sorting uses the sprint name (as this is what the user sees in the work packages table). After that, sprints of the same name are sorted via start and finish dates.
  • Added the ability for models to provide an API resource link name (commit 5014766). A resource link is necessary to make grouping work. The previous implementation added a manual mapping for some classes. Agile::Sprint would be converted to agile_sprint, but sprint is correct. So we have to add a mapping. Since the core should not be required to know about the backlogs module, I added a way for models to provide their API resource link name. The given name will be used by the mapping if available.
  • To make the GROUP BY statement consider the view_sprints permission, a custom join was necessary - I think. The solution is similar to Implementation/62982 project phase column on work package list #18851, although this might be a case of "if I have a hammer, every problem looks like a nail". On the upside, this solution can be easily extended to work with further visibility checks in the future 👍

Merge checklist

  • Added/updated tests
  • Added/updated documentation in Lookbook (patterns, previews, etc)
  • Tested major browsers (Chrome, Firefox, Edge, ...)
  • Tested that the PDF export still works with sprints listed (and grouped by sprints)

@EinLama EinLama force-pushed the feature/73104-sprint-column-sort-and-group-for-work-packages-table branch 9 times, most recently from e4ff513 to ce40bf4 Compare April 14, 2026 05:46
@EinLama EinLama marked this pull request as ready for review April 14, 2026 06:08
@EinLama
Copy link
Copy Markdown
Contributor Author

EinLama commented Apr 16, 2026

I think we should not merge this until #22740 is in, so that we don't cause any conflicts for the big clean up PR. I will have to remove some lines about the feature flag, then. But that will be easy.

Copy link
Copy Markdown
Contributor

@ulferts ulferts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@EinLama as discussed, the preliminary review before checking it out using the application.

# Since the property is called `sprint`, but the model_name is `agile_sprint`, there would
# otherwise be a mismatch. Avoid that:
:sprint
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I personally dislike having code I would consider representation (in the API) within the model. I am aware that this is a preference, not a strict rule.

But maybe backlogs could register the mapping on the ResourceLinkGenerator in its engine.rb

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, yes it is indeed a bit odd, especially since our other models don't do it like this. I moved the name mapping into a patch like suggested.

create(:user,
member_with_permissions: {
project => project_permissions,
another_project => another_project_permissions
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should add another shared sprint where the user lacks permissions in the sharing project.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some test cases for this. Doing so uncovered an issue with the previous query. Fixed that 👍


def projects_with_view_sprints_permissions
Project.allowed_to(User.current, :view_sprints).select(:id)
end
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, sprints shared with projects are not all visible. Only if the user has the :view_sprints permission in the sharing project will the sprint be visible.

There is already a visible scope for sprints. This can be used and can more or less replace the hand written subselect (at the cost of a way more complicated SQL the scope hides).

The check in the JOIN condition can then be simplified to visible_sprints.id = work_packages.sprint_id I believe.

@EinLama EinLama force-pushed the feature/73104-sprint-column-sort-and-group-for-work-packages-table branch from 90275fd to efa5a0a Compare April 17, 2026 14:12
@EinLama EinLama requested a review from ulferts April 20, 2026 08:09
@EinLama EinLama force-pushed the feature/73104-sprint-column-sort-and-group-for-work-packages-table branch from 596b5d6 to 457affb Compare April 20, 2026 09:58
@EinLama
Copy link
Copy Markdown
Contributor Author

EinLama commented Apr 20, 2026

#22740 has been merged, feature flag references have been removed from this PR. Comments have been addressed 🤞

Copy link
Copy Markdown
Contributor

@ulferts ulferts left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good @EinLama .

There is only one thing that I would like fixed before merging which is the unnecessary joining of the projects table.

def determine_path_method(record)
return :sprint if record.is_a?(Agile::Sprint)

super
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't mean to suggest writing a patch, @EinLama. What I had in mind was something like

class API::V3::Utilities::ResourceLinkGenerator
  def self.register_mapping(klass, symbol)
     ...
  end
end

class OpenProject::Backlogs::Engine
  config.to_prepare do
     API::V3::Utilities::ResourceLinkGenerator.register_mapping(Agile::Sprint, :sprint)
  end
end

But this here will also do the trick.

#{visible_sprints.to_sql}
) AS visible_sprints
ON visible_sprints.id = work_packages.sprint_id
AND "projects"."id" IN (#{projects_with_view_sprints.select(:id).to_sql})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The joining of projects is not necessary here. You can simply check against project_id:

AND "work_packages"."project_id" IN (#{projects_with_view_sprints.select(:id).to_sql})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also join the projects_with_view_sprints which might make it a bit easier to read:

LEFT OUTER JOIN (#{projects_with_view_sprints.to_sql}) "view_sprints_projects" 
  ON "view_sprints_projects"."id" = "work_packages"."project_id"
LEFT OUTER JOIN (
     #{visible_sprints.to_sql}
  ) AS visible_sprints
  ON visible_sprints.id = work_packages.sprint_id
  AND view_sprints_projects.id IS NOT NULL

Then again, having written it, maybe it is not easier to read.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I replaced the condition to use work_packages.project_id 👍 Is that what you meant?

We cannot get rid of the additional join on the projects table at the top of the SQL statement due to similar reasons like the project phase definition. If that line/join is removed, we get the same error we got back then:

PG::UndefinedTable: ERROR: missing FROM-clause entry for table "projects" LINE 1: ...status_id" WHERE ((statuses.is_closed=FALSE) AND (projects.i... ^

# directly. Without this second condition, a sprint that is shared *to*
# project_receiving could leak into the sort/group for work packages that live in
# the sharer project itself, because `sprint_source_for` transitively includes the
# sharer when the user only has permission in the receiver.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch here. Took me a while to understand but makes sense.

@EinLama EinLama mentioned this pull request Apr 20, 2026
11 tasks
@EinLama EinLama merged commit e66e682 into dev Apr 21, 2026
17 of 18 checks passed
@EinLama EinLama deleted the feature/73104-sprint-column-sort-and-group-for-work-packages-table branch April 21, 2026 11:55
@github-actions github-actions Bot locked and limited conversation to collaborators Apr 21, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants