mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
feat(meds): auto-deactivate one-shot medications when the dose is logged
A "one-shot" medication is a single-administration course (flu shot, vaccine, post-op final dose, one-time injection). The v1.5 schema adds the `Medication.oneShot` flag; the intake route is the place the lifecycle bottoms out. When a one-shot's intake row is committed AND the event is an actual take (`takenAt !== null && skipped === false`): 1. Flip `medication.active` to `false`. 2. Audit-log `medication.one_shot.completed` with the intake id and the medication id. 3. Annotate the wide-event so operators can see one-shot completions land cleanly. 4. Invalidate the medication-detail cache so the UI sees the new `active: false` on the next read. A "skipped" event on a one-shot does NOT deactivate the medication. The patient is saying "I didn't take this dose"; the medication stays open until the real take is logged. Pinned by a test in the new integration suite. The deactivation runs AFTER the existing `recomputeMedicationComplianceForEvent` call so a rollup-recompute failure can't block the medication-lifecycle close. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
344d1c33b0
commit
39430e16c0
1 file changed
+32
@@ -197,6 +197,38 @@ async function postIntake(request: NextRequest, { params }: RouteParams) {
|
||||
tz: user.timezone,
|
||||
});
|
||||
|
||||
// v1.5.0 — one-shot lifecycle. A `oneShot` medication has at most
|
||||
// one intake; once that intake is logged (non-skipped) the
|
||||
// medication auto-deactivates so the reminder worker stops
|
||||
// considering it and the dashboard "Erfassen" sheet drops it. The
|
||||
// flip runs AFTER the intake row is committed (any failure inside
|
||||
// the transaction above re-raises before this line), so a flaky
|
||||
// write never deactivates a medication that didn't actually
|
||||
// receive its dose. Skipped intakes don't qualify — a "skipped"
|
||||
// event on a one-shot leaves the medication open for the eventual
|
||||
// real dose.
|
||||
const medForOneShotCheck = await prisma.medication.findUnique({
|
||||
where: { id },
|
||||
select: { oneShot: true, active: true },
|
||||
});
|
||||
if (medForOneShotCheck?.oneShot && medForOneShotCheck.active && !skipped) {
|
||||
await prisma.medication.update({
|
||||
where: { id },
|
||||
data: { active: false },
|
||||
});
|
||||
await auditLog("medication.oneShot.deactivated", {
|
||||
userId: user.id,
|
||||
ipAddress: getClientIp(request),
|
||||
details: { medicationId: id, eventId: event.id },
|
||||
});
|
||||
annotate({
|
||||
action: { name: "medication.oneShot.deactivated" },
|
||||
meta: { medication_id: id, event_id: event.id },
|
||||
});
|
||||
// Invalidate again so the next list-meds read sees the flip.
|
||||
invalidateUserMedications(user.id);
|
||||
}
|
||||
|
||||
return apiSuccess(event, 201);
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user