feat: T6 wave 6 – treatment form with medicine and lot, notices and settings UI, restricted zone UI
- Treatment form and bulk entry: catalogue medicine or free text, lot with oldest preselected on create, lot fields from the lot, dose with at most 3 decimals, catalogue prefill with explicit take-over of the withdrawal period on a medicine switch, ?editTreatment deep link. - Authority notices: list with year filter, create with draft dedupe link, detail steps with PDF download and mark-sent, settings with MM-TT check, digit-rule editor and "Annahme – bitte prüfen" notes; COMPLIANCE label in notifications and Telegram categories. - Restricted zone: page with active/planned/ended, create and end dialogs (endsOn inclusive), red band at the apiary, guards on move, merge, edit and new colony, override only with compliance:manage, harvest warning. - ARCHITECTURE.md §12.11, Annahmen 91–94; TASKS.md T6 done with open items. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
13dda0e139
commit
8188e4cab3
90 files changed
+8069
-265
No files matched your search
@@ -0,0 +1,59 @@
|
||||
import { formatQuantity, roundToThousandths } from "@/lib/medicines/format";
|
||||
import type { LotView } from "@/lib/medicines/types";
|
||||
|
||||
const SEPARATOR = " · ";
|
||||
|
||||
/** "2027-03-31" as "03/2027", read as written. */
|
||||
function monthYear(day: string): string {
|
||||
const [year, month] = day.split("-");
|
||||
return `${month}/${year}`;
|
||||
}
|
||||
|
||||
/** "Ch.-B. 4711 · Rest 180 ml · verw. bis 03/2027" */
|
||||
export function lotLabel(lot: LotView): string {
|
||||
const parts = [
|
||||
lot.batchNumber ? `Ch.-B. ${lot.batchNumber}` : "Ohne Ch.-B.",
|
||||
`Rest ${formatQuantity(lot.remaining, lot.unit)}`,
|
||||
];
|
||||
if (lot.expiresOn) parts.push(`verw. bis ${monthYear(lot.expiresOn)}`);
|
||||
return parts.join(SEPARATOR);
|
||||
}
|
||||
|
||||
/** What the pack supplies instead of the typed fields, e.g. "aus Packung: Ch.-B. 4711 · Lieferant X". */
|
||||
export function lotSourceLine(lot: LotView): string {
|
||||
const parts = [
|
||||
lot.batchNumber ? `Ch.-B. ${lot.batchNumber}` : null,
|
||||
lot.supplier ? `Lieferant ${lot.supplier}` : null,
|
||||
lot.purchaseProof ? `Beleg ${lot.purchaseProof}` : null,
|
||||
lot.prescribingVet ? `Tierarzt ${lot.prescribingVet}` : null,
|
||||
].filter((part): part is string => part !== null);
|
||||
return parts.length > 0 ? `aus Packung: ${parts.join(SEPARATOR)}` : "aus Packung";
|
||||
}
|
||||
|
||||
/**
|
||||
* What speaks against taking from this pack: expired on the treatment day, or
|
||||
* too little left for the dose of all colonies. Only warnings - the server
|
||||
* books anyway and the readiness check reports a negative rest.
|
||||
*/
|
||||
export function lotWarnings(
|
||||
lot: LotView,
|
||||
dose: number | null,
|
||||
colonyCount: number,
|
||||
treatmentDay: string,
|
||||
): string[] {
|
||||
const warnings: string[] = [];
|
||||
if (lot.expiresOn && treatmentDay && lot.expiresOn < treatmentDay) {
|
||||
warnings.push(`Die Packung ist abgelaufen (verw. bis ${monthYear(lot.expiresOn)}).`);
|
||||
}
|
||||
if (dose !== null && Number.isFinite(dose) && dose > 0) {
|
||||
const needed = roundToThousandths(dose * Math.max(colonyCount, 1));
|
||||
if (needed > lot.remaining) {
|
||||
warnings.push(
|
||||
`Die Packung reicht nicht: Rest ${formatQuantity(lot.remaining, lot.unit)}, gebraucht ${formatQuantity(needed, lot.unit)}.`,
|
||||
);
|
||||
}
|
||||
} else if (lot.remaining <= 0) {
|
||||
warnings.push("Die Packung ist leer.");
|
||||
}
|
||||
return warnings;
|
||||
}
|
||||
Reference in new issue
Block a user