Files
HHA/app/lib/presentation/blocs/scan/scan_bloc.dart
2026-03-24 15:03:35 +01:00

917 lines
28 KiB
Dart

import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:equatable/equatable.dart';
import '../../../domain/entities/logistic_object.dart';
import '../../../domain/entities/tour.dart';
import '../../../domain/repositories/tour_repository.dart';
import '../../../core/constants/app_constants.dart';
import '../../../core/errors/failures.dart';
part 'scan_event.dart';
part 'scan_state.dart';
class ScanBloc extends Bloc<ScanEvent, ScanState> {
final TourRepository repository;
Tour? currentTour;
String? currentPageId;
String? scannedFsaId;
// Container handling for VS state machine
String? containerId;
String? containerType; // 'a' or 'b'
ScanBloc({required this.repository}) : super(ScanInitial()) {
on<InitializeScan>(_onInitializeScan);
on<ProcessBarcode>(_onProcessBarcode);
on<ValidateBarcode>(_onValidateBarcode);
on<UpdateObjectState>(_onUpdateObjectState);
on<ResetScan>(_onResetScan);
on<CreateUnknownObject>(_onCreateUnknownObject);
}
Future<void> _onInitializeScan(InitializeScan event, Emitter<ScanState> emit) async {
currentTour = event.tour;
containerId = null;
containerType = null;
emit(ScanReady(tour: event.tour));
}
Future<void> _onProcessBarcode(ProcessBarcode event, Emitter<ScanState> emit) async {
emit(ScanProcessing(barcode: event.barcode));
final barcode = event.barcode.trim();
// Prüfe auf spezielle Barcodes (Seiten-Codes)
if (currentTour != null) {
final pageInfo = _findPageForBarcode(barcode);
if (pageInfo != null) {
emit(ScanPageDetected(
pageId: pageInfo['pageId']!,
label: pageInfo['label']!,
tour: currentTour!,
));
return;
}
}
// Suche Objekt nach Barcode
final result = await repository.getObjectByBarcode(barcode);
result.fold(
(failure) => emit(ScanError(message: _mapFailureToMessage(failure))),
(object) {
if (object != null) {
_processScannedObject(object, barcode, emit);
} else {
// Unbekanntes Objekt - prüfe auf gültiges Präfix
final prefix = barcode.length >= 3 ? barcode.substring(0, 3) : '';
if (_isValidPrefix(prefix)) {
emit(ScanUnknownObject(
barcode: barcode,
prefix: prefix,
tour: currentTour,
));
} else {
emit(ScanError(message: 'Unbekannter Barcode: $barcode'));
}
}
},
);
}
void _processScannedObject(LogisticObject object, String barcode, Emitter<ScanState> emit) {
if (currentTour == null) {
emit(const ScanError(message: 'Keine Tour ausgewählt'));
return;
}
final tourType = currentTour!.type;
// Dispatch to appropriate state machine based on tour type
switch (tourType) {
case TourTypes.stockStart:
_stockStartStateMachine(object, emit);
break;
case TourTypes.vehStart:
_vehStartStateMachine(object, emit);
break;
case TourTypes.vehBulk:
_vehBulkStateMachine(object, emit);
break;
case TourTypes.veh:
_vehStateMachine(object, emit);
break;
case TourTypes.fsa:
_fsaStateMachine(object, emit);
break;
case TourTypes.vs:
_vsStateMachine(object, emit);
break;
case TourTypes.vehVs:
_vehVsStateMachine(object, emit);
break;
case TourTypes.gi:
_giStateMachine(object, emit);
break;
case TourTypes.vehEnd:
_vehEndStateMachine(object, emit);
break;
case TourTypes.stockEnd:
_stockEndStateMachine(object, emit);
break;
case TourTypes.stock:
_stockStateMachine(object, emit);
break;
default:
// Fallback to simple state machine for unknown tour types
final nextState = _determineNextState(object);
emit(ScanObjectDetected(
object: object,
suggestedState: nextState,
tour: currentTour,
));
}
}
// ============================================================================
// STATE MACHINE: stockStart (Lager Beladung)
// ============================================================================
void _stockStartStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final currentState = object.state;
if (currentState == ObjectStates.unknown) {
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.toDelivery,
tour: currentTour,
));
} else if (currentState == ObjectStates.finGITmp) {
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retGI,
tour: currentTour,
));
} else {
emit(ScanError(
message: 'Fehler: Ungültiger Barcode für Lager Beladung',
));
}
}
// ============================================================================
// STATE MACHINE: vehStart (Fahrzeug Start)
// ============================================================================
void _vehStartStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final currentState = object.state;
if (currentState == ObjectStates.toDelivery) {
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.delivery,
tour: currentTour,
));
} else if (currentState == ObjectStates.retGI) {
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retGIFzg,
tour: currentTour,
));
} else {
emit(const ScanError(message: 'Fehler: Ungültiger Barcode'));
}
}
// ============================================================================
// STATE MACHINE: vehBulk (Fahrzeug Bulk)
// ============================================================================
void _vehBulkStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final objectType = object.type;
final currentState = object.state;
// Type 9 = special bulk handling
if (objectType == 9) {
// Bulk update all objects with state to_delivery
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.delivery,
tour: currentTour,
));
} else {
if (currentState == ObjectStates.toDelivery) {
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.delivery,
tour: currentTour,
));
} else if (currentState == ObjectStates.retGI) {
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retGIFzg,
tour: currentTour,
));
} else {
emit(const ScanError(message: 'Fehler: Ungültiger Barcode'));
}
}
}
// ============================================================================
// STATE MACHINE: veh (Fahrzeug - Stationen)
// ============================================================================
void _vehStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final currentState = object.state;
switch (currentState) {
case ObjectStates.delivery:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.station,
tour: currentTour,
));
break;
case ObjectStates.retFail:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retFailFzg,
tour: currentTour,
));
break;
case ObjectStates.retGI:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retGIFzg,
tour: currentTour,
));
break;
case ObjectStates.retDS:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retDSFzg,
tour: currentTour,
));
break;
case ObjectStates.station:
// Reverse transition: back to delivery
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.delivery,
tour: currentTour,
));
break;
case ObjectStates.inFA:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.hdl,
tour: currentTour,
));
break;
default:
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
// ============================================================================
// STATE MACHINE: fsa (Fahrscheinautomat)
// ============================================================================
void _fsaStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final objectType = object.type;
final currentState = object.state;
final subtype = object.subtype.toLowerCase();
// GK = Geldkassette (type 1)
if (_isTypeGK(objectType, subtype)) {
_fsaGKStateMachine(object, currentState, emit);
}
// HP = Hauptkasse/Druckerpatronen (type 2)
else if (_isTypeHP(objectType, subtype)) {
_fsaHPStateMachine(object, currentState, emit);
}
// FR = Fahrkartenrolle (type 5)
else if (_isTypeFR(objectType, subtype)) {
_fsaFRStateMachine(object, currentState, emit);
}
else {
emit(const ScanError(message: 'Fehler: Falscher Typ für FSA'));
}
}
void _fsaGKStateMachine(LogisticObject object, String currentState, Emitter<ScanState> emit) {
switch (currentState) {
case ObjectStates.station:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.inFA,
tour: currentTour,
));
break;
case ObjectStates.inFA:
// Special handling: Fehlkassette logic
emit(ScanFehlKassetteDetected(
object: object,
suggestedState: ObjectStates.retFail,
tour: currentTour,
));
break;
case ObjectStates.unknown:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retGI,
tour: currentTour,
originBarcode: object.code,
));
break;
default:
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
void _fsaHPStateMachine(LogisticObject object, String currentState, Emitter<ScanState> emit) {
switch (currentState) {
case ObjectStates.station:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.inFA,
tour: currentTour,
));
break;
case ObjectStates.inFA:
// Bidirectional: back to station
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.station,
tour: currentTour,
));
break;
case ObjectStates.unknown:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retDS,
tour: currentTour,
originBarcode: object.code,
));
break;
default:
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
void _fsaFRStateMachine(LogisticObject object, String currentState, Emitter<ScanState> emit) {
switch (currentState) {
case ObjectStates.station:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.inFA,
tour: currentTour,
));
break;
case ObjectStates.inFA:
// Bidirectional: back to station
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.station,
tour: currentTour,
));
break;
default:
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
// ============================================================================
// STATE MACHINE: vs (Versorgungsstelle)
// ============================================================================
void _vsStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final objectType = object.type;
final currentState = object.state;
final subtype = object.subtype.toLowerCase();
// SB = Safebag
if (_isTypeSB(objectType, subtype)) {
_vsSBStateMachine(object, currentState, emit);
}
// ABS = Abfallbehälter
else if (_isTypeABS(objectType, subtype)) {
_vsABSStateMachine(object, currentState, emit);
}
// CNTR = Container
else if (_isTypeCNTR(objectType, subtype)) {
_vsCNTRStateMachine(object, currentState, subtype, emit);
}
else {
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
void _vsSBStateMachine(LogisticObject object, String currentState, Emitter<ScanState> emit) {
if (containerId == null) {
emit(const ScanError(message: 'Fehler: Falscher Zustand - Container nicht ausgewählt'));
return;
}
if (containerType == 'a') {
if (currentState == ObjectStates.inVS) {
emit(ScanContainerObjectDetected(
object: object,
suggestedState: ObjectStates.retGI,
tour: currentTour,
containerId: containerId!,
containerType: containerType!,
));
} else {
emit(const ScanError(message: 'Fehler: Falscher Zustand'));
}
} else {
emit(const ScanError(message: 'Fehler: Falscher Zustand'));
}
}
void _vsABSStateMachine(LogisticObject object, String currentState, Emitter<ScanState> emit) {
if (containerId == null) {
emit(const ScanError(message: 'Fehler: Falscher Zustand - Container nicht ausgewählt'));
return;
}
if (containerType == 'a') {
emit(const ScanError(message: 'Fehler: Falscher Zustand'));
} else {
if (currentState == ObjectStates.inVS) {
emit(ScanContainerObjectDetected(
object: object,
suggestedState: ObjectStates.retDS,
tour: currentTour,
containerId: containerId!,
containerType: containerType!,
));
} else {
emit(const ScanError(message: 'Fehler: Falscher Zustand'));
}
}
}
void _vsCNTRStateMachine(LogisticObject object, String currentState, String subtype, Emitter<ScanState> emit) {
containerId = object.code;
if (subtype == 'cntra') {
containerType = 'a';
emit(ScanContainerDetected(
object: object,
suggestedState: ObjectStates.retcGI,
tour: currentTour,
containerId: containerId!,
containerType: containerType!,
));
} else if (subtype == 'cntrb') {
containerType = 'b';
emit(ScanContainerDetected(
object: object,
suggestedState: ObjectStates.retcDS,
tour: currentTour,
containerId: containerId!,
containerType: containerType!,
));
} else {
emit(const ScanError(message: 'Fehler: Unbekannter Container-Typ'));
}
}
// ============================================================================
// STATE MACHINE: vehVs (Fahrzeug VS)
// ============================================================================
void _vehVsStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final objectType = object.type;
final currentState = object.state;
final subtype = object.subtype.toLowerCase();
// SB and ABS not allowed in vehVs
if (_isTypeSB(objectType, subtype) || _isTypeABS(objectType, subtype)) {
emit(const ScanError(message: 'Fehler: Falscher Status'));
return;
}
// CNTR = Container
if (_isTypeCNTR(objectType, subtype)) {
_vehVsCNTRStateMachine(object, currentState, subtype, emit);
} else {
emit(const ScanError(message: 'Fehler: Falscher Typ'));
}
}
void _vehVsCNTRStateMachine(LogisticObject object, String currentState, String subtype, Emitter<ScanState> emit) {
if (subtype == 'cntra') {
if (currentState == ObjectStates.retcGI) {
// Update all container objects and clear container
emit(ScanContainerCloseDetected(
object: object,
suggestedState: ObjectStates.unknown,
targetStateForObjects: ObjectStates.retGIFzg,
tour: currentTour,
containerType: 'a',
));
containerId = null;
containerType = null;
} else {
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
} else if (subtype == 'cntrb') {
if (currentState == ObjectStates.retcDS) {
// Update all container objects and clear container
emit(ScanContainerCloseDetected(
object: object,
suggestedState: ObjectStates.unknown,
targetStateForObjects: ObjectStates.retDSFzg,
tour: currentTour,
containerType: 'b',
));
containerId = null;
containerType = null;
} else {
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
}
// ============================================================================
// STATE MACHINE: gi (Geldinstitut)
// ============================================================================
void _giStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final objectType = object.type;
final currentState = object.state;
final subtype = object.subtype.toLowerCase();
// GK = Geldkassette
if (_isTypeGK(objectType, subtype)) {
_giGKStateMachine(object, currentState, emit);
}
// SB = Safebag
else if (_isTypeSB(objectType, subtype)) {
_giSBStateMachine(object, currentState, emit);
}
else {
emit(const ScanError(message: 'Fehler: Falscher Typ'));
}
}
void _giGKStateMachine(LogisticObject object, String currentState, Emitter<ScanState> emit) {
switch (currentState) {
case ObjectStates.retGIFzg:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.finGI,
tour: currentTour,
));
break;
case ObjectStates.unknown:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retDSEmpty,
tour: currentTour,
));
break;
case ObjectStates.delivery:
emit(const ScanError(message: 'Fehler: Falscher Status'));
break;
default:
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
void _giSBStateMachine(LogisticObject object, String currentState, Emitter<ScanState> emit) {
if (currentState == ObjectStates.retGIFzg) {
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.finGI,
tour: currentTour,
));
} else {
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
// ============================================================================
// STATE MACHINE: vehEnd (Fahrzeug Ende)
// ============================================================================
void _vehEndStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final currentState = object.state;
switch (currentState) {
case ObjectStates.retFailFzg:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retFailStk,
tour: currentTour,
));
break;
case ObjectStates.retDSFzg:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retDSStk,
tour: currentTour,
));
break;
case ObjectStates.delivery:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retDSStk,
tour: currentTour,
));
break;
case ObjectStates.retDSEmpty:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retDSStk,
tour: currentTour,
));
break;
case ObjectStates.retGIFzg:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retGIStk,
tour: currentTour,
));
break;
default:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.retDSErr,
tour: currentTour,
));
}
}
// ============================================================================
// STATE MACHINE: stockEnd (Lager Ende)
// ============================================================================
void _stockEndStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final currentState = object.state;
switch (currentState) {
case ObjectStates.retFailStk:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.finDSFail,
tour: currentTour,
));
break;
case ObjectStates.retDSStk:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.finDS,
tour: currentTour,
));
break;
case ObjectStates.retDSErr:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.finDSErr,
tour: currentTour,
));
break;
case ObjectStates.retGIStk:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.finGITmp,
tour: currentTour,
));
break;
default:
emit(const ScanError(message: 'Fehler: Falscher Status'));
}
}
// ============================================================================
// STATE MACHINE: stock (Lager - HADAG)
// ============================================================================
void _stockStateMachine(LogisticObject object, Emitter<ScanState> emit) {
final objectType = object.type;
final currentState = object.state;
final subtype = object.subtype.toLowerCase();
// Only HP and GK allowed
if (!_isTypeHP(objectType, subtype) && !_isTypeGK(objectType, subtype)) {
emit(const ScanError(message: 'Fehler: Falscher Typ'));
return;
}
switch (currentState) {
case ObjectStates.unknown:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.station,
tour: currentTour,
));
break;
case ObjectStates.stkHadag:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.station,
tour: currentTour,
));
break;
case ObjectStates.station:
emit(ScanObjectDetected(
object: object,
suggestedState: ObjectStates.stkHadag,
tour: currentTour,
));
break;
default:
emit(ScanUnknownObject(
barcode: object.code,
prefix: object.code.length >= 3 ? object.code.substring(0, 3) : '',
tour: currentTour,
));
}
}
// ============================================================================
// Helper methods for type checking
// ============================================================================
bool _isTypeGK(int type, String subtype) {
// GK = Geldkassette (type 1, subtypes meka, mekb, mekc, mekd, beka, bekb, bekc, bekd)
return type == 1 || subtype.startsWith('mek') || subtype.startsWith('bek');
}
bool _isTypeHP(int type, String subtype) {
// HP = Hauptkasse/Druckerpatronen (type 2, subtypes hp1a, hp1b, etc.)
return type == 2 || subtype.startsWith('hp');
}
bool _isTypeFR(int type, String subtype) {
// FR = Fahrkartenrolle (type 5, subtype fra)
return type == 5 || subtype.startsWith('fr');
}
bool _isTypeSB(int type, String subtype) {
// SB = Safebag (type 6)
return type == 6 || subtype == 'sb';
}
bool _isTypeABS(int type, String subtype) {
// ABS = Abfallbehälter (type 7)
return type == 7 || subtype == 'abs';
}
bool _isTypeCNTR(int type, String subtype) {
// CNTR = Container (type 8)
return type == 8 || subtype.startsWith('cntr');
}
// ============================================================================
// Legacy simple state machine (fallback)
// ============================================================================
String _determineNextState(LogisticObject object) {
switch (object.state) {
case ObjectStates.unknown:
return ObjectStates.toDelivery;
case ObjectStates.toDelivery:
return ObjectStates.delivery;
case ObjectStates.delivery:
return ObjectStates.station;
case ObjectStates.station:
return ObjectStates.inFA;
case ObjectStates.inFA:
return ObjectStates.retGI;
case ObjectStates.retGI:
return ObjectStates.retGIFzg;
case ObjectStates.retGIFzg:
return ObjectStates.finGI;
case ObjectStates.retFail:
return ObjectStates.retFailFzg;
case ObjectStates.retFailFzg:
return ObjectStates.retFailStk;
case ObjectStates.retDS:
return ObjectStates.retDSFzg;
case ObjectStates.retDSFzg:
return ObjectStates.finDS;
default:
return object.state;
}
}
// ============================================================================
// Event handlers
// ============================================================================
Future<void> _onValidateBarcode(ValidateBarcode event, Emitter<ScanState> emit) async {
if (event.barcode.isEmpty) {
emit(const ScanValidationError(message: 'Barcode darf nicht leer sein'));
return;
}
if (event.barcode.length < 6) {
emit(const ScanValidationError(message: 'Barcode zu kurz'));
return;
}
add(ProcessBarcode(barcode: event.barcode));
}
Future<void> _onUpdateObjectState(UpdateObjectState event, Emitter<ScanState> emit) async {
emit(ScanProcessing(barcode: event.object.code));
final result = await repository.updateObjectState(
event.object.objectId,
event.newState,
locationId: currentTour?.locationId,
refType: currentTour != null ? _getTourTypeCode(currentTour!.type) : null,
refId: currentTour?.tourId,
containerCode: event.containerCode,
);
result.fold(
(failure) => emit(ScanError(message: _mapFailureToMessage(failure))),
(_) {
emit(ScanObjectUpdated(
object: event.object.copyWith(state: event.newState),
previousState: event.object.state,
newState: event.newState,
tour: currentTour,
));
},
);
}
Future<void> _onCreateUnknownObject(CreateUnknownObject event, Emitter<ScanState> emit) async {
emit(ScanProcessing(barcode: event.barcode));
final result = await repository.createObject(
type: event.type,
code: event.barcode,
isManual: true,
);
result.fold(
(failure) => emit(ScanError(message: _mapFailureToMessage(failure))),
(_) {
emit(ScanObjectCreated(barcode: event.barcode));
},
);
}
void _onResetScan(ResetScan event, Emitter<ScanState> emit) {
if (currentTour != null) {
emit(ScanReady(tour: currentTour!));
} else {
emit(ScanInitial());
}
}
// ============================================================================
// Helper methods
// ============================================================================
Map<String, String>? _findPageForBarcode(String barcode) {
if (currentTour == null) return null;
for (final page in currentTour!.pages) {
if (page.code == barcode) {
return {
'pageId': page.pageId,
'label': page.label ?? page.pageId,
};
}
if (page.pageId.toLowerCase().startsWith('fsa') && page.type.isNotEmpty) {
// FSA page detected
}
}
return null;
}
bool _isValidPrefix(String prefix) {
final validPrefixes = ['MEK', 'BEK', 'HOP', 'H1P', 'H2P', 'H3P', 'FR', 'SB', 'ABS', 'FZG'];
return validPrefixes.any((p) => prefix.toUpperCase().startsWith(p));
}
int? _getTourTypeCode(String type) {
switch (type) {
case TourTypes.stockStart:
return 1;
case TourTypes.vehStart:
return 2;
case TourTypes.veh:
return 3;
case TourTypes.fsa:
return 4;
case TourTypes.vs:
return 5;
case TourTypes.gi:
return 6;
case TourTypes.vehEnd:
return 7;
case TourTypes.stockEnd:
return 8;
default:
return null;
}
}
String _mapFailureToMessage(Failure failure) {
return switch (failure) {
ServerFailure _ => 'Serverfehler: ${failure.message}',
NetworkFailure _ => 'Netzwerkfehler. Bitte überprüfen Sie Ihre Internetverbindung.',
NotFoundFailure _ => 'Objekt nicht gefunden',
BarcodeFailure _ => 'Barcode-Fehler: ${failure.message}',
_ => 'Ein Fehler ist aufgetreten: ${failure.message}',
};
}
}