MHD nur mit Monat und Jahr in Web-UI und iOS-App

Setzt den Backend-Umbau in beiden Oberflaechen um.

Die Genauigkeit gilt jeweils fuer den ganzen Einlager-Vorgang und nicht je
Charge: Auf einer Packung steht entweder ein Tagesdatum oder nur Monat/Jahr,
gemischt kommt das nicht vor. Vorbelegt wird sie aus dem Produkt, laesst sich
aber im Vorgang umstellen.

Web-UI: Umschalter im Kopf der Chargenliste; das Eingabefeld wechselt zwischen
type="date" und type="month". Im Produktformular gibt es das neue Feld
"MHD-Angabe" neben der Gebinde-Bezeichnung. Anzeige laeuft ueber den
Settings-Context, damit das eingestellte Datumsformat erhalten bleibt und
Monatsangaben ueberall als "09/2026" erscheinen (Auslagern, Uebersicht,
Chargentabelle). Die Abgelaufen-Warnung prueft bei Monatsangaben gegen den
Monatsletzten - sonst haette eine Packung schon am Monatsersten als abgelaufen
gegolten.

iOS: Auswahl "Tagesdatum / nur Monat/Jahr" im Einlagern-Formular und beim
Anlegen eines Artikels. SwiftUI hat keinen DatePicker ohne Tag, deshalb ein
eigener MonthYearPicker aus zwei Auswahlfeldern; die Jahresliste reicht zwei
Jahre zurueck (bereits abgelaufene Ware) und fuenfzehn nach vorn (Konserven).
Die Chargenauswahl beim Auslagern zeigt Monatsangaben ebenfalls ohne Tag.

Getestet: Web-Build (vite) und iOS-Geraetebuild laufen fehlerfrei durch, die
App ist auf dem iPhone installiert. Das Verhalten in der Oberflaeche ist noch
nicht von Hand durchgeklickt.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-22 17:57:09 +02:00
parent eed7c9e1de
commit 30307d6cfb
13 changed files with 2090 additions and 25 deletions

View File

@@ -81,6 +81,38 @@ export function formatDate(iso, format = "de") {
}
}
// ---- MHD-Genauigkeit ----
// Intern steht immer ein volles Datum (bei Monatsangaben der Monatsletzte).
// Nur die Anzeige und das Eingabefeld richten sich nach der Genauigkeit.
/** MHD anzeigen: Monatsangaben ohne Tag, also "09/2026". */
export function formatBestBefore(iso, precision, format = "de") {
if (!iso) return "";
if (precision !== "month") return formatDate(iso, format);
const date = new Date(`${iso}T00:00:00`);
if (Number.isNaN(date.getTime())) return iso;
return `${String(date.getMonth() + 1).padStart(2, "0")}/${date.getFullYear()}`;
}
/** "2026-09-30" -> "2026-09" für <input type="month">. */
export function toMonthInput(iso) {
return iso ? iso.slice(0, 7) : "";
}
/** "2026-09" -> "2026-09-01"; das Backend legt daraus den Monatsletzten. */
export function fromMonthInput(value) {
return value ? `${value}-01` : "";
}
/** "2026-09" -> "2026-09-30". Fuer Ablauf-Warnungen: ein Monats-MHD laeuft
* erst am Monatsende ab, nicht am Monatsanfang. */
export function monthInputToLastDay(value) {
if (!value) return "";
const [year, month] = value.split("-").map(Number);
if (!year || !month) return "";
return new Date(Date.UTC(year, month, 0)).toISOString().slice(0, 10);
}
// MHD lesbar als Zeitspanne: "in 3 Tagen", "in 1 Woche", "vor 2 Wochen".
// Deutsch nach "in"/"vor" steht im Dativ: 1 Tag / 3 Tagen, 1 Woche / 2 Wochen.
export function relativeExpiry(daysLeft) {