Content
View differences
Updated by Klaus Zanders 12 days ago
**As a** developer / consumer **As** a \[enter role of `UserQuery` (HAL+JSON API, internal services such as the upcoming Resource Allocation feature, and any UI built on top of `PersistedQuery`) user\]
**I want to** filter, sort and select user queries by `UserCustomField` values \[enter objective\]
**so that** I can express selections like "all active developers, sorted by birthday, with the Job title column visible" using the same primitives we already have for projects and work packages. \[enter desired result\]
## Acceptance criteria **Acceptance criteria**
* [x] `UserQuery` accepts a `cf_<id>` filter for any visible `UserCustomField` (string, text, int, float, date, bool, list, multi-list).
<br>
**Technical notes**
* [x] All operators supported by the corresponding shared CF strategy work <br>
**Permissions and produce the expected SQL:
visibility considerations**
* `=`, `!`, `*`, `!*` for list / list-optional
* `=`, `!`, `~`, `!~`, `*`, `!*` for string/text
* `<>d`, `=d`, `>t-`, `<t-`, `t`, `w` and friends for date — including the "greater than or equal" use case via `<>d` with an open upper bound (`["YYYY-MM-DD", ""]`)
* the standard numeric operators
* [x] `UserQuery` accepts a `cf_<id>` order for any visible `UserCustomField` whose format _To whom is sortable (everything except `text`):
this feature visible?_
* asc and desc both produce stable, expected orderings
* NULL values land at the natural end (NULLS FIRST on asc, NULLS LAST on desc)
* text CFs are rejected as non-sortable (`available? == false`, `query.valid? == false`)
* [x] `UserQuery` accepts a `cf_<id>` select for any visible `UserCustomField`. `Queries::Users::Selects::CustomField.all_available` enumerates one select per visible CF, and each select's `caption` _When is the CF's `name`.
it not visible?_
**Translation considerations**
* [x] CF filters can be combined with the existing `name`, `status`, `group`, `login`, `blocked` filters _Key terms and with each other under AND semantics.
* [x] A `UserQuery` (which is a `PersistedQuery`) round-trips a CF filter, CF order and CF select through serialization. The on-disk JSONB shape for filters is `{ "attribute": "cf_<id>", "operator": "=", "values": ["<option_id>"] }`. Reloading reconstructs working filter/order/select objects that produce phrases in the same results.
* [x] If the referenced custom field is deleted, the deserializer degrades gracefully:
* filters become `NotExistingFilter` and the query reports `valid? == false`
* orders become a `NotExisting`\-style order and the query reports `valid? == false`
* selects become unavailable
* nothing raises
* [x] Existing `ProjectQuery` and `WorkPackageQuery` CF filtering / sorting / selecting behaviour is unchanged.
* [x] `Queries::Register.{filters,orders,selects}[UserQuery]` includes the three new classes.
##
##
##
###
## Out key languages_
**Out of scope scope**
* **Grouping** `**UserQuery**` **by a CF value** — only filter / order / select are added here; `group_by` for users is not introduced.
* **UI work** — no changes <br>
_Set the_ **To be informed/consulted teams** _field to include all teams necessary to be informed of the user administration list, user picker or any frontend filter / column / sort control. Hooking the new primitives into a UI is a follow-up.
* **API exposure** beyond what `UserQuery` already provides via the existing serialization path.
* **Filtering for Group or PlaceholderUser CFs.** `GroupCustomField` / placeholder-user CFs follow a similar pattern and would each need their own `CustomFieldContext` + filter + order + select.
* **Backfilling** or migrating existing persisted queries — there are none using `cf_<id>` for users today. changes._
**I want to** filter, sort and select user queries by `UserCustomField` values
**so that** I can express selections like "all active developers, sorted by birthday, with the Job title column visible" using the same primitives we already have for projects and work packages.
## Acceptance criteria
* [x] `UserQuery` accepts a `cf_<id>` filter for any visible `UserCustomField` (string, text, int, float, date, bool, list, multi-list).
**Technical notes**
**Permissions
* `=`, `!`, `~`, `!~`, `*`, `!*` for string/text
* `<>d`, `=d`, `>t-`, `<t-`, `t`, `w` and friends for date — including the "greater than or equal" use case via `<>d` with an open upper bound (`["YYYY-MM-DD", ""]`)
* the standard numeric operators
* [x] `UserQuery` accepts a `cf_<id>` order for any visible `UserCustomField` whose format
* NULL values land at the natural end (NULLS FIRST on asc, NULLS LAST on desc)
* text CFs are rejected as non-sortable (`available? == false`, `query.valid? == false`)
* [x] `UserQuery` accepts a `cf_<id>` select for any visible `UserCustomField`. `Queries::Users::Selects::CustomField.all_available` enumerates one select per visible CF, and each select's `caption`
**Translation considerations**
* [x] A `UserQuery` (which is a `PersistedQuery`) round-trips a CF filter, CF order and CF select through serialization. The on-disk JSONB shape for filters is `{ "attribute": "cf_<id>", "operator": "=", "values": ["<option_id>"] }`. Reloading reconstructs working filter/order/select objects that produce
* [x] If the referenced custom field is deleted, the deserializer degrades gracefully:
* filters become `NotExistingFilter` and the query reports `valid? == false`
* orders become a `NotExisting`\-style order and the query reports `valid? == false`
* selects become unavailable
* nothing raises
* [x] Existing `ProjectQuery` and `WorkPackageQuery` CF filtering / sorting / selecting behaviour is unchanged.
* [x] `Queries::Register.{filters,orders,selects}[UserQuery]` includes the three new classes.
##
##
##
###
## Out
**Out
* **Grouping** `**UserQuery**` **by a CF value** — only filter / order / select are added here; `group_by` for users is not introduced.
* **UI work** — no changes
_Set the_ **To be informed/consulted teams** _field
* **API exposure** beyond what `UserQuery` already provides via the existing serialization path.
* **Filtering for Group or PlaceholderUser CFs.** `GroupCustomField` / placeholder-user CFs follow a similar pattern and would each need their own `CustomFieldContext` + filter + order + select.
* **Backfilling** or migrating existing persisted queries — there are none using `cf_<id>` for users today.