Files
Holzleitner-Lieferservice-App/lib/feature/delivery/bloc/tour_event.dart
Dennis Nemec a9bf8ecdd1 Final commit.
2026-06-01 17:12:28 +02:00

342 lines
10 KiB
Dart

/// Events für den [`TourBloc`].
///
/// Bewusst minimaler Scope für Phase C+D-2: Lese-Pfad, Reorder,
/// Car-Assign. Scan-, Hold-, Cancel-, Complete-, Discount- und
/// Notiz-Events kommen in C+D-3 / C+D-4 zurück.
sealed class TourEvent {
const TourEvent();
}
/// Initial-Load der heutigen Tour des angemeldeten Fahrers.
/// Account-Filter sitzt im JWT — kein zusätzliches Argument nötig.
class LoadTour extends TourEvent {
const LoadTour();
}
/// Pull-to-refresh / manueller Reload aus der Übersicht. Vorhandener
/// `TourLoaded`-State bleibt sichtbar, bis der neue Snapshot da ist;
/// `TourLoading` wird nur emittiert, wenn vorher kein State existierte.
class RefreshTour extends TourEvent {
const RefreshTour();
}
/// Schreibt die Sortier-Reihenfolge aller Lieferungen einer Tour an das
/// Backend. Der Bloc fordert die UI nicht auf, die vollständige Reihenfolge
/// zu kennen — sie wird aus der Liste übergeben.
class ReorderDeliveries extends TourEvent {
const ReorderDeliveries({required this.orderedDeliveryIds});
final List<String> orderedDeliveryIds;
}
/// Weist einer Lieferung ein Fahrzeug zu — oder hebt die Zuweisung auf
/// (`carId == null`).
class AssignCarToDelivery extends TourEvent {
const AssignCarToDelivery({required this.deliveryId, required this.carId});
final String deliveryId;
final String? carId;
}
/// Bulk-Variante: weist mehreren Lieferungen in einer atomaren Aktion das
/// gleiche Fahrzeug zu. Notwendig, weil flutter_bloc Events standardmäßig
/// **concurrent** verarbeitet — N parallel laufende `AssignCarToDelivery`-
/// Handler lesen alle den Initial-State und überschreiben sich gegenseitig
/// beim `emit`, sodass nur die letzte Zuweisung sichtbar bleibt. Der
/// Bulk-Handler verarbeitet die Liste sequenziell und emittiert genau
/// einen Zwischen-State am Ende.
///
/// Backend-seitig macht der Handler trotzdem N einzelne HTTP-Calls, weil
/// der `PATCH /deliveries/{id}/assigned-car`-Endpoint keine Liste kennt.
class AssignCarToDeliveries extends TourEvent {
const AssignCarToDeliveries({
required this.deliveryIds,
required this.carId,
});
final List<String> deliveryIds;
final String? carId;
}
/// Scan-Trigger aus der Loading-Phase. Der Bloc inkrementiert lokal
/// **optimistisch** die Scan-Quantität, ruft anschließend `POST /scans`
/// und rollt im `rejected`-Fall lokal zurück. `duplicate` ist still
/// (lokal schon hochgezählt, Server bestätigt nicht erneut).
///
/// `actorCarId` ist die UUID des aktuell gewählten Fahrzeugs — das
/// Backend nutzt das nur als Audit-Spur; an der Lieferung selbst
/// passiert dadurch nichts.
class ScanItem extends TourEvent {
const ScanItem({
required this.deliveryItemId,
required this.actorCarId,
this.manual = false,
});
final String deliveryItemId;
final String actorCarId;
/// `true` = manuelle Zeilen-Bestätigung (Fallback ohne Barcode): die
/// **gesamte Restmenge** wird auf einmal als geladen verbucht und im
/// Backend als `manual` protokolliert. `false` = regulärer Einzel-Scan (+1).
final bool manual;
}
/// Umkehrung eines Scans (z. B. „falsch gescannt"). `reason` ist vom
/// Backend für `unscan` erforderlich.
class UnscanItem extends TourEvent {
const UnscanItem({
required this.deliveryItemId,
required this.actorCarId,
required this.reason,
});
final String deliveryItemId;
final String actorCarId;
final String reason;
}
/// Pausiert ein Item (`scan_status=held`). Reversibel über [UnholdItem].
/// `reason` Pflicht.
class HoldItem extends TourEvent {
const HoldItem({
required this.deliveryItemId,
required this.actorCarId,
required this.reason,
});
final String deliveryItemId;
final String actorCarId;
final String reason;
}
/// Setzt ein pausiertes Item zurück auf `in_progress`. Kein Reason
/// nötig — der Server löscht beim Unhold den `held_reason`.
class UnholdItem extends TourEvent {
const UnholdItem({
required this.deliveryItemId,
required this.actorCarId,
});
final String deliveryItemId;
final String actorCarId;
}
/// Entfernt ein Item aus der Lieferung (`scan_status=removed`).
/// `reason` Pflicht. Reversibel über [UnremoveItem] — die Historie
/// bleibt im Audit-Log vollständig erhalten.
class RemoveItem extends TourEvent {
const RemoveItem({
required this.deliveryItemId,
required this.actorCarId,
required this.reason,
this.quantity,
this.saveReasonAsNote = false,
});
final String deliveryItemId;
final String actorCarId;
final String reason;
/// Mengen-Gutschrift: wie viele Stück gutgeschrieben werden. `null` =
/// ganze Restmenge (= klassisches „ganze Zeile entfernen").
final int? quantity;
/// Wenn `true`, wird der `reason` nach erfolgreichem Entfernen zusätzlich
/// als Lieferungs-Notiz gespeichert. Gesetzt aus dem Gutschrift-Flow
/// (Step „Artikel & Gutschriften"); im Beladen-Flow `false`, dort wäre
/// eine Notiz pro Abbuchung nur Rauschen.
final bool saveReasonAsNote;
}
/// Stellt ein entferntes Item wieder her — geht nur, wenn der aktuelle
/// Status `removed` ist. Kein Reason nötig (Backend-Konvention wie bei
/// `unscan` und `unhold`). Audit-Log behält den ursprünglichen
/// `remove`-Eintrag und fügt einen neuen `unremove`-Eintrag hinzu.
class UnremoveItem extends TourEvent {
const UnremoveItem({
required this.deliveryItemId,
required this.actorCarId,
this.quantity,
});
final String deliveryItemId;
final String actorCarId;
/// Mengen-Gutschrift zurücknehmen: wie viele Stück wiederhergestellt
/// werden. `null` = gesamte Gutschrift zurück.
final int? quantity;
}
// ─── Delivery-Lifecycle ───────────────────────────────────────────────
/// Bricht eine Lieferung endgültig ab. `reason` ist Pflicht.
class CancelDelivery extends TourEvent {
const CancelDelivery({required this.deliveryId, required this.reason});
final String deliveryId;
final String reason;
}
/// Pausiert eine Lieferung. `reason` ist Pflicht. Reversibel über
/// [ResumeDelivery].
class HoldDelivery extends TourEvent {
const HoldDelivery({required this.deliveryId, required this.reason});
final String deliveryId;
final String reason;
}
/// Setzt eine pausierte Lieferung zurück auf `active`.
class ResumeDelivery extends TourEvent {
const ResumeDelivery({required this.deliveryId});
final String deliveryId;
}
/// Schließt eine Lieferung ab: lädt beide Unterschriften hoch, dokumentiert
/// die Bestätigungen des Kunden und setzt die Lieferung auf `completed`.
class CompleteDelivery extends TourEvent {
const CompleteDelivery({
required this.deliveryId,
required this.customerSignaturePng,
required this.driverSignaturePng,
required this.receiptConfirmed,
required this.notesAcknowledged,
required this.acknowledgedNoteIds,
this.paymentMethodId,
this.actorCarId,
this.paymentCollected = false,
});
final String deliveryId;
final List<int> customerSignaturePng;
final List<int> driverSignaturePng;
final bool receiptConfirmed;
final bool notesAcknowledged;
final List<String> acknowledgedNoteIds;
/// Vom Fahrer im Summary gewählte Zahlungsmethode (Override). `null` =
/// die am Beleg hinterlegte Methode bleibt. Wird beim Abschluss persistiert.
final String? paymentMethodId;
final String? actorCarId;
/// Fahrer hat das Vor-Ort-Inkasso (Bar/EC) des offenen Betrags bestätigt.
/// `false`, wenn kein Inkasso anfiel (offen == 0 oder „Auf Rechnung").
final bool paymentCollected;
}
/// Legt eine neue (Text- oder Bild-)Notiz an einer Lieferung an. Aktuell
/// wird nur der Text-Pfad von der UI getriggert; `imageAttachment` ist als
/// Storage-Key (z. B. Pre-Signed-URL-Key) gedacht und wartet auf die
/// zukünftige Foto-Upload-Phase.
/// Setzt/ändert die Betrags-Gutschrift einer Lieferung (Geld-Nachlass).
class SetDeliveryCredit extends TourEvent {
const SetDeliveryCredit({
required this.deliveryId,
required this.amountCents,
required this.reason,
required this.actorCarId,
});
final String deliveryId;
final int amountCents;
final String reason;
final String actorCarId;
}
/// Entfernt die Betrags-Gutschrift einer Lieferung.
class RemoveDeliveryCredit extends TourEvent {
const RemoveDeliveryCredit({
required this.deliveryId,
required this.actorCarId,
});
final String deliveryId;
final String actorCarId;
}
/// Setzt/ändert den Wert eines Service für eine Lieferung (Checkbox/Zahl).
class SetDeliveryServiceValue extends TourEvent {
const SetDeliveryServiceValue({
required this.deliveryId,
required this.serviceId,
required this.actorCarId,
this.boolValue,
this.numericValue,
});
final String deliveryId;
final String serviceId;
final String actorCarId;
final bool? boolValue;
final int? numericValue;
}
/// Entfernt den Service-Wert einer Lieferung (Service „nicht gesetzt").
class RemoveDeliveryServiceValue extends TourEvent {
const RemoveDeliveryServiceValue({
required this.deliveryId,
required this.serviceId,
});
final String deliveryId;
final String serviceId;
}
class AddDeliveryNote extends TourEvent {
const AddDeliveryNote({
required this.deliveryId,
this.text,
this.imageAttachment,
this.creditDeliveryItemId,
});
final String deliveryId;
final String? text;
final String? imageAttachment;
/// Optionaler Gutschrift-Bezug (DeliveryItem-Id). Gesetzt, wenn die Notiz
/// einen Gutschrift-Grund dokumentiert — ermöglicht das Löschen beim
/// Unremove.
final String? creditDeliveryItemId;
}
/// Ändert Text/Bild einer bestehenden Notiz.
class UpdateDeliveryNote extends TourEvent {
const UpdateDeliveryNote({
required this.deliveryId,
required this.noteId,
this.text,
this.imageAttachment,
});
final String deliveryId;
final String noteId;
final String? text;
final String? imageAttachment;
}
/// Löscht eine Notiz.
class DeleteDeliveryNote extends TourEvent {
const DeleteDeliveryNote({required this.deliveryId, required this.noteId});
final String deliveryId;
final String noteId;
}
/// Lädt ein Bild als Notiz hoch (geht über DOCUframe).
class UploadDeliveryNoteImage extends TourEvent {
const UploadDeliveryNoteImage({
required this.deliveryId,
required this.filename,
required this.mime,
required this.bytes,
});
final String deliveryId;
final String filename;
final String mime;
final List<int> bytes;
}