Skip to content

Make the replacement field optional and neutrally worded on HR-recorded absences - #48

Merged
karlitschek merged 2 commits into
mainfrom
fix/noid/replacement-optional-for-hr-records
Aug 12, 2026
Merged

Make the replacement field optional and neutrally worded on HR-recorded absences#48
karlitschek merged 2 commits into
mainfrom
fix/noid/replacement-optional-for-hr-records

Conversation

@karlitschek

Copy link
Copy Markdown
Member

"Who covers for you?" is the right question when you are booking your own leave. It is the wrong one when HR records an absence for somebody else — and the requirement behind it was wrong there too: the replacement field was mandatory for any requires_replacement type regardless of who was filling the form in.

The rule belongs to the employee, not to the record

Somebody arranging their own leave knows who can cover and is asked to sort it out before going (§5.1). HR recording or correcting an absence for somebody else is stating a fact, often after the event, and cannot nominate cover on that person's behalf.

So on those paths the field is now offered but never demanded, and reads:

Label Placeholder
Your own leave Replacement * Who covers for you?
Somebody else's, recorded by HR Replacement (optional) Who is the replacement?

The hint follows too — "Optional. A colleague who covers for Lea while they are away, and is notified once this is recorded."

The wording keys off whose leave it is, not off HR mode. So HR recording their own absence — possible since #47 — still gets "Who covers for you?" and the requirement that goes with it, which raw hrMode would have got wrong.

Enforced on both sides, not just hidden: resolveReplacement() takes a required flag, false from create() when onBehalf and from hrEdit(). Without the server half the dialog would allow submit and the API would then reject it.

Also closes a hole

Found while restructuring the check. For a type that did not require a replacement, whatever was submitted was stored unvalidated:

if (!$type->getRequiresReplacement()) {
    return $replacementUid !== null && trim($replacementUid) !== '' ? trim($replacementUid) : null;
}

The guest and self checks sat below that early return, so a guest — or the employee themselves — could be recorded as covering, on sick leave for instance. The method's own docblock claimed "Also rejects guests". Only the demand is conditional; who may be named never was, and now isn't.

Testing

174 tests, 483 assertions, 0 failures (3 new). Each was checked against the original code: the HR-records-for-someone-else case errors without the required flag, and the guest case fails without the restructure. The third pins that self-service still demands a replacement, so the loosening did not leak into the employee path.

eslint 0 errors, stylelint clean, 27/27 vitest, build succeeds. Bundles recompiled in the last commit.

🤖 Generated with Claude Code

Frank Karlitschek and others added 2 commits August 12, 2026 16:16
…absence

"Who covers for you?" is the right question when you are booking your own leave.
It is the wrong one when HR records an absence for somebody else, and the
requirement behind it is wrong there too: the replacement field was mandatory for
any requires_replacement type regardless of who was filling the form in.

The rule is about the employee, not the record. Somebody arranging their own leave
knows who can cover and is asked to sort it out before going. HR recording or
correcting an absence for somebody else is stating a fact, often after the event,
and cannot nominate cover on that person's behalf.

So the field is now offered but not demanded on those paths, and reads "Who is the
replacement?" with an (optional) marker and a hint naming the employee. The
wording keys off whose leave it is rather than off HR mode, so HR recording their
*own* absence — possible since #47 — still gets "Who covers for you?" and the
requirement that goes with it.

Also closes a hole found while restructuring the check: for a type that did not
require a replacement, whatever was submitted was stored unvalidated, so a guest
or the employee themselves could be recorded as covering. Only the *demand* is
conditional; who may be named never was.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@nextcloud-command nextcloud-command added the AI assisted This PR contains AI-assisted commits label Aug 12, 2026
@karlitschek
karlitschek merged commit e032be0 into main Aug 12, 2026
29 of 35 checks passed
@karlitschek
karlitschek deleted the fix/noid/replacement-optional-for-hr-records branch August 12, 2026 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AI assisted This PR contains AI-assisted commits

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants