Einlagern-UX: 3-Wege-Barcode, Inline-Anlage aus OFF, Mehr-Chargen mit MHD

Einlagern:
- Barcode-Erkennung dreiteilig: bekannt -> direkt Menge; unbekannt aber in OFF
  -> "Anlegen & einlagern" inline; gar nicht gefunden -> manuell anlegen.
- Mehrere Chargen pro Vorgang: je Zeile eigene Menge + eigenes MHD
  (z.B. 5 Glaeser mit unterschiedlichen Daten). Neuer Endpoint /stock/checkin/batch.

Produkte/OFF:
- OFF-Fuellmenge (quantity) wird in Basiseinheit + Packungsgroesse geparst
  (kg->g, l->ml, cl/dl); parse_quantity + Tests.
- Produktformular uebernimmt Barcode aus ?barcode= und schlaegt automatisch nach,
  fuellt Packungsgroesse; gemeinsame offUtils (guessGroup/suggestionToProduct).

Lagerorte:
- Baum jetzt rekursiv ueber beliebig viele Ebenen (Schrank->Fach->Kiste->...).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-22 10:40:54 +02:00
parent b683f2d93b
commit 4c17705290
10 changed files with 368 additions and 105 deletions

View File

@@ -245,6 +245,22 @@ input::placeholder { color: var(--muted); opacity: 0.7; }
.selected-product > .info { flex: 1; min-width: 0; }
.selected-product .title { font-weight: 620; }
/* ---------- OFF suggestion ---------- */
.suggestion {
display: flex; align-items: center; gap: var(--sp-3);
margin-top: var(--sp-3); padding: var(--sp-3);
background: var(--accent-soft); border: 1px solid var(--border); border-radius: var(--radius-sm);
}
.suggestion > .info { flex: 1; min-width: 0; }
.suggestion .title { font-weight: 620; }
/* ---------- Check-in lines ---------- */
.lines { margin-top: var(--sp-4); border-top: 1px solid var(--border); padding-top: var(--sp-3); }
.lines-head { display: flex; align-items: baseline; justify-content: space-between; margin-bottom: var(--sp-2); font-weight: 600; font-size: 0.85rem; }
.line { display: flex; gap: var(--sp-2); align-items: flex-end; margin-bottom: var(--sp-2); }
.line .btn-icon { margin-bottom: 1px; }
.line-label { display: block; font-size: 0.72rem; color: var(--muted); font-weight: 580; margin-bottom: 3px; }
/* ---------- Stat tiles ---------- */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--sp-3); margin-bottom: var(--sp-5); }
.stat {