fix(labels): eindeutige Platzhalter-Namen, echte Beispiele, Hinweis bei doppeltem Inhalt
Backend E2E / e2e (push) Canceled after 0s
Backend Image / build-and-push (push) Successful in 16m7s
Frontend Image / build-and-push (push) Successful in 42m6s

- Die Werte waren richtig verdrahtet; irreführend waren die Namen:
  „Chargennummer“ (Honigcharge) und „Los-/Chargennummer der Abfüllung“
  lasen sich gleich. Jetzt mit Herkunft im Namen, z. B. „Chargennummer der
  Honigcharge (nicht Los)“, „Los-Zeile („L“ + Los-Nr.)“, „MHD (nur Datum)“,
  und einem Beispiel in Druckform unter „Inhalt“.
- Beispielwerte entsprechen dem Druck und sind alle verschieden.
- Etiketten-Prüfung: neue Warnung DUPLICATE_TEXT, wenn zwei Felder den
  gleichen Text drucken oder Nummer und Zeile nebeneinander stehen (nicht
  blockierend, abwärtskompatibel).

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick.RosenloecherandClaude Opus 5.5 committed 2026-10-19 19:39:12 +02:00
1 parent d2581abbd4
commit b59be8e201
9 files changed
+245 -29

No files matched your search

+1 -1
View File
@@ -828,7 +828,7 @@ Für `MEDICINE_LOT`, `AUTHORITY_NOTICE` und `APIARY_RESTRICTION` gilt zusätzlic
### 12.4 Etikett-Pflichtangaben
`checkLabel` (`src/label-templates/label-particulars.ts`) ist rein und ohne Datenbank; Regeln und Grenzen: Annahmen 65 bis 68. **Pflichtangaben** (sperren den Druck: 409 `LABEL_PARTICULARS_MISSING` mit `{ gaps }`, Abschnitt 12.6): `LEGAL_NAME` (ein Wort mit „honig“), `NET_QUANTITY` (Füllmenge der Abfüllung, „250,5 g“ wie „250.5 g“, „1 kg“ nur bei vollen Kilogramm; als ganze Wörter), `BEST_BEFORE` („Mindestens haltbar bis“ mit dem Datum), `LOT`, `ORIGIN` und `FOOD_BUSINESS` (Name und Anschrift). **Warnungen** (sperren nie): `LOT_PREFIX`, `X_HEIGHT` (unter `LabelTemplate.minXHeightMm`), `NET_QUANTITY_HEIGHT` (Ziffernhöhe unter der Regel aus `ComplianceSettings.netQuantityDigitRules`), `TEXT_CLIPPED`. Das Ergebnis trägt `requirements` (`minXHeightMm`, `netQuantityMinCapHeightMm`) und je Befund `elementIds` (bei Lücken die abgeschnittenen Elemente, die die Angabe getragen hätten). **Verfeinerungen (Runden 2 bis 4 der Task 5).**
`checkLabel` (`src/label-templates/label-particulars.ts`) ist rein und ohne Datenbank; Regeln und Grenzen: Annahmen 65 bis 68. **Pflichtangaben** (sperren den Druck: 409 `LABEL_PARTICULARS_MISSING` mit `{ gaps }`, Abschnitt 12.6): `LEGAL_NAME` (ein Wort mit „honig“), `NET_QUANTITY` (Füllmenge der Abfüllung, „250,5 g“ wie „250.5 g“, „1 kg“ nur bei vollen Kilogramm; als ganze Wörter), `BEST_BEFORE` („Mindestens haltbar bis“ mit dem Datum), `LOT`, `ORIGIN` und `FOOD_BUSINESS` (Name und Anschrift). **Warnungen** (sperren nie): `LOT_PREFIX`, `X_HEIGHT` (unter `LabelTemplate.minXHeightMm`), `NET_QUANTITY_HEIGHT` (Ziffernhöhe unter der Regel aus `ComplianceSettings.netQuantityDigitRules`), `TEXT_CLIPPED`, `DUPLICATE_TEXT` (zwei Felder drucken denselben Inhalt, sobald ein Platzhalter beteiligt ist, oder `lotNumber` neben `lotLine` bzw. `bestBeforeDate` neben `bestBeforeLine`; gleicher fester Text zählt nicht). Das Ergebnis trägt `requirements` (`minXHeightMm`, `netQuantityMinCapHeightMm`) und je Befund `elementIds` (bei Lücken die abgeschnittenen Elemente, die die Angabe getragen hätten). **Verfeinerungen (Runden 2 bis 4 der Task 5).**
- **Ziffern-Wächter je Element.** „Zahl direkt vor der Menge“ („1,250 g“ ist kein Treffer für 250 g) gilt nur im Text eines Elements, nie im verbundenen Text: Eine Losnummer oder ein Datum in einem Nachbarelement macht „500 g“ nicht ungültig. „250g“ gilt als „250 g“, „L2026“ bleibt.
- **Lesereihenfolge** kommt allein aus den Positionen (nach y, Zeilen mit 1 mm Toleranz gegen die erste Oberkante, je Zeile von links nach rechts), nie aus der Reihenfolge der Elemente in der Vorlage.
- **Verteilte Angaben** (Name und Anschrift): Zeilen, Blöcke in Lesereihenfolge oder Ketten wortgleicher Elemente, jeweils mit **symmetrischem** senkrechtem Abstand von höchstens 10 mm (`max(0, y − Unterkante, Oberkante − (y + h))`, gemessen an der laufenden Ober- und Unterkante der ganzen Gruppe, nicht am Vorgänger, `groupByDistance`). Elemente mit leerem normalisiertem Text werden ignoriert und überbrücken nichts; eine hohe Textbox überbrückt Zeilen um ihre Boxhöhe. Nur sichtbare (nicht abgeschnittene) Teile zählen (Annahme 75).