From 08a1dbccacb6863b5a6140df7bad072fb1a9cfd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Bombeck?= Date: Mon, 10 Aug 2026 06:59:57 +0200 Subject: [PATCH] feat(backup): carry per-schedule units-per-dose through backup and restore The schedule exporter enumerates columns, so the new per-schedule units-per-dose needs adding by hand: export it as a Decimal string in the disaster-recovery payload, accept it in the wire schema, and write it back on restore (NULL stays NULL to inherit). Extend the round-trip test to prove a half-tablet slot survives export and re-import. --- src/app/api/admin/backups/[id]/restore/route.ts | 3 +++ src/lib/export/__tests__/full-backup-payload.test.ts | 4 ++++ src/lib/export/full-backup-payload.ts | 4 ++++ src/lib/validations/backup.ts | 3 +++ 4 files changed, 14 insertions(+) diff --git a/src/app/api/admin/backups/[id]/restore/route.ts b/src/app/api/admin/backups/[id]/restore/route.ts index d7fac6681..52dc674fa 100644 --- a/src/app/api/admin/backups/[id]/restore/route.ts +++ b/src/app/api/admin/backups/[id]/restore/route.ts @@ -581,6 +581,9 @@ const handler = apiHandler( windowEnd: s.windowEnd, label: s.label ?? null, dose: s.dose ?? null, + // #219 — per-schedule units per dose. Prisma coerces the + // Decimal string; NULL / absent stays NULL (inherit). + unitsPerDose: s.unitsPerDose ?? null, daysOfWeek: s.daysOfWeek ?? null, timesOfDay: s.timesOfDay ?? [], reminderGraceMinutes: s.reminderGraceMinutes ?? null, diff --git a/src/lib/export/__tests__/full-backup-payload.test.ts b/src/lib/export/__tests__/full-backup-payload.test.ts index c7aeccfbd..1bfe3d447 100644 --- a/src/lib/export/__tests__/full-backup-payload.test.ts +++ b/src/lib/export/__tests__/full-backup-payload.test.ts @@ -131,6 +131,8 @@ function makePrisma() { scheduleType: "CYCLIC", cyclicOnWeeks: 3, cyclicOffWeeks: 1, + // #219 — per-schedule units per dose (a half tablet at this slot). + unitsPerDose: { toString: () => "0.5000" }, doseWindows: [ { timeOfDay: "08:00", start: "07:30", end: "09:00" }, ], @@ -502,6 +504,8 @@ describe("buildFullBackupPayload disaster-recovery mode", () => { scheduleType: "CYCLIC", cyclicOnWeeks: 3, cyclicOffWeeks: 1, + // #219 — Decimal → string, round-tripped through the DR payload. + unitsPerDose: "0.5000", }), ], }), diff --git a/src/lib/export/full-backup-payload.ts b/src/lib/export/full-backup-payload.ts index 05a9537a7..22b31a229 100644 --- a/src/lib/export/full-backup-payload.ts +++ b/src/lib/export/full-backup-payload.ts @@ -507,6 +507,10 @@ export async function buildFullBackupPayload( cyclicOnWeeks: s.cyclicOnWeeks, cyclicOffWeeks: s.cyclicOffWeeks, doseWindows: s.doseWindows, + // #219 — per-schedule units per dose. Decimal → string like the + // medication-level column; NULL (inherit) stays NULL. + unitsPerDose: + s.unitsPerDose == null ? null : s.unitsPerDose.toString(), } : {}), windowStart: s.windowStart, diff --git a/src/lib/validations/backup.ts b/src/lib/validations/backup.ts index e22f68524..b9a84977a 100644 --- a/src/lib/validations/backup.ts +++ b/src/lib/validations/backup.ts @@ -127,6 +127,9 @@ const medicationScheduleSchema = z windowEnd: z.string().min(1), label: z.string().nullable().optional(), dose: z.string().nullable().optional(), + // #219 — per-schedule units per dose. Serialised as a Decimal string in a + // DR file (or a number in a hand-authored one); NULL means inherit. + unitsPerDose: z.union([z.string(), z.number()]).nullable().optional(), daysOfWeek: z.string().nullable().optional(), timesOfDay: z.array(z.string()).optional(), reminderGraceMinutes: z.number().int().nullable().optional(),