Repository navigation
F17 follow-up: voiding a daily entry vacates its day for re-entry (#82) - #83
Merged
Merged
Conversation
added 2 commits
July 19, 2026 10:58
A voided entry kept occupying the (account, farm, house, flock, date) natural key: RecordDailyEntry found it with no status filter and the Draft-only guard rejected the save with a misleading "Only draft entries can be edited." — the day could never be recorded again. - FindByNaturalKeyAsync excludes Voided entries; the natural-key unique index becomes partial (WHERE "Status" <> 'Voided'): at most one live entry per key, any number of voided ones preserved in history. - SPA prefill skips voided entries (limit raised past 1 — a re-recorded day keeps its voided siblings on the same date) and the submitted-day banner drops the stale "(coming later)" for the shipped History adjust/void controls. - Help + GLOSSARY: voiding frees the day for a fresh entry. - Regression test: live entry still blocks the key; void -> re-record -> void -> re-record leaves two voided rows and a live one on the same day, with the bird ledger netting back out.
The stored status values are enum member names (HasConversion<string>), so nameof keeps the raw-SQL filter compile-checked against renames. Generated SQL is unchanged — no model drift, no new migration.
mforce
pushed a commit
that referenced
this pull request
Jul 19, 2026
- FindByNaturalKeyAsync keeps the tenant query filter on: the explicit AccountId predicate becomes belt-and-suspenders instead of the only tenant guard (IgnoreQueryFilters was inherited, not needed here). - Daily entry prefill fetches the server's default page (100) instead of 10 so the live row can't fall off the set behind voided siblings. Declined with rationale (see PR #81 comment): unique-violation race on concurrent creates keeps the repo-wide precheck+index-backstop pattern (same as grade names); migration integrity assertion would check a state the old fully-unique index made impossible.
mforce
pushed a commit
that referenced
this pull request
Jul 19, 2026
entryFor picked the first same-flock row for today regardless of status; after void + re-record the arbitrary (GUID) ordering could surface the voided row's totals instead of the replacement — or mark a genuinely vacated day as covered. Voided rows are now skipped, so a day with only voided entries correctly reads as 'no entry yet'.
mforce
added a commit
that referenced
this pull request
Jul 19, 2026
…#81) * F17 (part 2): History-page admin adjust/void with status badges (#69) - Status column renders the full lifecycle: Locked (muted, lock time on hover), Adjusted (reason on hover), Voided (warn + reason, row dimmed). - Admin-only actions on Submitted/Locked/ManagerAdjusted rows: adjust opens a panel prefilled with the entry (totals, losses, deaths, grade lines — the entry's own grades plus the active saleable catalog; a deactivated grade already on the entry stays correctable), requires a reason, and sends the loaded version as its base; void uses the F13 prompt-as-confirmation with a required reason. Workers see no actions and the API enforces the role regardless. - Previously adjusted entries show the audit snapshot summary in the panel. 409 conflicts reload the list with a human message; the 422s (sold stock, pre-link entries) surface the API's explanation as-is. - DailyEntry client type gains version/adjustReason/voidReason/lockedAtUtc/ adjustedFrom; new adjustDailyEntry/voidDailyEntry API functions. - Help page: History section describes the badges and controls; the two Fixing-mistakes rows now point at History -> adjust/void. Closes #69. * Address codex review of PR #81 - Key lifecycle split by outcome: any server response (success or 4xx) rotates the idempotency key so an edited retry is a fresh request — the version base already guards double-apply; only transport failures keep the key for an exact replay. Fixes the write-succeeded/refresh-failed trap where an edited retry replayed the old success and reported the new values as saved. - 409 on adjust re-binds the panel to the freshly fetched entry (typed values survive, new version becomes the base) instead of closing and discarding the correction; if the entry became uncorrectable meanwhile, the panel closes with an explanation. Reload failures no longer claim the list was reloaded. - Voiding an entry that is open in the adjust panel closes the panel. - Void success message no longer says "stock returned" — voiding production empties the lots; it now says so. - Panel scrolls into view and focuses its first input on open; error and success banners carry role=alert/status for assistive tech. * Address pi review of PR #81 - 409 rebind refills every field from the freshly fetched entry instead of keeping this admin's typed numbers — a kept grade map could silently clobber a line the race winner just added. Only the reason survives. - Client-side sellable-cap check (grades <= total - losses) for an instant message; the API stays the authority. * F17 follow-up: voiding a daily entry vacates its day for re-entry (#82) (#83) * F17 follow-up: voiding a daily entry vacates its day for re-entry (#82) A voided entry kept occupying the (account, farm, house, flock, date) natural key: RecordDailyEntry found it with no status filter and the Draft-only guard rejected the save with a misleading "Only draft entries can be edited." — the day could never be recorded again. - FindByNaturalKeyAsync excludes Voided entries; the natural-key unique index becomes partial (WHERE "Status" <> 'Voided'): at most one live entry per key, any number of voided ones preserved in history. - SPA prefill skips voided entries (limit raised past 1 — a re-recorded day keeps its voided siblings on the same date) and the submitted-day banner drops the stale "(coming later)" for the shipped History adjust/void controls. - Help + GLOSSARY: voiding frees the day for a fresh entry. - Regression test: live entry still blocks the key; void -> re-record -> void -> re-record leaves two voided rows and a live one on the same day, with the bird ledger netting back out. * Use nameof for the partial index filter's column and enum literals The stored status values are enum member names (HasConversion<string>), so nameof keeps the raw-SQL filter compile-checked against renames. Generated SQL is unchanged — no model drift, no new migration. --------- Co-authored-by: mforce <> * Address pi review of PR #83 - FindByNaturalKeyAsync keeps the tenant query filter on: the explicit AccountId predicate becomes belt-and-suspenders instead of the only tenant guard (IgnoreQueryFilters was inherited, not needed here). - Daily entry prefill fetches the server's default page (100) instead of 10 so the live row can't fall off the set behind voided siblings. Declined with rationale (see PR #81 comment): unique-violation race on concurrent creates keeps the repo-wide precheck+index-backstop pattern (same as grade names); migration integrity assertion would check a state the old fully-unique index made impossible. * Address codex review of PR #83: dashboard must not show a voided entry entryFor picked the first same-flock row for today regardless of status; after void + re-record the arbitrary (GUID) ordering could surface the voided row's totals instead of the replacement — or mark a genuinely vacated day as covered. Voided rows are now skipped, so a day with only voided entries correctly reads as 'no entry yet'. --------- Co-authored-by: mforce <>
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.
Closes #82. Stacked on #81 (base =
feat/f17-entry-adjust-ui) — the Help-page void wording this PR extends lives there; GitHub retargets tomainautomatically once #81 merges.Problem
A voided entry kept occupying its
(account, farm, house, flock, date)natural key.RecordDailyEntryHandlerfound it with no status filter and the Draft-only guard refused with 422 "Only draft entries can be edited." — so a voided day could never be recorded again, with an error that didn't explain why. Void means "this entry should never have existed"; the day's real production still needs a way in.Fix — Voided vacates the key
FindByNaturalKeyAsyncexcludesVoidedentries.IX_DailyEntries_NaturalKeybecomes a partial unique index (WHERE "Status" <> 'Voided'): at most one live entry per house/flock/day, any number of voided ones preserved in history (migrationVoidedEntryVacatesNaturalKey).limitpast 1 (a re-recorded day keeps its voided siblings on the same date; same-date ordering is by id, solimit: 1could return a voided row and hide the live one).Tests (224 green)
Void_VacatesDay_SameKeyCanBeReRecorded: live entry still blocks the key (422) → void → re-record succeeds as a fresh entry (new id; the voided one keeps its numbers) → full lifecycle on the replacement → void it too → a third entry still fits (two voided rows + one live on the same key) → History lists all three → bird ledger nets back to the seeded count.Runtime verify (Playwright, alt-port stack)
Void from History → Daily entry for the same flock+day comes up fresh (no banner, no voided prefill) → save & submit succeeds → banner shows the corrected wording → History shows
Voided+Submittedrows side by side.Notes