Jede Kontaktquelle zeigt ihre Anschrift (inkl. Zusatz, Land nur im
Ausland), sofern das ERP eine pflegt. Die Anschrift zählt beim
Zusammenführen gleicher Quellen mit — gleicher Name an anderer Adresse
bleibt eine eigene Karte. API-Client neu generiert.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Address.addition aus dem Backend (ERP-Freitext, praktisch Ortsteil,
Etage oder Hinweis); API-Client neu generiert.
- Listen (Sortieren, Beladen, Liefer-Übersicht): Adresse mit Zusatz in
Klammern, z. B. „… 41472 Neuss (Holzheim)".
- Lieferdetails: „Zusatz: …" unter der Anschrift von Empfänger und
Besteller; Land nur außerhalb Deutschlands.
- Maps-Suche weiter ohne Zusatz (sonst stört „3 Etage" die Suche).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Belege können im ERP eine eigene Lieferadresse (LieferAdressId) mit
anderer Person tragen als der Besteller. Die App zeigt jetzt überall den
Empfänger vor Ort:
- TourDetails.recipientOf liefert Empfänger (Name aus der Lieferadresse,
sonst Besteller), Lieferanschrift und differsFromOrderer.
- Sortieren, Auswählen, Beladen und Liefer-Übersicht: Empfängername +
Lieferadresse, bei Abweichung „Bestellt von: <Kunde>".
- Lieferdetails: hervorgehobene Karte „Abweichende Lieferadresse" mit
Empfänger, Anschrift und Kontakt; Kunden-Block als „Besteller (Kunde)"
mit Adresse, Kundennummer und Kontaktdaten.
- Maps navigiert zur Lieferadresse statt zur Kundenadresse; AppBar,
Unterschrift und Beladen-Avatar nutzen den Empfänger.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der Fertig-Button der Filial-Abholung ersetzt die Scan-Page jetzt durch
DeliveryDetail (pushReplacement) statt zur Übersicht zurückzukehren.
Zurück aus der Lieferung führt weiterhin zur Übersicht.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Schalter stehen in assets/feature_flags.json (Key -> enabled +
Beschreibung) statt als Konstanten im Code
- Enum Feature buendelt die stabilen Keys; FeatureFlags liest die Datei,
ignoriert unbekannte Keys und nutzt fuer fehlende den Default aus dem Enum
- Laden beim App-Start im AppBloc, bereitgestellt per RepositoryProvider;
FeatureGate blendet Bereiche bei inaktivem Feature komplett aus
- articles.credit_section = false: Abschnitt "Gutschriften" im
Artikel-Step ist ausgeblendet
- Bisheriges Stepper-Flag migriert (articles.credit_amount_stepper); die
fuenf ungenutzten statischen Flags mit veralteten Kommentaren entfernt
- Tests: Asset enthaelt alle Keys, Parsing, Defaults, FeatureGate
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Nach "Abschliessen" oeffnet sich ein Blatt "Interne Notiz" (optional,
max. 2000 Zeichen). Ohne Eingabe genuegt ein Tipp auf "Ohne Notiz
abschliessen"; "Zurueck zur Unterschrift" bricht nur das Abschliessen ab
- Die Notiz reist im Abschluss-Aufruf mit (internalNote) und landet im
Lieferbericht, nicht in den Kunden-Notizen
- API-Client aus aktueller Backend-Spec neu generiert
- Widget-Tests fuer das Blatt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Neuer Workflow-Step "Zahlung" zeigt ausgelieferte Artikel mit Menge und
Preis, Betragsaufstellung, Zahlungsstatus und einen grossen Button
"Zahlung abwickeln" (fest unten)
- Zahlungs-Modal: grosser offener Betrag, Methoden als Cards aus den
Backend-Stammdaten, bei EC-Karte gross die Kundennummer; schliesst nur
ueber Abbrechen, "Zahlung erhalten" oder "Lieferung abbrechen" (mit Grund)
- Bestaetigung wird am Server protokolliert (RecordDeliveryPayment); bis
dahin sind "Weiter" und die Uebersicht gesperrt, ausser es ist nichts
offen (bereits bezahlt). Aendert sich der Betrag danach, wird die Zahlung
als veraltet markiert und muss erneut abgewickelt werden
- Uebersicht zeigt die abgewickelte Zahlung statt eigener Methodenauswahl;
Unterschrift-Flow ohne doppelte Inkasso-Stufe; Abschluss sendet Methode
und Inkasso-Flag der gueltigen Zahlung
- DeliveryPaymentStatus als einzige Quelle fuer offenen Betrag
(gleiche Formel wie Backend); Artikelliste/Betragskarte als geteilte
Widgets; Begruendungs-Dialog geteilt mit Info-Step
- API-Client aus aktueller Backend-Spec neu generiert
- Tests fuer Zahlungsstatus und Modal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Inventur aller Lese-Requests (Tour, Fahrzeuge, Zahlungsmethoden,
Foto-Vorschauen) und aller Stellen, an denen ihr Fehlerzustand gerendert wird.
Geschlossene Luecken:
- Sortieren-Seite: TourLoadFailed fiel in den Lade-Zweig -> Endlos-Spinner
ohne Ausweg. Jetzt Fehlerseite mit 'Erneut versuchen' im Header-Rahmen
- Home: scheiterte beim Start CarLoad oder LoadTour, wurde die Phase nie
geladen -> Endlos-Spinner. Jetzt Fehlerseite mit Retry (+ Drawer)
- Beladen-/Auswaehlen-Seite: Fehlerseite stand ohne Scaffold/Header/Drawer
da; jetzt im selben Rahmen wie die uebrigen Zustaende
- Hintergrund-Reload (refreshError): Snackbar jetzt MIT Retry-Aktion und
zentral im PhaseStepper-Header -> gilt in allen Phasen (vorher nur
Ausliefern, ohne Retry; auf Sortieren/Beladen blieb der Fehler stumm)
- AttachmentImage: Fehleranzeige ist antippbar ('Erneut laden')
Zahlungsmethoden haben den Retry bereits aus dem vorigen Commit.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Kunde meldete haeufig 'Sitzung abgelaufen' und danach dauerhaft rotes
'Sitzung abgelaufen' bei den Zahlungsmethoden trotz erfolgreichem Login.
Token-Refresh:
- _performRefresh wertete JEDEN Fehler als tote Session (Logout + Refresh-
Token geloescht) - auch Mobilfunk-/VPN-Aussetzer. Jetzt gilt nur noch eine
echte Ablehnung durch Keycloak (OAuth invalid_grant o. ae.) als abgelaufen;
voruebergehende Fehler behalten die Session, nutzen den noch gueltigen
Access-Token weiter oder werfen AuthTemporarilyUnavailableException
- Interceptor schickt Requests nicht mehr tokenlos weiter (ergab 401 ->
irrefuehrendes 'Sitzung abgelaufen'), sondern bricht als Verbindungsfehler ab
- restoreSession verwirft den Refresh-Token bei fehlendem Netz nicht mehr
Zahlungsmethoden:
- Cubit lud nur einmal beim App-Start (vor dem Login -> 401) und blieb danach
fuer immer im Fehlerzustand. Jetzt Reload bei jedem Wechsel auf Authenticated
- Fehlerkarte in der Uebersicht hat einen 'Erneut laden'-Button
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
An einem Tag ohne Tour konnte man in der AppBar durch alle Stepper-Schritte
navigieren. Ursache: an leeren Tagen gilt der GETEILTE 'no-tour'-Persistenz-
Eimer; einmal dort aufgeschlossener Fortschritt (hohes Max) machte an JEDEM
leeren Tag Beladen/Ausliefern anklickbar.
- Stepper: ohne geladene Tour (TourEmpty/loading) ist maxReached hart der
erste sichtbare Schritt -> weitere Schritte nicht anklickbar, keine Haekchen
- PhaseBloc._load: ohne Tour ausschliesslich Eintrittsphase, Persistenz wird
weder gelesen noch geschrieben (kein Fortschritt im 'no-tour'-Eimer)
- PhaseBloc._set: ohne Tour jeden Vorwaerts-Wechsel verwerfen, nichts
persistieren - unabhaengig vom ausloesenden Button/Tap
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nach einem Tour-Wechsel (Tageswechsel im Kopf-Kalender, Resync) blieben bis
zum asynchronen Nachladen aus den SharedPreferences noch Phase und Haekchen
(maxReached) der vorherigen Tour sichtbar; zudem konnte ein persistiertes
Max uebernommen werden, obwohl fuer die neue Tour-Version gar keine Phase
persistiert war.
- Bei Tour-Wechsel eines bereits geladenen Autos den In-Memory-Stand SOFORT
synchron auf die Eintrittsphase setzen (nur Sortieren aktiv, keine
Haekchen); ein persistierter Stand der neuen Tour folgt per PhaseLoaded
- persistedMax nur uebernehmen, wenn fuer diese Tour-Version auch eine Phase
persistiert ist (frische Tour-Version startet immer ohne ererbte Haekchen)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Beim Datumswechsel im Kopf-Kalender blieben Beladen/Ausliefern des alten
Tags 'erledigt': PhaseBloc._load brach bei bereits geladenem Auto ab und
Home stiess den Load nur einmal pro Auto an - der In-Memory-Stand wurde
beim Tour-/Token-Wechsel nie invalidiert. Die Persistenz ist bereits pro
Tour-Token (syncedAt) getrennt, nur das Nachladen fehlte.
- PhaseBloc: Guard tokenbewusst (_loadedTokenByCar); anderer Token laedt den
persistierten Stand der neuen Tour nach (neuer Tag -> Eintrittsphase,
Rueckkehr -> alter Stand wiederhergestellt)
- Home._ensurePhaseLoaded: Guard auf carId|tourToken statt nur carId
- Token-Ableitung zentral als TourState.phaseToken (tour_state.dart), genutzt
von tourTokenResolver (app.dart) UND Home - eine Quelle statt Duplikat
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der Header-Kalender zeigte nach Neustart/Retry noch 'morgen', geladen wurde
aber 'heute' (leer). Ursache: Initial-Load und diverse Retry-Buttons
dispatchten LoadTour() ohne Datum -> immer heute.
- LoadTour(): reiner Reload, behaelt das gemerkte Datum
- LoadTour.forDate(date): setzt gezielt ein Datum (Kalender + Initial-Load)
- Initial-Load in home.dart liest das Datum aus dem TourDateCubit
- Retry-Buttons (Fail-/Sort-/Loading-Seite) laden dadurch den angezeigten Tag
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neue Kalender-Pille im Phase-Stepper: zeigt das Tour-Datum und oeffnet einen
Datepicker, um die Tour eines anderen Tages zu laden (z. B. morgen vorab).
Fahrzeugwechsel bleibt ueber den Drawer erreichbar.
- TourDateCubit haelt das gewaehlte Datum (null = heute, Server autoritativ)
- LoadTour({date}) + TourBloc merkt das Datum (RefreshTour laedt denselben Tag)
- Repository getMyTourDetails({date}) -> API listMyTours(date:) -> GET /me/tours?date=
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Nach dem Anzeige-/Zuordnungs-Fix erschien eine neu gesyncte, noch NICHT
beladene Lieferung sofort in der Auslieferung — obwohl im Standardlager
nichts geladen wurde. Sie gehört aber noch in die Beladen-Phase.
Auslieferung zeigt aktive Lieferungen jetzt nur, wenn ihre Standardlager-
Beladung fertig ist (standardWarehouseLoadingDone). Pausiert/fertig/
abgebrochen (post-Beladung) bleiben unverändert sichtbar; unbeladene
erscheinen weiter in Beladen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Die Auslieferungs-Übersicht filterte immer nach assignedCarId==selectedCarId,
während die Beladen-Übersicht bei Single-Car-Teams ALLE Lieferungen zeigt.
Folge: eine neu importierte, noch nicht zugeordnete Lieferung
(assignedCarId == null) erschien in Beladen (und ließ sich scannen), aber
nach dem Scannen NICHT in der Auslieferung.
Auslieferung nutzt jetzt dieselbe multiCarTeam-Logik (CarsBloc,
cars.length >= 2): Single-Car → alle Lieferungen, Multi-Car → nach
assignedCarId filtern.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ein Set kann mehrfach bestellt sein (Oberartikel-Menge = Set-Anzahl),
daher bleibt der Mengen-Stepper beim Entfernen über den Oberartikel
erhalten. Die gewählte Set-Anzahl kaskadiert PROPORTIONAL auf die
Komponenten (Stückzahl je Set × entfernte Sets, geklemmt auf Restmenge)
— funktioniert für 1:1-Mengen wie für Komponenten mit Stückzahl je Set.
Einzelne Komponenten bleiben weiterhin nicht direkt entfernbar.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A) Nach erfolgreichem Abschluss (aktiv→completed) poppt die Detail-Page
automatisch zurück zur Übersicht. Scaffold ist jetzt StatefulWidget
mit BlocListener<TourBloc>; nur „gearmt", wenn die Lieferung beim
Öffnen aktiv war → erneutes Öffnen einer fertigen Lieferung poppt nicht.
B) Step „Info": Artikelliste zeigt weiter die Ursprungsmenge
(requiredQuantity). Bei entfernten/teilweise gutgeschriebenen Positionen
erscheint pro Zeile ein „Menge geändert"-Hinweis + ein tappbares Banner,
das zu Step 3 „Artikel" springt.
C) Beladen: nicht-scanbare Set-Köpfe (Parent-Komponenten) werden jetzt
IMMER mit ihrem Set gezeigt — als Kopf in der Lagergruppe ihrer
Komponenten statt isoliert unter „Dienstleistungen". _ItemRow leitet
scanNotRequired aus der Artikel-Scanbarkeit ab.
D) Step „Übersicht": Wording der Zahlungsweise-Sperre bei offen==0
präzisiert („Keine Zahlung mehr offen (bereits bezahlt)").
E) Step „Artikel": Komponenten eines Sets sind einzeln nicht mehr
entfernbar (kein Button + Hinweis). Das Entfernen/Wiederherstellen läuft
nur über den Oberartikel und kaskadiert auf das ganze Set (ganz oder
gar nix). Set-Entfernen ist blockiert, solange eine Komponente noch
nicht verladen ist.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Steht kein offener Betrag mehr aus (vollständig vorab bezahlt oder per
Gutschrift ausgeglichen), wird die Zahlungsmethoden-Auswahl gesperrt und
ein erklärender Hinweis angezeigt — analog zur Sperre bei bereits
abgeschlossener Lieferung.
- Offener-Betrag-Formel in Helper _openAmount(delivery, credit)
extrahiert (Single Source; vorher nur in _PaymentSummary).
- _PaymentMethodPicker bekommt die Gutschrift und sperrt das Dropdown
bei state != active ODER offen == 0 (editable = active && offen > 0).
- Sperr-/Info-Hinweis in wiederverwendbares _PickerHint-Widget gezogen.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Auch der Lade-Zwischenzustand (state is! TourLoaded, u.a. waehrend 'Neu laden')
zeigt jetzt die PhaseStepper-AppBar statt der schlichten AppBar -> die Leiste
springt beim Reload nicht mehr kurz auf den Theme-Default zurueck.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Der TourEmpty-Zweig der Sortieren-Seite rendert jetzt den PhaseStepper
(Steps, theme.primaryColor) als AppBar — die Phasen-Navigation bleibt sichtbar
und die Optik ist primary statt der Theme-Default-AppBar. Die mittige
'Keine Lieferungen heute'-Nachricht (inkl. 'Neu laden') bleibt unveraendert.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TourEmpty-Leerzustand der Sortieren-Seite ('Keine Lieferungen heute') bekommt
einen 'Neu laden'-Button (LoadTour) — analog zum Beladen-Empty-State.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clean-Arch-Schichten für Cars:
- lib/domain/entity/car.dart: UUID-id, accountId (Personalnummer),
plate, active. Pendant zum Backend-Schema.
- lib/domain/repository/cars_repository.dart: Port — listMine,
create, update. Keine teamId/personalnummer-Parameter, der
Account fließt serverseitig aus dem JWT.
- lib/data/mapper/car_mapper.dart: API-DTO (built_value) → Domain.
- lib/data/repository/cars_repository_impl.dart: konkrete Impl via
generierter CarsApi (dio), mit DioException → CarsRepositoryException-
Übersetzung.
Feature-Cars-Refactoring:
- CarsBloc nimmt jetzt die Domain-Repository-Schnittstelle. Events:
CarLoad/CarAdd/CarEdit/CarDeactivate (statt CarDelete). Keine
teamId-Parameter mehr. Kein authBloc-Bezug, Session-Expiry läuft
über den globalen Provider-Stream.
- CarsState sealed mit CarsInitial/Loading/LoadingFailed/Loaded.
- Pages: car_management_page, car_management, car_card, car_fail_page,
car_selection_page komplett auf die neue Entity und Event-Signaturen.
- Alte lib/feature/cars/service/cars_service.dart und
lib/feature/cars/repository/cars_repository.dart gelöscht.
CarSelectBloc + Storage:
- CarSelection.selectedCarId von int? auf String? umgestellt.
- CarSelectionRepository persistiert die UUID jetzt als String;
defensive Migration für noch vorhandene int-Werte (alte
Pre-Migration-Installations) verwirft den Wert leise und
erzwingt Neuauswahl.
Konsequenz-Cleanup im Tour-Code (Phase-D-Vorbereitung):
- Delivery.carId String? statt int?.
- Tour.hasUndeliveredLoadedArticles / getFinishedDeliveries auf
String carId.
- _selectedCarId / int? carId / int selectedCarId in DeliveryOverview,
LoadingCustomerPage/OverviewPage, Home, DeliverySelection/SortPage,
DeliveryInfo/List, CustomSortDialog, SortableDeliveryList auf
String umgestellt.
- TourRepository ersetzt int.parse(carId)/int.tryParse-Zuweisungen
direkt durch String.
- lib/model/car.dart wird zum Re-Export der neuen Domain-Entity,
damit Legacy-Imports während Phase-D-Übergang weiter compilieren.
DI:
- app.dart: CarsBloc bekommt CarsRepositoryImpl(locator<HolzleitnerApi>())
statt der alten CarsRepository(service: CarService()).
Build (flutter build apk --debug) durch, flutter analyze ohne
errors.
UI-Restructuring:
- TabBar in scan_page durch dedizierte Phasen ersetzt: Sortieren / Beladen / Ausliefern
- PhaseBloc + PhaseService leiten Phase aus Tour-/Item-States ab
- DeliverySelectionPage (ab 2 Autos) und DeliverySortPage als eigene Flows
- LoadingOverviewPage / LoadingCustomerPage für die Beladephase
- PhaseStepper-Widget im Home für Phasen-Anzeige
- Lager-Differenzierung (Standardlager 0 vs. Außenlager) via WarehouseBadge
Process-Stubs:
- ProcessRepository für Hold/Cancel/Sort/Assign-Flows (stub, bereit für Backend-Anbindung)
Doku:
- docs/BACKEND_MIGRATION.md: Phasenplan für Umstellung auf das neue
Rust-Backend (OpenAPI-Generator, Keycloak OIDC, Clean-Arch-Layering)