Files
HealthLog/prisma/migrations/0482_medication_course_ended_at/migration.sql
T
Marc-André Bombeck 23a67f61e6 feat(medications): let a course end at an instant
A course's end date is a calendar day and counts in full: the doses of that
day stay due, reminded and expected until local midnight. That is right for
an end date set in the editor and wrong for "End course", whose dialog
promises that nothing more is due.

A course now carries `endedAt` (migration 0482, additive, nullable), the
instant it was ended on its `endsOn` day, projected onto the medication next
to `startsOn` / `endsOn` by the one course writer. From that instant:

- the schedule engine emits no slot, so a dose later that day is not due,
  not reminded, not projected onto the today list and never expected by
  compliance, while a dose earlier that day stays inside the course;
- the course reads ENDED (`courseStatusAt` / `courseStatusOn`), so the dose
  actions, `remindersActive` and the reminder gate stop at once, follow-ups
  of an earlier dose included;
- every engine context builder, compliance context, dose history, Coach
  snapshot, doctor report and the adherence storyline read it, through one
  select and one picker for the medication window.

Any write that moves a course's `endsOn` clears the instant, so an end date
chosen in the editor keeps meaning the whole day. Backups carry it in both
purposes and a restore projects it again. `courseStatusAt` moves next to the
other course rules.

Refs #1280
2026-10-24 22:20:06 +02:00

30 lines
1.3 KiB
SQL

-- "End course" ends a course at the moment it is pressed (#1280).
--
-- A course's `ends_on` is a calendar day and counts in full: the doses of
-- that day stay due, reminded and expected until local midnight. That is the
-- right reading for an end date written in the course editor ("the last day
-- of the pack"), and the wrong one for "End course", whose dialog promises
-- that nothing more is due. `ended_at` records the instant the course was
-- ended that way: it lies on the `ends_on` day, a dose slot at or after it
-- is not part of the course, and the course reads as ended from then on.
-- NULL keeps the full-day reading every existing course has.
--
-- `medications.ended_at` is the projection of the latest course's value,
-- written next to `starts_on` / `ends_on` by the one course writer, so the
-- engines that read the medication's window read it too.
--
-- Nullable, no default, no backfill: no existing course was ended this way.
-- Reversibility: dropping both columns restores the full-day reading.
--
-- @retry-on-lock-timeout
DO $migration$
BEGIN
PERFORM set_config('lock_timeout', '5s', true);
ALTER TABLE "medication_courses"
ADD COLUMN IF NOT EXISTS "ended_at" TIMESTAMPTZ(6);
ALTER TABLE "medications"
ADD COLUMN IF NOT EXISTS "ended_at" TIMESTAMPTZ(6);
END
$migration$;