import 'scan_progress.dart'; /// Eine Belegzeile innerhalb einer Lieferung. /// /// Verweist über `articleId` auf den Artikel-Stamm (lookup via /// `TourDetails.articleById`) und über `warehouseId` auf das Lager. Die /// Soll-/Ist-Quantitäten leben hier: `requiredQuantity` ist statisch (ERP), /// `scanProgress.scannedQuantity` wandert mit jedem Scan nach oben. /// /// `komponentenArtikelNr` markiert Stücklisten-Komponenten. Im neuen /// Backend gibt es **keine** Parent-/Child-Hierarchie mehr — jedes Item ist /// gleichrangig; das Feld dient nur noch der Anzeige ("Teil von X") und /// hat keinerlei Scan-Semantik. class DeliveryItem { const DeliveryItem({ required this.id, required this.deliveryId, required this.articleId, required this.warehouseId, required this.belegzeilenNr, required this.requiredQuantity, required this.scanProgress, this.unitPrice = 0, this.komponentenArtikelNr, this.parentArtikelNr, }); final String id; final String deliveryId; final String articleId; final String warehouseId; /// ERP-Belegzeilen-Nummer. Bestimmt die Reihenfolge der Items in der /// Detail-Ansicht (aufsteigend). final int belegzeilenNr; final int requiredQuantity; final ScanProgress scanProgress; /// Stückpreis (brutto, EUR) aus dem ERP-Sync. final double unitPrice; final String? komponentenArtikelNr; /// Artikelnummer des Oberartikels, zu dem diese Komponente gehört (aus dem /// Sync). `null` bei Oberartikeln/regulären Zeilen. Die Liste rückt /// Komponenten unter ihrem Oberartikel ein. final String? parentArtikelNr; /// `true`, wenn dieses Item eine Stücklisten-Komponente ist (gehört unter /// einen Oberartikel). bool get isComponent => parentArtikelNr != null; // ─── Abgeleitete Sicht-Eigenschaften ────────────────────────────────── /// Tatsächlich auszuliefernde Menge = Soll − Gutschrift. Nie negativ. int get deliveredQuantity { final d = requiredQuantity - scanProgress.creditedQuantity; return d < 0 ? 0 : d; } /// Wert der ausgelieferten Menge dieser Position (brutto, EUR). double get lineTotal => unitPrice * deliveredQuantity; /// Vollständig gescannt (Status `done` oder Ist ≥ Soll). bool get isDone => scanProgress.status == ScanStatus.done || scanProgress.scannedQuantity >= requiredQuantity; /// Aktuell pausiert. bool get isHeld => scanProgress.status == ScanStatus.held; /// Nach dem Laden wieder entfernt. bool get isRemoved => scanProgress.status == ScanStatus.removed; /// Noch offene Restmenge (für Loading-UI). Nicht negativ. int get remainingQuantity { final remaining = requiredQuantity - scanProgress.scannedQuantity; return remaining < 0 ? 0 : remaining; } DeliveryItem copyWith({ String? id, String? deliveryId, String? articleId, String? warehouseId, int? belegzeilenNr, int? requiredQuantity, ScanProgress? scanProgress, double? unitPrice, String? komponentenArtikelNr, String? parentArtikelNr, }) { return DeliveryItem( id: id ?? this.id, deliveryId: deliveryId ?? this.deliveryId, articleId: articleId ?? this.articleId, warehouseId: warehouseId ?? this.warehouseId, belegzeilenNr: belegzeilenNr ?? this.belegzeilenNr, requiredQuantity: requiredQuantity ?? this.requiredQuantity, scanProgress: scanProgress ?? this.scanProgress, unitPrice: unitPrice ?? this.unitPrice, komponentenArtikelNr: komponentenArtikelNr ?? this.komponentenArtikelNr, parentArtikelNr: parentArtikelNr ?? this.parentArtikelNr, ); } }