fix: keep topical stock non-depleting in planner flows (#359)

* fix: keep topical stock non-depleting in planner and reports

* test: stabilize e2e selectors for updated medication semantics

* fix(backend): add missing planner translation keys
This commit is contained in:
Daniel Volz
2026-02-28 23:36:52 +01:00
committed by GitHub
parent 8efd99d738
commit 9e8a6315e7
19 changed files with 807 additions and 130 deletions
+56 -12
View File
@@ -298,6 +298,32 @@ function fmtDateTime(iso: string | null | undefined): string {
return `${m[3]}.${m[2]}.${m[1]} ${m[4]}:${m[5]}`;
}
function getTubeUnitKey(med: Medication): "form.ml" | "blisters.applications" {
if (med.packageType === "liquid_container") return "form.ml";
return med.medicationForm === "liquid" ? "form.ml" : "blisters.applications";
}
function getUsageText(med: Medication, usage: number, t: TFn): string {
if (med.packageType === "tube" || med.packageType === "liquid_container") {
return `${usage} ${t(getTubeUnitKey(med))}`;
}
return `${usage} ${usage === 1 ? t("common.pill") : t("common.pills")}`;
}
function getTotalCapacityLabel(med: Medication, t: TFn): string {
if (med.packageType === "tube" || med.packageType === "liquid_container") {
return t("form.totalAmountLabel", { unit: t(getTubeUnitKey(med)) });
}
return t("report.docTotalCapacity");
}
function getCurrentStockText(med: Medication, t: TFn): string {
if (med.packageType === "tube" || med.packageType === "liquid_container") {
return `${getPackageSize(med)} ${t(getTubeUnitKey(med))}`;
}
return `${getPackageSize(med)} ${t("common.pills")}`;
}
function generateTextReport(
meds: Medication[],
reportData: ReportData,
@@ -341,7 +367,16 @@ function generateTextReport(
// Package / Stock
lines.push(h3(t("report.docPackage")));
lines.push(
item(t("report.docPackageType"), med.packageType === "bottle" ? t("report.docBottle") : t("report.docBlister"))
item(
t("report.docPackageType"),
med.packageType === "bottle"
? t("report.docBottle")
: med.packageType === "tube"
? t("report.docTube")
: med.packageType === "liquid_container"
? t("form.packageTypeLiquidContainer")
: t("report.docBlister")
)
);
if (med.packageType === "blister") {
lines.push(item(t("report.docPacks"), String(med.packCount)));
@@ -349,10 +384,11 @@ function generateTextReport(
lines.push(item(t("report.docPillsPerBlister"), String(med.pillsPerBlister)));
if (med.looseTablets > 0) lines.push(item(t("report.docLoosePills"), String(med.looseTablets)));
} else {
lines.push(item(t("report.docTotalCapacity"), String(med.totalPills ?? med.looseTablets)));
lines.push(item(getTotalCapacityLabel(med, t), String(med.totalPills ?? med.looseTablets)));
}
lines.push(item(t("report.docCurrentStock"), `${getPackageSize(med)} ${t("common.pills")}`));
if (med.pillWeightMg) lines.push(item(t("report.docDosePerPill"), `${med.pillWeightMg} ${med.doseUnit ?? "mg"}`));
lines.push(item(t("report.docCurrentStock"), getCurrentStockText(med, t)));
if (med.packageType !== "tube" && med.packageType !== "liquid_container" && med.pillWeightMg)
lines.push(item(t("report.docDosePerPill"), `${med.pillWeightMg} ${med.doseUnit ?? "mg"}`));
if (med.expiryDate) lines.push(item(t("report.docExpiryDate"), fmtDate(med.expiryDate)));
if (med.notes) lines.push(item(t("report.docNotes"), med.notes));
lines.push("");
@@ -365,7 +401,7 @@ function generateTextReport(
if (intakes?.length) {
lines.push(h3(t("report.docIntakeSchedule")));
for (const intake of intakes) {
let entry = `${intake.usage} ${intake.usage === 1 ? t("common.pill") : t("common.pills")}`;
let entry = getUsageText(med, intake.usage, t);
entry += ` ${intake.every === 1 ? t("common.daily") : t("common.everyNDays", { count: intake.every })}`;
entry += ` ${t("form.blisters.from")} ${fmtDateTime(intake.start)}`;
if ("takenBy" in intake && intake.takenBy)
@@ -407,7 +443,7 @@ function generateTextReport(
if (data.refills.length > 0) {
lines.push(h3(t("report.docRefillHistory")));
for (const r of data.refills) {
let entry = `${fmtDate(r.refillDate)}: +${r.packsAdded} ${t("report.docPacks")}, +${r.loosePillsAdded} ${t("common.pills")}`;
let entry = `${fmtDate(r.refillDate)}: +${r.packsAdded} ${t("report.docPacks")}, +${r.loosePillsAdded} ${med.packageType === "tube" || med.packageType === "liquid_container" ? t(getTubeUnitKey(med)) : t("common.pills")}`;
if (r.usedPrescription) entry += ` ${t("report.docRefillPrescription")}`;
lines.push(fmt === "md" ? `- ${entry}` : `${entry}`);
}
@@ -539,7 +575,15 @@ function buildPrintHtml(
// Package / Stock
s += `<h3>${escHtml(t("report.docPackage"))}</h3>`;
s += `<table><tbody>`;
s += `<tr><td class="label">${escHtml(t("report.docPackageType"))}</td><td>${escHtml(med.packageType === "bottle" ? t("report.docBottle") : t("report.docBlister"))}</td></tr>`;
s += `<tr><td class="label">${escHtml(t("report.docPackageType"))}</td><td>${escHtml(
med.packageType === "bottle"
? t("report.docBottle")
: med.packageType === "tube"
? t("report.docTube")
: med.packageType === "liquid_container"
? t("form.packageTypeLiquidContainer")
: t("report.docBlister")
)}</td></tr>`;
if (med.packageType === "blister") {
s += `<tr><td class="label">${escHtml(t("report.docPacks"))}</td><td>${med.packCount}</td></tr>`;
s += `<tr><td class="label">${escHtml(t("report.docBlistersPerPack"))}</td><td>${med.blistersPerPack}</td></tr>`;
@@ -547,10 +591,10 @@ function buildPrintHtml(
if (med.looseTablets > 0)
s += `<tr><td class="label">${escHtml(t("report.docLoosePills"))}</td><td>${med.looseTablets}</td></tr>`;
} else {
s += `<tr><td class="label">${escHtml(t("report.docTotalCapacity"))}</td><td>${med.totalPills ?? med.looseTablets}</td></tr>`;
s += `<tr><td class="label">${escHtml(getTotalCapacityLabel(med, t))}</td><td>${med.totalPills ?? med.looseTablets}</td></tr>`;
}
s += `<tr><td class="label">${escHtml(t("report.docCurrentStock"))}</td><td>${getPackageSize(med)} ${escHtml(t("common.pills"))}</td></tr>`;
if (med.pillWeightMg)
s += `<tr><td class="label">${escHtml(t("report.docCurrentStock"))}</td><td>${escHtml(getCurrentStockText(med, t))}</td></tr>`;
if (med.packageType !== "tube" && med.packageType !== "liquid_container" && med.pillWeightMg)
s += `<tr><td class="label">${escHtml(t("report.docDosePerPill"))}</td><td>${med.pillWeightMg} ${escHtml(med.doseUnit ?? "mg")}</td></tr>`;
if (med.expiryDate)
s += `<tr><td class="label">${escHtml(t("report.docExpiryDate"))}</td><td>${fmtDate(med.expiryDate)}</td></tr>`;
@@ -567,7 +611,7 @@ function buildPrintHtml(
s += `<h3>${escHtml(t("report.docIntakeSchedule"))}</h3>`;
s += `<ul>`;
for (const intake of filteredPrintIntakes) {
let entry = `${intake.usage} ${escHtml(intake.usage === 1 ? t("common.pill") : t("common.pills"))}`;
let entry = escHtml(getUsageText(med, intake.usage, t));
entry += ` ${escHtml(intake.every === 1 ? t("common.daily") : t("common.everyNDays", { count: intake.every }))}`;
entry += ` ${escHtml(t("form.blisters.from"))} ${fmtDateTime(intake.start)}`;
if ("takenBy" in intake && intake.takenBy)
@@ -614,7 +658,7 @@ function buildPrintHtml(
s += `<h3>${escHtml(t("report.docRefillHistory"))}</h3>`;
s += `<ul>`;
for (const r of data.refills) {
let entry = `${fmtDate(r.refillDate)}: +${r.packsAdded} ${escHtml(t("report.docPacks"))}, +${r.loosePillsAdded} ${escHtml(t("common.pills"))}`;
let entry = `${fmtDate(r.refillDate)}: +${r.packsAdded} ${escHtml(t("report.docPacks"))}, +${r.loosePillsAdded} ${escHtml(med.packageType === "tube" || med.packageType === "liquid_container" ? t(getTubeUnitKey(med)) : t("common.pills"))}`;
if (r.usedPrescription) entry += ` <em>${escHtml(t("report.docRefillPrescription"))}</em>`;
s += `<li>${entry}</li>`;
}