runable
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
import 'package:dartz/dartz.dart';
|
||||
import '../../../../core/errors/failures.dart';
|
||||
import '../../../../core/errors/exceptions.dart';
|
||||
import '../../domain/entities/scan_entity.dart';
|
||||
import '../../domain/repositories/scanner_repository.dart';
|
||||
import '../datasources/scanner_local_datasource.dart';
|
||||
import '../datasources/scanner_remote_datasource.dart';
|
||||
import '../models/save_request_model.dart';
|
||||
import '../models/scan_item.dart';
|
||||
|
||||
/// Implementation of ScannerRepository
|
||||
/// This class handles the coordination between remote and local data sources
|
||||
class ScannerRepositoryImpl implements ScannerRepository {
|
||||
final ScannerRemoteDataSource remoteDataSource;
|
||||
final ScannerLocalDataSource localDataSource;
|
||||
|
||||
ScannerRepositoryImpl({
|
||||
required this.remoteDataSource,
|
||||
required this.localDataSource,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> saveScan({
|
||||
required String barcode,
|
||||
required String field1,
|
||||
required String field2,
|
||||
required String field3,
|
||||
required String field4,
|
||||
}) async {
|
||||
try {
|
||||
// Create the request model
|
||||
final request = SaveRequestModel.fromParams(
|
||||
barcode: barcode,
|
||||
field1: field1,
|
||||
field2: field2,
|
||||
field3: field3,
|
||||
field4: field4,
|
||||
);
|
||||
|
||||
// Validate the request
|
||||
if (!request.isValid) {
|
||||
return Left(ValidationFailure(request.validationErrors.join(', ')));
|
||||
}
|
||||
|
||||
// Save to remote server
|
||||
await remoteDataSource.saveScan(request);
|
||||
|
||||
// If remote save succeeds, we return success
|
||||
// Local save will be handled separately by the use case if needed
|
||||
return const Right(null);
|
||||
|
||||
} on ValidationException catch (e) {
|
||||
return Left(ValidationFailure(e.message));
|
||||
} on NetworkException catch (e) {
|
||||
return Left(NetworkFailure(e.message));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to save scan: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<ScanEntity>>> getScanHistory() async {
|
||||
try {
|
||||
// Get scans from local storage
|
||||
final scanItems = await localDataSource.getAllScans();
|
||||
|
||||
// Convert to domain entities
|
||||
final entities = scanItems.map((item) => item.toEntity()).toList();
|
||||
|
||||
return Right(entities);
|
||||
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to get scan history: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> saveScanLocally(ScanEntity scan) async {
|
||||
try {
|
||||
// Convert entity to data model
|
||||
final scanItem = ScanItem.fromEntity(scan);
|
||||
|
||||
// Save to local storage
|
||||
await localDataSource.saveScan(scanItem);
|
||||
|
||||
return const Right(null);
|
||||
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to save scan locally: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteScanLocally(String barcode) async {
|
||||
try {
|
||||
if (barcode.trim().isEmpty) {
|
||||
return const Left(ValidationFailure('Barcode cannot be empty'));
|
||||
}
|
||||
|
||||
// Delete from local storage
|
||||
await localDataSource.deleteScan(barcode);
|
||||
|
||||
return const Right(null);
|
||||
|
||||
} on ValidationException catch (e) {
|
||||
return Left(ValidationFailure(e.message));
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to delete scan: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> clearScanHistory() async {
|
||||
try {
|
||||
// Clear all scans from local storage
|
||||
await localDataSource.clearAllScans();
|
||||
|
||||
return const Right(null);
|
||||
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to clear scan history: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, ScanEntity?>> getScanByBarcode(String barcode) async {
|
||||
try {
|
||||
if (barcode.trim().isEmpty) {
|
||||
return const Left(ValidationFailure('Barcode cannot be empty'));
|
||||
}
|
||||
|
||||
// Get scan from local storage
|
||||
final scanItem = await localDataSource.getScanByBarcode(barcode);
|
||||
|
||||
if (scanItem == null) {
|
||||
return const Right(null);
|
||||
}
|
||||
|
||||
// Convert to domain entity
|
||||
final entity = scanItem.toEntity();
|
||||
|
||||
return Right(entity);
|
||||
|
||||
} on ValidationException catch (e) {
|
||||
return Left(ValidationFailure(e.message));
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to get scan by barcode: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> updateScanLocally(ScanEntity scan) async {
|
||||
try {
|
||||
// Convert entity to data model
|
||||
final scanItem = ScanItem.fromEntity(scan);
|
||||
|
||||
// Update in local storage
|
||||
await localDataSource.updateScan(scanItem);
|
||||
|
||||
return const Right(null);
|
||||
|
||||
} on CacheException catch (e) {
|
||||
return Left(CacheFailure(e.message));
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to update scan: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Additional utility methods for repository
|
||||
|
||||
/// Get scans count
|
||||
Future<Either<Failure, int>> getScansCount() async {
|
||||
try {
|
||||
if (localDataSource is ScannerLocalDataSourceImpl) {
|
||||
final impl = localDataSource as ScannerLocalDataSourceImpl;
|
||||
final count = await impl.getScansCount();
|
||||
return Right(count);
|
||||
}
|
||||
|
||||
// Fallback: get all scans and count them
|
||||
final result = await getScanHistory();
|
||||
return result.fold(
|
||||
(failure) => Left(failure),
|
||||
(scans) => Right(scans.length),
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to get scans count: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if scan exists locally
|
||||
Future<Either<Failure, bool>> scanExistsLocally(String barcode) async {
|
||||
try {
|
||||
if (barcode.trim().isEmpty) {
|
||||
return const Left(ValidationFailure('Barcode cannot be empty'));
|
||||
}
|
||||
|
||||
if (localDataSource is ScannerLocalDataSourceImpl) {
|
||||
final impl = localDataSource as ScannerLocalDataSourceImpl;
|
||||
final exists = await impl.scanExists(barcode);
|
||||
return Right(exists);
|
||||
}
|
||||
|
||||
// Fallback: get scan by barcode
|
||||
final result = await getScanByBarcode(barcode);
|
||||
return result.fold(
|
||||
(failure) => Left(failure),
|
||||
(scan) => Right(scan != null),
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to check if scan exists: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Get scans by date range
|
||||
Future<Either<Failure, List<ScanEntity>>> getScansByDateRange({
|
||||
required DateTime startDate,
|
||||
required DateTime endDate,
|
||||
}) async {
|
||||
try {
|
||||
if (localDataSource is ScannerLocalDataSourceImpl) {
|
||||
final impl = localDataSource as ScannerLocalDataSourceImpl;
|
||||
final scanItems = await impl.getScansByDateRange(
|
||||
startDate: startDate,
|
||||
endDate: endDate,
|
||||
);
|
||||
|
||||
// Convert to domain entities
|
||||
final entities = scanItems.map((item) => item.toEntity()).toList();
|
||||
return Right(entities);
|
||||
}
|
||||
|
||||
// Fallback: get all scans and filter
|
||||
final result = await getScanHistory();
|
||||
return result.fold(
|
||||
(failure) => Left(failure),
|
||||
(scans) {
|
||||
final filteredScans = scans
|
||||
.where((scan) =>
|
||||
scan.timestamp.isAfter(startDate) &&
|
||||
scan.timestamp.isBefore(endDate))
|
||||
.toList();
|
||||
return Right(filteredScans);
|
||||
},
|
||||
);
|
||||
|
||||
} catch (e) {
|
||||
return Left(UnknownFailure('Failed to get scans by date range: ${e.toString()}'));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user