Mobile mirror du backend P2-NEW-3. DRY strict — Clean Architecture identique aux autres features (compliance, devise). Domain - StatutRapport enum (draft, signe, archive, inconnu) + extension StatutRapportX (code/libelle/severite/fromCode) - RapportTrimestriel entity (Equatable) + helpers libellePeriode, scoreSeverite (>=80 success, >=60 warning, <60 danger) Data - RapportTrimestrielModel.fromJson (parsing tolérant null + dates ISO) - ReportingRemoteDataSourceImpl (Dio) : lister/generer/signer/archiver/telechargerPdf - ReportingRepositoryImpl @Injectable Presentation - ReportingBloc : Load/Generer/Signer/Archiver events, 4 states (Initial/Loading/Loaded/Error) - RapportsTrimestrielsPage : Material 3 ListView avec cards (period, score coloré, tag statut, hash tronqué) + EmptyView + ErrorView Tests (10/10 verts) - StatutRapportX × 2 (fromCode parse/null/inconnu, libelle/severite/code) - RapportTrimestriel × 5 (libellePeriode, estDraft/estSigne/estArchive, scoreSeverite × 3) - RapportTrimestrielModel.fromJson × 3 (complet, fallbacks, date invalide) Note : viewer PDF interne reporté à un sprint dédié (intégration pdfx + permission storage Android). Téléchargement bytes via API exposé dans datasource pour usage futur.
130 lines
4.2 KiB
Dart
130 lines
4.2 KiB
Dart
import 'package:equatable/equatable.dart';
|
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:injectable/injectable.dart';
|
|
import '../../../../core/utils/logger.dart';
|
|
import '../../domain/entities/rapport_trimestriel.dart';
|
|
import '../../domain/repositories/reporting_repository.dart';
|
|
|
|
// ── Events ───────────────────────────────────────────────────────────
|
|
abstract class ReportingEvent extends Equatable {
|
|
const ReportingEvent();
|
|
@override
|
|
List<Object?> get props => [];
|
|
}
|
|
|
|
class LoadRapports extends ReportingEvent {
|
|
final int? annee;
|
|
const LoadRapports({this.annee});
|
|
@override
|
|
List<Object?> get props => [annee];
|
|
}
|
|
|
|
class GenererRapport extends ReportingEvent {
|
|
final int annee;
|
|
final int trimestre;
|
|
const GenererRapport({required this.annee, required this.trimestre});
|
|
@override
|
|
List<Object?> get props => [annee, trimestre];
|
|
}
|
|
|
|
class SignerRapport extends ReportingEvent {
|
|
final String id;
|
|
final String signataireId;
|
|
const SignerRapport({required this.id, required this.signataireId});
|
|
@override
|
|
List<Object?> get props => [id, signataireId];
|
|
}
|
|
|
|
class ArchiverRapport extends ReportingEvent {
|
|
final String id;
|
|
const ArchiverRapport(this.id);
|
|
@override
|
|
List<Object?> get props => [id];
|
|
}
|
|
|
|
// ── States ───────────────────────────────────────────────────────────
|
|
abstract class ReportingState extends Equatable {
|
|
const ReportingState();
|
|
@override
|
|
List<Object?> get props => [];
|
|
}
|
|
|
|
class ReportingInitial extends ReportingState {
|
|
const ReportingInitial();
|
|
}
|
|
|
|
class ReportingLoading extends ReportingState {
|
|
const ReportingLoading();
|
|
}
|
|
|
|
class ReportingLoaded extends ReportingState {
|
|
final List<RapportTrimestriel> rapports;
|
|
final int annee;
|
|
const ReportingLoaded({required this.rapports, required this.annee});
|
|
@override
|
|
List<Object?> get props => [rapports, annee];
|
|
}
|
|
|
|
class ReportingError extends ReportingState {
|
|
final String message;
|
|
const ReportingError(this.message);
|
|
@override
|
|
List<Object?> get props => [message];
|
|
}
|
|
|
|
// ── Bloc ─────────────────────────────────────────────────────────────
|
|
@injectable
|
|
class ReportingBloc extends Bloc<ReportingEvent, ReportingState> {
|
|
final ReportingRepository repository;
|
|
|
|
ReportingBloc(this.repository) : super(const ReportingInitial()) {
|
|
on<LoadRapports>(_onLoad);
|
|
on<GenererRapport>(_onGenerer);
|
|
on<SignerRapport>(_onSigner);
|
|
on<ArchiverRapport>(_onArchiver);
|
|
}
|
|
|
|
Future<void> _onLoad(LoadRapports event, Emitter<ReportingState> emit) async {
|
|
emit(const ReportingLoading());
|
|
try {
|
|
final annee = event.annee ?? DateTime.now().year;
|
|
final rapports = await repository.lister(annee: annee);
|
|
emit(ReportingLoaded(rapports: rapports, annee: annee));
|
|
} catch (e, st) {
|
|
AppLogger.error('ReportingBloc: load', error: e, stackTrace: st);
|
|
emit(ReportingError(e.toString()));
|
|
}
|
|
}
|
|
|
|
Future<void> _onGenerer(GenererRapport event, Emitter<ReportingState> emit) async {
|
|
final current = state;
|
|
try {
|
|
await repository.generer(annee: event.annee, trimestre: event.trimestre);
|
|
// Reload list
|
|
add(LoadRapports(annee: current is ReportingLoaded ? current.annee : event.annee));
|
|
} catch (e) {
|
|
emit(ReportingError(e.toString()));
|
|
}
|
|
}
|
|
|
|
Future<void> _onSigner(SignerRapport event, Emitter<ReportingState> emit) async {
|
|
final current = state;
|
|
try {
|
|
await repository.signer(event.id, event.signataireId);
|
|
add(LoadRapports(annee: current is ReportingLoaded ? current.annee : null));
|
|
} catch (e) {
|
|
emit(ReportingError(e.toString()));
|
|
}
|
|
}
|
|
|
|
Future<void> _onArchiver(ArchiverRapport event, Emitter<ReportingState> emit) async {
|
|
final current = state;
|
|
try {
|
|
await repository.archiver(event.id);
|
|
add(LoadRapports(annee: current is ReportingLoaded ? current.annee : null));
|
|
} catch (e) {
|
|
emit(ReportingError(e.toString()));
|
|
}
|
|
}
|
|
}
|