Skip to content

Confidential leave categories, HR tools, in-app handbooks, i18n and performance - #53

Merged
karlitschek merged 1 commit into
mainfrom
feat/confidential-categories-handbooks-and-hr-tools
Aug 18, 2026
Merged

Confidential leave categories, HR tools, in-app handbooks, i18n and performance#53
karlitschek merged 1 commit into
mainfrom
feat/confidential-categories-handbooks-and-hr-tools

Conversation

@karlitschek

Copy link
Copy Markdown
Member

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_only flag. 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

  • Exports accept a group filter (endpoint + UI, backed by a new HR-only GET /api/groups)
  • Statistics reworked: average sick days per employee over the calendar year, and a "most vacation still to take" ranking of unplanned available days (flagging >50% of entitlement unused)
  • Managers get a team balances table under the team timeline (GET /api/team/balances, scoped strictly to the caller's own reports — the same canViewBalanceOf rule 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

  • Escalation job can no longer clobber a concurrent decision (conditional UPDATE … WHERE status='PENDING')
  • Working days validated against the calendar span (catches the 40-for-4 typo)
  • HR corrections publish an honest request_updated activity subject
  • ⚠️ estimate warning under the working-days field in both dialogs
  • Optional employees group setting: only members count as employees — keeps service accounts out and bounds directory enumeration on large instances

i18n

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)

  • Direct reports resolved from one indexed preference query instead of walking the user backend on nearly every request; isManagerOf() reads the employee's own manager property
  • Batch balances aggregated in SQL — scales with headcount, not years of history; single-employee views load only the requested year
  • Session badges via COUNT(*); leave types memoised per request; new indexes (status, created_at) and supersedes_id

Docs & 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

… 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>
@nextcloud-command nextcloud-command added the AI assisted This PR contains AI-assisted commits label Aug 18, 2026
@karlitschek
karlitschek merged commit 0196a72 into main Aug 18, 2026
25 of 30 checks passed
@karlitschek
karlitschek deleted the feat/confidential-categories-handbooks-and-hr-tools branch August 18, 2026 19:03
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