Make the history say what actually changed - #45
Merged
Conversation
The request timeline said "HR adjusted to 2 – 6 March (5 days)", which is the resulting state — readable, and useless for the question anybody opens the history to ask. The request itself already shows what it says now. What the timeline is for is what somebody changed and by how much, and a day count means nothing without the number it replaced. Edits now report the difference, field by field: "Working days 3 → 5 (+2)", "Dates 2–4 Mar → 2–6 Mar", "Reason “Wedding” → …", replacement added or removed. Both edit paths snapshot the request before mutating it, since afterwards the old values are gone. A save that changes nothing writes no detail rather than a sentence implying it did. Creation gained a real detail line too — type, dates, day count and the employee's reason. That reason previously went only into the server log, on the grounds that the Details tab shows it; but the Details tab shows the *current* reason, so once HR corrected the request nothing could say what the leave had been booked for. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entitlement changes had nowhere to be recorded. The request timeline is keyed on
request_id and an entitlement belongs to no request, so an adjustment left a line
in nextcloud.log that nobody reads and an activity entry reading "Leave balance
of X was adjusted" — with neither the amount nor the reason.
The note HR is *required* to write when adjusting was the sharpest part: stored
on the entitlement row, displayed in no view at all, and overwritten by the next
adjustment. A mandatory field nobody could ever read.
Adds absence_entitlement_events: one row per figure a save actually moved,
carrying who, which figure, from what to what, and the note. One row per figure
rather than per save, so "+2 days for the wedding" is a fact that reads on its
own instead of something to be diffed out of a blob. A save that moves nothing
records nothing.
Surfaced where the question gets asked — the entitlement editor in HR → Balances,
next to the figure it explains — and over GET /api/entitlements/{id}/history.
The activity entry now says what changed rather than only that something did.
Writes are best-effort, like the request timeline: an unwritable history must not
cost HR the adjustment they just made. The events name the employee and describe
their allowance, so UserDeletedListener purges them with the rest (§17).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
The History tab said "Adjusted by HR" and, underneath, what the request had become. It never said what changed — so "+2 days added for the wedding" was nowhere to be found. Two separate causes; one commit each.
1. Edits recorded the result, not the change
request_hr_editedwrote"HR adjusted to 2026-03-02 – 2026-03-06 (5 days)". That is the resulting state, which the request itself already shows. What a history is for is what somebody changed and by how much — and a day count means nothing without the number it replaced.request_updatedwas worse:"Changed to <start> – <end>"never mentioned working days at all, so a pure day-count correction produced a line implying nothing had happened.Edits now report the difference, field by field:
Both edit paths snapshot the request before mutating it, since afterwards the old values are gone. A save that changes nothing writes no detail at all, rather than a sentence implying it did.
Creation gained a real detail line too — type, dates, day count and the employee's reason. That reason previously went only to
nextcloud.log, on the stated grounds that the Details tab shows it. But the Details tab shows the current reason, so once HR corrected the request, nothing could say what the leave had originally been booked for.2. Entitlement changes had no history at all
absence_request_eventsis keyed onrequest_id, and an entitlement belongs to no request — so an adjustment left only a log line nobody reads and an activity entry reading "Leave balance of X was adjusted", with neither the amount nor the reason.The sharpest part: the note HR is required to write when adjusting (
'A note is required when adjusting an entitlement.') was stored on the entitlement row, displayed in no view whatsoever — it appeared in zerosrc/files — and overwritten by the next adjustment. A mandatory field nobody could ever read.New
absence_entitlement_eventstable records one row per figure a save actually moved, carrying who, which figure, from what to what, and the note. One row per figure rather than per save, so "+2 days for the wedding" reads on its own instead of having to be diffed out of a blob. A save that moves nothing records nothing.Surfaced where the question is asked — the entitlement editor in HR → Balances, next to the figure it explains — and over
GET /api/entitlements/{id}/history. The activity entry now says what changed rather than only that something did.Writes are best-effort, like the request timeline: an unwritable history must not cost HR the adjustment they just made. The events name the employee and describe their allowance, so
UserDeletedListenerpurges them with the rest (§17).Testing
PHP: 163 tests, 451 assertions, 0 failures (2 new — one that an adjustment records field, delta, actor and note; one that a no-op save records nothing). Frontend: eslint 0 errors, stylelint clean, 27/27 vitest, production build succeeds. Bundles recompiled in the last commit.
Schema changes are in
Version1004Date20260812120000, guarded so a re-run is a no-op. Existing rows are unaffected — history starts accumulating from the first change after upgrade.🤖 Generated with Claude Code