mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
Three things the tests said were covered and were not. A column flipped from honoured to ignored moved both sides of its own assertion, because the check read the verdict off the table it was checking. The verdicts are now written out as literals, so changing one is a decision somebody states twice. The preview block was unreachable from an SSR-only test, so nothing would have noticed it rendering through the outcome view — which would classify what a preview *would* write as a success and put a tick on a run that never ran. It moves into its own component that takes the verdict as a prop, the way the CSV result view already does, and the preview and the archived-row report are asserted directly. The account-wide import narrows its in-progress check to account-wide jobs, so an import running on one medication's page does not refuse it. Nothing pinned that; now something does. Also correct two comments about the earlier replay-key fault. The repeated value in the reported file was not a dose quantity — it was copied from the example this dialog itself displays. The field never carried identity, and the submission was never at fault.
53 lines
2.1 KiB
TypeScript
53 lines
2.1 KiB
TypeScript
/**
|
|
* The one derivation from a submitted intake-import row to the queued entry.
|
|
*
|
|
* It lives here rather than inline in the route so the test that proves a
|
|
* reported export lands 28 rows can drive the real derivation instead of
|
|
* restating it. A test that mirrors the production formula proves the mirror.
|
|
*/
|
|
|
|
/** A validated row of the import body: a local date and a local wall time. */
|
|
export interface MedicationImportRow {
|
|
datum: string;
|
|
uhrzeit: string;
|
|
}
|
|
|
|
export interface MedicationImportQueueEntry {
|
|
scheduledFor: string;
|
|
takenAt: string;
|
|
idempotencyKey: string;
|
|
}
|
|
|
|
/**
|
|
* An imported dose is identified by its medication and its instant, and by
|
|
* nothing else.
|
|
*
|
|
* The key used to be built from the row's optional `zaehler` field whenever it
|
|
* was present. That reads as a per-row counter, but nothing required it to be
|
|
* one and nothing checked. The file that surfaced this repeated `1.0` down the
|
|
* column because it was modelled on the example the import dialog itself
|
|
* displayed — so the value came from us, and a field we presented as a quantity
|
|
* silently decided identity. A month of distinct doses collapsed onto one key.
|
|
*
|
|
* Deriving from the instant makes the key a function of the fact it identifies,
|
|
* and matches the live-row unique on `(user_id, medication_id, scheduled_for,
|
|
* source)` that the database enforces regardless.
|
|
*/
|
|
export function buildMedicationImportEntries(
|
|
medicationId: string,
|
|
rows: readonly MedicationImportRow[],
|
|
): MedicationImportQueueEntry[] {
|
|
return rows.map((row) => {
|
|
const takenAt = new Date(`${row.datum}T${row.uhrzeit}`);
|
|
return {
|
|
// This body carries one time per row and nothing to say which slot it
|
|
// belonged to, so every dose it describes is ad-hoc: anchored on itself,
|
|
// which is how HealthLog already records a dose that belongs to no slot.
|
|
// The export-reading route is the one that has both facts to give.
|
|
scheduledFor: takenAt.toISOString(),
|
|
takenAt: takenAt.toISOString(),
|
|
idempotencyKey: `import-${medicationId}-${takenAt.getTime()}`,
|
|
};
|
|
});
|
|
}
|