Confidential leave categories, HR tools, in-app handbooks, i18n and performance - #53
Merged
karlitschek merged 1 commit intoAug 18, 2026
Conversation
… and performance Confidentiality (§5.7, §5.8): - Leave types gain an hr_only flag: maternity leave, parental leave, medical work prohibition and doctor's note ship seeded (idempotently, updates too). Recorded by HR as a category of sick leave; visible to HR alone. Everyone else — the employee's own views, calendars, timelines, balances, the type list itself — sees a neutral "Absent". Enforced at serialization, coverage, CalDAV titles and the SPA bootstrap, not hidden in the UI. - Annual leave requests gain an HR-only disability flag for the additional statutory entitlement: set and seen exclusively by HR, excluded from the request history, recorded in the always-on audit log. HR tools: - CSV exports accept a group filter (endpoint + UI), fed by a new HR-only group listing endpoint. - Statistics reworked: average sick days per employee over the calendar year replaces the per-month average, and a "most vacation still to take" list ranks unplanned available days so HR can nudge people before year end. - Managers get a team balances table under the team timeline, served by GET /api/team/balances scoped strictly to the caller's own reports. Handbooks: - An illustrated in-app handbook for employees and team leads, and an extended HR edition, both linked in the sidebar. Element-level screenshots ship under img/handbook/; every string is translatable. Correctness & UX: - The escalation job can no longer clobber a concurrent decision (conditional UPDATE ... WHERE status = 'PENDING'). - Working days are validated against the calendar span of the leave. - HR corrections publish an honest request_updated activity subject. - The working-day field warns in both dialogs that the prefill is an estimate that must be checked and adjusted manually. - An optional employees group narrows who counts as an employee (keeps service accounts out of reports and bounds directory enumeration). Internationalisation: - Every server-side user-facing string (domain exceptions, calendar event titles, declarative settings) now goes through IL10N; personal calendar events use the employee's language. Performance: - Direct reports resolve from one indexed preference query instead of enumerating the user backend on nearly every request; isManagerOf() reads the employee's own manager property. - Session badges use COUNT(*) queries; leave types are memoised per request. - Batch balances aggregate in SQL, scaling with headcount instead of years of accumulated history; single-employee balances load only the asked year. - New indexes: (status, created_at) and supersedes_id. Docs and assets: README and SPECIFICATION updated throughout (new §5.7, §5.8, §13.1), screenshots refreshed with descriptive names, built js/ included per convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
karlitschek
deleted the
feat/confidential-categories-handbooks-and-hr-tools
branch
August 18, 2026 19:03
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
One combined PR for the August round of work, developed and verified on the devel instance with demo data. Everything is enforced server-side and covered by tests (215 PHP / 43 frontend, psalm clean).
Confidential leave categories (new §5.7)
Leave types gain an
hr_onlyflag. Maternity leave 🤱, parental leave 🍼, medical work prohibition ⚕️ and doctor's note 🩺 ship seeded — on existing installations too, idempotently per key. HR records them by picking Sick leave and a Category; only HR ever sees which. Everyone else — including the employee's own views, calendars, timelines, balance rows and even the type list in the browser — gets a neutral "Absent". The admin's "reveal types" calendar setting deliberately does not extend to these.HR-only disability flag on annual leave (new §5.8)
For the additional statutory entitlement: a tick HR can set when recording/editing annual leave. Withheld from every non-HR viewer (the employee included), excluded from the request history, recorded in the always-on audit log. A crafted self-service payload cannot set it.
HR tooling
GET /api/groups)GET /api/team/balances, scoped strictly to the caller's own reports — the samecanViewBalanceOfrule that already applied per person)In-app handbooks
Illustrated handbook for employees & team leads plus an extended HR edition, linked in the sidebar. Element-level screenshots ship under
img/handbook/(REUSE-covered); all strings translatable.Correctness & UX
UPDATE … WHERE status='PENDING')request_updatedactivity subjecti18n
Every server-side user-facing string (domain exceptions, calendar titles, settings) now goes through
IL10N; personal calendar events use the employee's language.Performance (large-instance work)
isManagerOf()reads the employee's own manager propertyCOUNT(*); leave types memoised per request; new indexes(status, created_at)andsupersedes_idDocs & assets
README + SPECIFICATION updated throughout (§3.2, §5.7, §5.8, §13.1, indexes, manager resolution); screenshots refreshed against the current UI with descriptive names; built
js/included per convention. Version bump intentionally left out.🤖 Generated with Claude Code