Final commit.

This commit is contained in:
Dennis Nemec
2026-06-01 17:12:28 +02:00
parent 3ecbc82885
commit a9bf8ecdd1
385 changed files with 29081 additions and 12089 deletions

View File

@ -0,0 +1,106 @@
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,
);
}
}