feat(medications): end a course now through its own action

"End course" now posts to `POST /api/medications/{id}/courses/end`, which
ends the running course at that moment: its last day becomes today on the
person's clock and the instant is stored as `endedAt`, so a dose later today
is not due, not reminded and never missed, while one taken earlier still
counts. Pending placeholders already minted for later slots are tombstoned.
A medication without dates gets the course its window describes, ended now.

End stays its own verb instead of `PUT { endsOn }`: an end date set through
the PUT or the course editor keeps meaning the whole day, and the PUT keeps
accepting it on any course, since moving an end is a legitimate edit.

The list and detail reads serve `canEndCourse`, true exactly while a course
is running, and the action refuses with 409 `medications.course.notRunning`
when it is false, so no client can reopen an ended course or invent a
one-day course from a planned one by ending it. The web controls render End
from the served flag; the contributed tests keep their cases against it.

Refs #1280
This commit is contained in:
Marc-André Bombeck committed 2026-10-24 22:24:15 +02:00
1 parent 23a67f61e6
commit 275367f6e9
15 files changed
+990 -124

No files matched your search

+1
View File
@@ -312,6 +312,7 @@ export const ERROR_CODE_CATALOGUE: Readonly<Record<string, readonly string[]>> =
"medications.course.invalid",
"medications.course.invalidRange",
"medications.course.notFound",
"medications.course.notRunning",
"medications.course.oneShot",
"medications.course.overlap",
"medications.course.windowRequired",