Initial commit: unionflow-mobile-apps
Application Flutter complète (sans build artifacts). Signed-off-by: lions dev Team
This commit is contained in:
@@ -0,0 +1,413 @@
|
||||
/// Implémentation du repository de workflow financier
|
||||
library finance_workflow_repository_impl;
|
||||
|
||||
import 'dart:async';
|
||||
import 'package:dartz/dartz.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../../../../core/error/exceptions.dart';
|
||||
import '../../../../core/error/failures.dart';
|
||||
import '../../../../core/network/network_info.dart';
|
||||
import '../../../../core/network/retry_policy.dart';
|
||||
import '../../../../core/network/offline_manager.dart';
|
||||
import '../../domain/entities/budget.dart';
|
||||
import '../../domain/entities/financial_audit_log.dart';
|
||||
import '../../domain/entities/transaction_approval.dart';
|
||||
import '../../domain/repositories/finance_workflow_repository.dart';
|
||||
import '../datasources/finance_workflow_remote_datasource.dart';
|
||||
|
||||
@LazySingleton(as: FinanceWorkflowRepository)
|
||||
class FinanceWorkflowRepositoryImpl implements FinanceWorkflowRepository {
|
||||
final FinanceWorkflowRemoteDatasource remoteDatasource;
|
||||
final NetworkInfo networkInfo;
|
||||
final OfflineManager offlineManager;
|
||||
final RetryPolicy _retryPolicy;
|
||||
|
||||
FinanceWorkflowRepositoryImpl({
|
||||
required this.remoteDatasource,
|
||||
required this.networkInfo,
|
||||
required this.offlineManager,
|
||||
}) : _retryPolicy = RetryPolicy(config: RetryConfig.standard);
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<TransactionApproval>>> getPendingApprovals({
|
||||
String? organizationId,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final approvals = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.getPendingApprovals(
|
||||
organizationId: organizationId,
|
||||
),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(approvals);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, TransactionApproval>> getApprovalById(
|
||||
String approvalId) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final approval = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.getApprovalById(approvalId),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(approval);
|
||||
} on NotFoundException {
|
||||
return Left(NotFoundFailure('Approbation non trouvée'));
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, TransactionApproval>> approveTransaction({
|
||||
required String approvalId,
|
||||
String? comment,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
// Queue for retry when back online
|
||||
await offlineManager.queueOperation(
|
||||
operationType: 'approveTransaction',
|
||||
endpoint: '/api/finance/approvals/$approvalId/approve',
|
||||
data: {'approvalId': approvalId, 'comment': comment},
|
||||
);
|
||||
return Left(NetworkFailure('Pas de connexion Internet. Opération mise en attente.'));
|
||||
}
|
||||
|
||||
try {
|
||||
final approval = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.approveTransaction(
|
||||
approvalId: approvalId,
|
||||
comment: comment,
|
||||
),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(approval);
|
||||
} on ForbiddenException catch (e) {
|
||||
return Left(ForbiddenFailure(e.message));
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, TransactionApproval>> rejectTransaction({
|
||||
required String approvalId,
|
||||
required String reason,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
// Queue for retry when back online
|
||||
await offlineManager.queueOperation(
|
||||
operationType: 'rejectTransaction',
|
||||
endpoint: '/api/finance/approvals/$approvalId/reject',
|
||||
data: {'approvalId': approvalId, 'reason': reason},
|
||||
);
|
||||
return Left(NetworkFailure('Pas de connexion Internet. Opération mise en attente.'));
|
||||
}
|
||||
|
||||
try {
|
||||
final approval = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.rejectTransaction(
|
||||
approvalId: approvalId,
|
||||
reason: reason,
|
||||
),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(approval);
|
||||
} on ForbiddenException catch (e) {
|
||||
return Left(ForbiddenFailure(e.message));
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<Budget>>> getBudgets({
|
||||
String? organizationId,
|
||||
BudgetStatus? status,
|
||||
int? year,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final budgets = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.getBudgets(
|
||||
organizationId: organizationId,
|
||||
status: status?.name,
|
||||
year: year,
|
||||
),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(budgets);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Budget>> getBudgetById(String budgetId) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final budget = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.getBudgetById(budgetId),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(budget);
|
||||
} on NotFoundException {
|
||||
return Left(NotFoundFailure('Budget non trouvé'));
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Budget>> createBudget({
|
||||
required String name,
|
||||
String? description,
|
||||
required String organizationId,
|
||||
required BudgetPeriod period,
|
||||
required int year,
|
||||
int? month,
|
||||
required List<BudgetLine> lines,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
// Queue for retry when back online
|
||||
await offlineManager.queueOperation(
|
||||
operationType: 'createBudget',
|
||||
endpoint: '/api/finance/budgets',
|
||||
data: {
|
||||
'name': name,
|
||||
'description': description,
|
||||
'organizationId': organizationId,
|
||||
'period': period.name,
|
||||
'year': year,
|
||||
'month': month,
|
||||
'lines': lines.map((line) => {
|
||||
'id': line.id,
|
||||
'category': line.category.name,
|
||||
'name': line.name,
|
||||
'description': line.description,
|
||||
'amountPlanned': line.amountPlanned,
|
||||
'amountRealized': line.amountRealized,
|
||||
'notes': line.notes,
|
||||
}).toList(),
|
||||
},
|
||||
);
|
||||
return Left(NetworkFailure('Pas de connexion Internet. Opération mise en attente.'));
|
||||
}
|
||||
|
||||
try {
|
||||
final budget = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.createBudget(
|
||||
name: name,
|
||||
description: description,
|
||||
organizationId: organizationId,
|
||||
period: period.name,
|
||||
year: year,
|
||||
month: month,
|
||||
lines: lines.map((line) => {
|
||||
'id': line.id,
|
||||
'category': line.category.name,
|
||||
'name': line.name,
|
||||
'description': line.description,
|
||||
'amountPlanned': line.amountPlanned,
|
||||
'amountRealized': line.amountRealized,
|
||||
'notes': line.notes,
|
||||
}).toList(),
|
||||
),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(budget);
|
||||
} on ForbiddenException catch (e) {
|
||||
return Left(ForbiddenFailure(e.message));
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<String, dynamic>>> getBudgetTracking({
|
||||
required String budgetId,
|
||||
}) async {
|
||||
if (!await networkInfo.isConnected) {
|
||||
return Left(NetworkFailure('Pas de connexion Internet'));
|
||||
}
|
||||
|
||||
try {
|
||||
final tracking = await _retryPolicy.execute(
|
||||
operation: () => remoteDatasource.getBudgetTracking(budgetId: budgetId),
|
||||
shouldRetry: (error) => _isRetryableError(error),
|
||||
);
|
||||
return Right(tracking);
|
||||
} on UnauthorizedException {
|
||||
return Left(UnauthorizedFailure('Session expirée'));
|
||||
} on ServerException catch (e) {
|
||||
return Left(ServerFailure(e.message));
|
||||
} on TimeoutException {
|
||||
return Left(NetworkFailure('Délai d\'attente dépassé'));
|
||||
} catch (e) {
|
||||
return Left(UnexpectedFailure('Erreur inattendue: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine if an error is retryable
|
||||
bool _isRetryableError(dynamic error) {
|
||||
// Server errors are retryable
|
||||
if (error is ServerException) return true;
|
||||
|
||||
// Timeout errors are retryable
|
||||
if (error is TimeoutException) return true;
|
||||
|
||||
// Client errors are not retryable
|
||||
if (error is UnauthorizedException) return false;
|
||||
if (error is ForbiddenException) return false;
|
||||
if (error is NotFoundException) return false;
|
||||
if (error is ValidationException) return false;
|
||||
|
||||
// Unknown errors - default to not retryable
|
||||
return false;
|
||||
}
|
||||
|
||||
// === MÉTHODES NON IMPLÉMENTÉES (Stubs) ===
|
||||
|
||||
@override
|
||||
Future<Either<Failure, TransactionApproval>> requestApproval({
|
||||
required String transactionId,
|
||||
required TransactionType transactionType,
|
||||
required double amount,
|
||||
}) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<TransactionApproval>>> getApprovalsHistory({
|
||||
String? organizationId,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
ApprovalStatus? status,
|
||||
}) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Budget>> updateBudget({
|
||||
required String budgetId,
|
||||
String? name,
|
||||
String? description,
|
||||
List<BudgetLine>? lines,
|
||||
BudgetStatus? status,
|
||||
}) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, void>> deleteBudget(String budgetId) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<FinancialAuditLog>>> getAuditLogs({
|
||||
String? organizationId,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
AuditOperation? operation,
|
||||
AuditEntityType? entityType,
|
||||
AuditSeverity? severity,
|
||||
int? limit,
|
||||
}) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, List<FinancialAuditLog>>> getAnomalies({
|
||||
String? organizationId,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
}) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, String>> exportAuditLogs({
|
||||
required String organizationId,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
String format = 'csv',
|
||||
}) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Either<Failure, Map<String, dynamic>>> getWorkflowStats({
|
||||
required String organizationId,
|
||||
DateTime? startDate,
|
||||
DateTime? endDate,
|
||||
}) async {
|
||||
return Left(
|
||||
NotImplementedFailure('Fonctionnalité en cours de développement'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user