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.
134 lines
3.3 KiB
Dart
134 lines
3.3 KiB
Dart
import 'package:hl_lieferservice/dto/payment.dart';
|
|
|
|
import 'car.dart';
|
|
import 'delivery.dart';
|
|
|
|
class Payment {
|
|
const Payment({
|
|
required this.description,
|
|
required this.shortcode,
|
|
required this.id,
|
|
});
|
|
|
|
final String id;
|
|
final String description;
|
|
final String shortcode;
|
|
|
|
factory Payment.fromDTO(PaymentMethodDTO dto) {
|
|
return Payment(
|
|
description: dto.description,
|
|
shortcode: dto.shortCode,
|
|
id: dto.id,
|
|
);
|
|
}
|
|
|
|
Payment copyWith({
|
|
String? description,
|
|
String? shortcode,
|
|
String? id,
|
|
}) {
|
|
return Payment(
|
|
description: description ?? this.description,
|
|
shortcode: shortcode ?? this.shortcode,
|
|
id: id ?? this.id,
|
|
);
|
|
}
|
|
}
|
|
|
|
class Tour {
|
|
Tour({
|
|
required this.date,
|
|
required this.deliveries,
|
|
required this.driver,
|
|
required this.discountArticleNumber,
|
|
required this.paymentMethods,
|
|
}) : deliveriesPerCar = {};
|
|
|
|
final DateTime date;
|
|
final String discountArticleNumber;
|
|
Driver driver;
|
|
final List<Delivery> deliveries;
|
|
List<Payment> paymentMethods;
|
|
|
|
Map<String, List<Delivery>> deliveriesPerCar;
|
|
|
|
int getFinishedDeliveries(String carId) {
|
|
return deliveries
|
|
.where((delivery) => delivery.carId == carId)
|
|
.where((delivery) => delivery.state == DeliveryState.finished)
|
|
.toList()
|
|
.length;
|
|
}
|
|
|
|
/// Returns true if the car still has loaded articles assigned to a delivery
|
|
/// that has not been finished yet. Scannable articles count when their
|
|
/// effective scanned amount (scanned minus removed) is positive; non-scannable
|
|
/// articles count when their target amount is greater than zero.
|
|
bool hasUndeliveredLoadedArticles(String carId) {
|
|
return deliveries.any((delivery) {
|
|
if (delivery.carId != carId) return false;
|
|
if (delivery.state == DeliveryState.finished) return false;
|
|
return delivery.articles.any((article) {
|
|
if (article.scannable) {
|
|
return article.scannedAmount > article.scannedRemovedAmount;
|
|
}
|
|
return article.amount > 0;
|
|
});
|
|
});
|
|
}
|
|
|
|
Tour copyWith({
|
|
DateTime? date,
|
|
String? discountArticleNumber,
|
|
Driver? driver,
|
|
List<Delivery>? deliveries,
|
|
List<Payment>? paymentMethods,
|
|
}) {
|
|
return Tour(
|
|
date: date ?? this.date.copyWith(),
|
|
discountArticleNumber:
|
|
discountArticleNumber ?? this.discountArticleNumber,
|
|
driver: driver ?? this.driver,
|
|
deliveries: deliveries ?? this.deliveries,
|
|
paymentMethods: paymentMethods ?? this.paymentMethods,
|
|
);
|
|
}
|
|
}
|
|
|
|
class Driver {
|
|
Driver({
|
|
required this.teamNumber,
|
|
required this.name,
|
|
required this.salutation,
|
|
required this.cars,
|
|
});
|
|
|
|
final int teamNumber;
|
|
final String name;
|
|
final String salutation;
|
|
List<Car> cars;
|
|
|
|
/// If the driver is representing a company, then the company name is returned.
|
|
String getSalutatedLastName() {
|
|
if (salutation != "Firma") {
|
|
return "$salutation, ${name.split(" ").first}";
|
|
}
|
|
|
|
return "$salutation, $name";
|
|
}
|
|
|
|
Driver copyWith(
|
|
int? teamNumber,
|
|
String? name,
|
|
String? salutation,
|
|
List<Car>? cars,
|
|
) {
|
|
return Driver(
|
|
teamNumber: teamNumber ?? this.teamNumber,
|
|
name: name ?? this.name,
|
|
salutation: salutation ?? this.salutation,
|
|
cars: cars ?? this.cars,
|
|
);
|
|
}
|
|
}
|