Files
Holzleitner-Lieferservice-App/lib/domain/entity/scan_intent.dart
Dennis Nemec a9bf8ecdd1 Final commit.
2026-06-01 17:12:28 +02:00

94 lines
3.3 KiB
Dart

/// Vom Treiber ausgelöstes Scan-Ereignis, bevor es serverseitig
/// angewendet wurde.
///
/// `clientScanId` ist ein vom Client generierter UUID-Schlüssel und dient
/// als **Idempotenz-Anker**: der Server speichert ihn beim ersten Apply
/// und antwortet auf jeden weiteren Request mit derselben Id mit
/// `duplicate` statt einer zweiten Anwendung. So bleibt Network-Retry
/// (z. B. nach Verbindungsabbruch beim ersten POST) bedeutungslos.
///
/// `clientScannedAt` ist die Wall-Clock-Zeit am Gerät zum Zeitpunkt des
/// Scans — der Server nutzt das nur als Audit-Spur, sortiert aber selbst
/// nach Server-Empfangszeit, sodass eine schiefe Uhr am Phone die
/// Reihenfolge nicht durcheinanderbringt.
class ScanIntent {
const ScanIntent({
required this.clientScanId,
required this.clientScannedAt,
required this.deliveryItemId,
required this.action,
this.actorCarId,
this.reason,
this.quantity,
this.manual = false,
});
final String clientScanId;
final DateTime clientScannedAt;
final String deliveryItemId;
final ScanAction action;
/// `true`, wenn der Fahrer die Position manuell als geladen bestätigt hat
/// (Fallback ohne Barcode). Reine Audit-Information; Default `false`.
final bool manual;
/// Menge für `remove` / `unremove` (Mengen-Gutschrift): wie viele Stück
/// der Belegzeile gutgeschrieben bzw. wiederhergestellt werden. `null` =
/// ganze Restmenge. Bei `scan`/`unscan`/`hold`/`unhold` ignoriert.
final int? quantity;
/// Fahrzeug, mit dem gescannt wurde — Audit-Spur. Optional, aber die
/// App schickt ihn in der Loading-Phase immer mit, weil das Auto zu
/// dem Zeitpunkt definitiv gewählt ist.
final String? actorCarId;
/// Klartext-Begründung. Bei `unscan` / `hold` / `remove` vom Backend
/// erwartet, bei `scan` / `unhold` ignoriert.
final String? reason;
}
/// Auswirkung eines Scan-Ereignisses auf die Pipeline eines Items.
/// Spiegel des Backend-Enums `AuditAction`.
///
/// `unremove` ist die Umkehrung von `remove`: setzt ein `Removed`-Item
/// zurück auf `InProgress` (oder `Done`, falls die Soll-Menge schon
/// erreicht war). Der ursprüngliche `remove`-Audit-Eintrag bleibt
/// erhalten — `unremove` erzeugt einen eigenen Eintrag, sodass die
/// Historie der Korrektur vollständig nachvollziehbar bleibt.
enum ScanAction { scan, unscan, hold, unhold, remove, unremove }
/// Ergebnis eines Apply-Versuchs vom Server.
class ScanOutcome {
const ScanOutcome({
required this.clientScanId,
required this.status,
this.deliveryItemId,
this.reason,
});
final String clientScanId;
final ScanOutcomeStatus status;
/// Bei `applied` und `duplicate` immer gesetzt, bei `rejected` häufig
/// `null` (z. B. wenn die Id beim Server gar nicht ankam).
final String? deliveryItemId;
/// Bei `rejected` die Server-Begründung — Standard-Text in der UI.
final String? reason;
}
enum ScanOutcomeStatus {
/// Server hat den Scan angewendet — `scannedQuantity` ist hochgezählt
/// oder Status hat sich geändert.
applied,
/// Server hat denselben `clientScanId` schon einmal verarbeitet —
/// kein Effekt, aber auch kein Fehler.
duplicate,
/// Server hat den Scan abgelehnt (z. B. Item gehört zu fremder
/// Lieferung, Soll-Menge schon voll, Item ist auf `removed`). UI muss
/// optimistische Mutation zurückrollen.
rejected,
}