Final commit.
This commit is contained in:
210
lib/domain/repository/tour_repository.dart
Normal file
210
lib/domain/repository/tour_repository.dart
Normal file
@ -0,0 +1,210 @@
|
||||
import 'package:hl_lieferservice/domain/entity/delivery.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/delivery_credit.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/delivery_note.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/delivery_service_value.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/scan_intent.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/tour.dart';
|
||||
import 'package:hl_lieferservice/domain/entity/tour_details.dart';
|
||||
|
||||
/// Port für das Tour-Aggregat.
|
||||
///
|
||||
/// Der Port deckt in dieser Migrations-Phase nur die Read-Seite + die
|
||||
/// beiden Operationen, die zum Loading-Flow zwingend gebraucht werden:
|
||||
/// Sortierung und Fahrzeug-Zuweisung. Hold/Resume/Cancel/Complete und
|
||||
/// Notizen werden in C+D-4 nachgezogen, damit das hier nicht überladen
|
||||
/// wird und der Bloc fokussiert bleibt.
|
||||
///
|
||||
/// Account-Filter sitzt serverseitig im JWT — der Client schickt nie eine
|
||||
/// `personalnummer`/`accountId` mit.
|
||||
abstract interface class TourRepository {
|
||||
/// Die heutige Tour-Übersicht des angemeldeten Fahrers oder `null`,
|
||||
/// wenn keine Tour für heute angelegt ist (ERP-Sync noch nicht
|
||||
/// gelaufen, Treiber-Urlaub etc.).
|
||||
///
|
||||
/// Liefert nur die schlanke `TourSummary`-Repräsentation;
|
||||
/// [getTourDetails] zieht dann den vollen Aggregat-Snapshot.
|
||||
Future<TourSummary?> getMyTourSummaryOfToday();
|
||||
|
||||
/// Lädt das volle Tour-Aggregat (Tour + Lieferungen + Items +
|
||||
/// Stammdaten + Notizen) für die gegebene Tour-Id.
|
||||
Future<TourDetails> getTourDetails(String tourId);
|
||||
|
||||
/// Convenience: kombiniert [getMyTourSummaryOfToday] + [getTourDetails]
|
||||
/// und gibt `null` zurück, wenn keine Tour existiert. Verwendet die App
|
||||
/// beim Initial-Load.
|
||||
Future<TourDetails?> getMyTourDetailsOfToday();
|
||||
|
||||
/// Schreibt die Sortier-Reihenfolge der Lieferungen einer Tour neu.
|
||||
///
|
||||
/// `orderedDeliveryIds` muss **alle** Lieferungen der Tour enthalten,
|
||||
/// in der gewünschten Reihenfolge — das Backend lehnt unvollständige
|
||||
/// Listen mit `400 validation` ab.
|
||||
///
|
||||
/// Rückgabe: deliveryId → neuer sortOrder (für den Bloc-Reducer).
|
||||
Future<Map<String, int>> setDeliveryOrder({
|
||||
required String tourId,
|
||||
required List<String> orderedDeliveryIds,
|
||||
});
|
||||
|
||||
/// Weist einer Lieferung ein Fahrzeug zu. `carId == null` löst die
|
||||
/// bestehende Zuweisung. Der Server gibt die aktualisierte Delivery
|
||||
/// zurück; weil dieser Endpoint nur Stamm-Felder mutiert, ist es Aufgabe
|
||||
/// des Aufrufers, die `items` aus dem lokalen Aggregat zu erhalten.
|
||||
///
|
||||
/// Rückgabe: die Stamm-Delivery **ohne** Items — Aufrufer nutzt
|
||||
/// `copyWith(items: ...)` zum Mergen mit dem lokalen State.
|
||||
Future<Delivery> assignCarToDelivery({
|
||||
required String deliveryId,
|
||||
required String? carId,
|
||||
});
|
||||
|
||||
/// Bricht eine Lieferung ab — endgültig (`canceled`). `reason` ist
|
||||
/// vom Backend Pflicht; leere Begründungen werden mit 400 abgelehnt.
|
||||
/// Rückgabe: Server-Snapshot der Delivery **ohne** Items.
|
||||
Future<Delivery> cancelDelivery({
|
||||
required String deliveryId,
|
||||
required String reason,
|
||||
});
|
||||
|
||||
/// Pausiert eine Lieferung (`held`). Reversibel über [resumeDelivery].
|
||||
/// `reason` ist Pflicht.
|
||||
Future<Delivery> holdDelivery({
|
||||
required String deliveryId,
|
||||
required String reason,
|
||||
});
|
||||
|
||||
/// Setzt eine pausierte Lieferung auf `active` zurück. Kein Reason
|
||||
/// erforderlich.
|
||||
Future<Delivery> resumeDelivery({required String deliveryId});
|
||||
|
||||
/// Schließt eine Lieferung ab (`completed`). Lädt beide Unterschriften
|
||||
/// (Kunde + Fahrer, PNG) per multipart hoch und dokumentiert die
|
||||
/// Bestätigungen des Kunden. Atomar serverseitig — das Backend prüft
|
||||
/// vorher: Lieferung aktiv, alle scanbaren Positionen fertig, Notizen
|
||||
/// bestätigt (falls vorhanden). [paymentMethodId] persistiert die ggf. im
|
||||
/// Summary geänderte Zahlungsmethode (muss existieren + aktiv sein); `null`
|
||||
/// lässt die am Beleg hinterlegte Methode unangetastet. Rückgabe:
|
||||
/// Server-Snapshot der Delivery **ohne** Items (Aufrufer merged Items aus
|
||||
/// dem lokalen Aggregat).
|
||||
Future<Delivery> completeDelivery({
|
||||
required String deliveryId,
|
||||
required List<int> customerSignaturePng,
|
||||
required List<int> driverSignaturePng,
|
||||
required bool receiptConfirmed,
|
||||
required bool notesAcknowledged,
|
||||
required List<String> acknowledgedNoteIds,
|
||||
String? paymentMethodId,
|
||||
String? actorCarId,
|
||||
bool paymentCollected = false,
|
||||
});
|
||||
|
||||
/// Legt eine neue Notiz an einer Lieferung an.
|
||||
///
|
||||
/// Mindestens eines von [text] und [imageAttachment] muss inhaltlich
|
||||
/// gefüllt sein — das Backend erzwingt das. Aktuell unterstützt die App
|
||||
/// nur den Text-Pfad; das `imageAttachment`-Feld bleibt der zukünftigen
|
||||
/// Foto-Upload-Phase vorbehalten.
|
||||
///
|
||||
/// Rückgabe: die neu angelegte Notiz (mit Server-gesetzter `id` und
|
||||
/// `createdAt`) — der Aufrufer hängt sie an das lokale Tour-Aggregat.
|
||||
Future<DeliveryNote> addDeliveryNote({
|
||||
required String deliveryId,
|
||||
String? text,
|
||||
String? imageAttachment,
|
||||
String? creditDeliveryItemId,
|
||||
bool isAmountCreditNote,
|
||||
});
|
||||
|
||||
/// Ändert Text/Bild einer bestehenden Notiz. Mindestens eines von [text]
|
||||
/// und [imageAttachment] muss inhaltlich gefüllt sein. Rückgabe: die
|
||||
/// aktualisierte Notiz (Autor/`createdAt` bleiben).
|
||||
Future<DeliveryNote> updateDeliveryNote({
|
||||
required String deliveryId,
|
||||
required String noteId,
|
||||
String? text,
|
||||
String? imageAttachment,
|
||||
});
|
||||
|
||||
/// Löscht eine Notiz. Innerhalb des (geteilten) Accounts darf jeder Fahrer
|
||||
/// löschen — keine Autor-Prüfung serverseitig.
|
||||
Future<void> deleteDeliveryNote({
|
||||
required String deliveryId,
|
||||
required String noteId,
|
||||
});
|
||||
|
||||
/// Lädt ein Bild zu einer Lieferung hoch (multipart, Feld `file`). Das
|
||||
/// Backend reicht es an DOCUframe weiter und legt eine Bild-Notiz mit der
|
||||
/// zurückgelieferten Referenz (`~ObjectID`) als `imageAttachment` an.
|
||||
/// Rückgabe: die neue Notiz.
|
||||
Future<DeliveryNote> uploadDeliveryNoteImage({
|
||||
required String deliveryId,
|
||||
required String filename,
|
||||
required String mime,
|
||||
required List<int> bytes,
|
||||
});
|
||||
|
||||
/// Setzt/ändert die Betrags-Gutschrift einer Lieferung. Append-only +
|
||||
/// idempotent über [clientEventId]. Rückgabe: aktueller Stand (`null`, wenn
|
||||
/// — theoretisch — nichts gesetzt ist).
|
||||
Future<DeliveryCredit?> setDeliveryCredit({
|
||||
required String deliveryId,
|
||||
required String clientEventId,
|
||||
required int amountCents,
|
||||
required String reason,
|
||||
String? actorCarId,
|
||||
});
|
||||
|
||||
/// Entfernt die Betrags-Gutschrift einer Lieferung (append-only `remove`).
|
||||
/// Rückgabe: aktueller Stand danach (`null`).
|
||||
Future<DeliveryCredit?> removeDeliveryCredit({
|
||||
required String deliveryId,
|
||||
required String clientEventId,
|
||||
String? actorCarId,
|
||||
});
|
||||
|
||||
/// Setzt (Upsert) den Wert eines Service für eine Lieferung. Genau das zum
|
||||
/// Service-Typ passende Feld angeben. Rückgabe: der gespeicherte Wert.
|
||||
Future<DeliveryServiceValue> setDeliveryService({
|
||||
required String deliveryId,
|
||||
required String serviceId,
|
||||
bool? boolValue,
|
||||
int? numericValue,
|
||||
String? actorCarId,
|
||||
});
|
||||
|
||||
/// Entfernt den Service-Wert einer Lieferung (Service „nicht gesetzt").
|
||||
Future<void> removeDeliveryService({
|
||||
required String deliveryId,
|
||||
required String serviceId,
|
||||
});
|
||||
|
||||
/// Wendet eine Liste Scan-Ereignisse als Batch am Server an.
|
||||
///
|
||||
/// Der Endpoint ist bewusst Bulk: damit kann der Client einen
|
||||
/// Scanner-Burst (z. B. 5 Barcodes in 2 Sekunden) in einem HTTP-Call
|
||||
/// abschicken, **muss** aber nicht — auch ein Aufruf mit nur einem
|
||||
/// `ScanIntent` ist erlaubt.
|
||||
///
|
||||
/// Idempotenz: das Backend speichert pro `clientScanId` einmal. Wer
|
||||
/// retried, bekommt `duplicate` zurück; doppelte Anwendung kann es
|
||||
/// nicht geben.
|
||||
///
|
||||
/// Rückgabe: pro Eingabe-Intent ein [ScanOutcome] (Key =
|
||||
/// `clientScanId`). Die Map enthält **jeden** Intent, auch
|
||||
/// `rejected`-Fälle; bei Netzwerk-/Server-Fehlern wirft das Repository
|
||||
/// stattdessen [TourRepositoryException], die Map ist dann nicht
|
||||
/// teilweise gefüllt.
|
||||
Future<Map<String, ScanOutcome>> applyScans(List<ScanIntent> intents);
|
||||
}
|
||||
|
||||
/// Allgemeine Repository-Exception für Tour-Operationen. Konkrete Impls
|
||||
/// dürfen spezifischere Subtypen werfen.
|
||||
class TourRepositoryException implements Exception {
|
||||
const TourRepositoryException(this.message, [this.cause]);
|
||||
|
||||
final String message;
|
||||
final Object? cause;
|
||||
|
||||
@override
|
||||
String toString() => 'TourRepositoryException: $message';
|
||||
}
|
||||
Reference in New Issue
Block a user