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>
- 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>
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>
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.
OpenAPI-Generator-Setup:
- tool/generate_api_client.sh: Direkter Aufruf der openapi-generator-cli.jar
(Java-CLI statt Dart-build_runner-Integration — vermeidet die
analyzer-/source_gen-Version-Hölle mit json_serializable)
- tool/fetch_openapi_generator.sh: lädt die JAR (29 MB) nach (gitignored)
- openapi/holzleitner.json: Snapshot der Backend-Spec für reproduzierbare
Generation
- packages/holzleitner_api/: generiertes Dart-Sub-Package (built_value +
dio), per path-dep im Haupt-pubspec eingehängt
Netzwerk-Layer (lib/data/network/):
- BackendConfig: API- und Keycloak-Endpoints für Local-Dev (localhost
wegen Keycloak-iss-Claim).
- AuthTokenProvider-Schnittstelle.
- DevPasswordGrantTokenProvider: Phase-A-Provider via Keycloak
password-grant, Token-Caching mit Expiry-Check (Phase B ersetzt das
durch flutter_appauth PKCE).
- HolzleitnerAuthInterceptor: dynamischer Bearer-Inject pro Request.
- HolzleitnerApiFactory: baut die generierte HolzleitnerApi-Klasse
mit unserem Interceptor statt der vier Default-Auth-Interceptors.
- network_locator.registerNetworking(): get_it-Setup, in main() vor
runApp() aufgerufen.
Clean-Arch-Scaffolding (lib/data/, lib/domain/):
- Verzeichnisstruktur für Phase C+D angelegt (mapper/, repository/,
entity/, repository/) — befüllt sich in den Folge-Phasen.
Smoke-Test:
- tool/smoke_test_api.dart ruft /health (ungeschützt) und /me/cars
(mit Bearer) via generiertem Client — grün gegen lokales Backend.