| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496 |
- import 'package:drift/drift.dart';
- import 'package:flutter_riverpod/flutter_riverpod.dart';
- import 'package:ristoris/core/constants.dart';
- import 'package:ristoris/database/database.dart';
- import '../services/api_service.dart';
- import '../services/sync/agente_sync_service.dart';
- import '../services/sync/cliente_sync_service.dart';
- import '../services/sync/ordine_sync_service.dart';
- import '../services/sync/filiale_sync_service.dart';
- import '../services/sync/reference_sync_service.dart';
- import '../services/image_service.dart';
- import 'article_provider.dart';
- import 'auth_provider.dart';
- import 'customer_provider.dart';
- import 'dashboard_provider.dart';
- import 'database_provider.dart';
- import 'order_provider.dart';
- import '../ui/pages/payments/payments_page.dart';
- final apiServiceProvider = Provider<ApiService>((ref) {
- final parametriDao = ref.read(parametriDaoProvider);
- return ApiService(parametriDao);
- });
- final imageServiceProvider = Provider<ImageService>((ref) {
- final parametriDao = ref.read(parametriDaoProvider);
- return ImageService(parametriDao);
- });
- final agenteSyncServiceProvider = Provider<AgenteSyncService>((ref) {
- final agentiDao = ref.read(agentiDaoProvider);
- return AgenteSyncService(ref.read(apiServiceProvider), agentiDao);
- });
- final clienteSyncServiceProvider = Provider<ClienteSyncService>((ref) {
- final api = ref.read(apiServiceProvider);
- final clientiDao = ref.read(clientiDaoProvider);
- final clientiAgenteDao = ref.read(clientiAgenteDaoProvider);
- return ClienteSyncService(api, clientiDao, clientiAgenteDao);
- });
- final ordineSyncServiceProvider = Provider<OrdineSyncService>((ref) {
- return OrdineSyncService(
- ref.read(apiServiceProvider),
- ref.read(ordiniDaoProvider),
- ref.read(clientiDaoProvider),
- ref.read(filialiDaoProvider),
- ref.read(agentiDaoProvider),
- );
- });
- final filialeSyncServiceProvider = Provider<FilialeSyncService>((ref) {
- return FilialeSyncService(
- ref.read(apiServiceProvider),
- ref.read(filialiDaoProvider),
- ref.read(clientiDaoProvider),
- );
- });
- final referenceSyncServiceProvider = Provider<ReferenceSyncService>((ref) {
- return ReferenceSyncService(
- ref.read(apiServiceProvider),
- ref.read(tabelleDaoProvider),
- ref.read(pagamentiDaoProvider),
- ref.read(scontiDaoProvider),
- ref.read(articoliDaoProvider),
- ref.read(listiniDaoProvider),
- ref.read(promozioniDaoProvider),
- ref.read(scadenzeDaoProvider),
- ref.read(statisticheDaoProvider),
- ref.read(parametriDaoProvider),
- ref.read(clientiDaoProvider),
- );
- });
- enum SyncPhaseStatus { waiting, downloading, processing, completed }
- class SyncPhaseInfo {
- final String name;
- final SyncPhaseStatus status;
- final int? currentRecord;
- final int? totalRecords;
- const SyncPhaseInfo({
- required this.name,
- this.status = SyncPhaseStatus.waiting,
- this.currentRecord,
- this.totalRecords,
- });
- SyncPhaseInfo copyWith({
- SyncPhaseStatus? status,
- int? currentRecord,
- int? totalRecords,
- }) {
- return SyncPhaseInfo(
- name: name,
- status: status ?? this.status,
- currentRecord: currentRecord ?? this.currentRecord,
- totalRecords: totalRecords ?? this.totalRecords,
- );
- }
- String get statusText {
- switch (status) {
- case SyncPhaseStatus.waiting:
- return 'IN ATTESA';
- case SyncPhaseStatus.downloading:
- return 'IN SCARICAMENTO';
- case SyncPhaseStatus.processing:
- if (currentRecord != null && totalRecords != null) {
- return 'Elaborazione $currentRecord di $totalRecords';
- }
- return 'IN ELABORAZIONE';
- case SyncPhaseStatus.completed:
- return 'COMPLETATO';
- }
- }
- }
- const List<String> syncPhaseNames = [
- 'Invio nuovi clienti',
- 'Invio nuove filiali',
- 'Invio nuovi ordini',
- 'Scaricamento tabelle',
- 'Scaricamento pagamenti',
- 'Scaricamento sconti',
- 'Scaricamento articoli',
- 'Scaricamento listini',
- 'Scaricamento listini articolo',
- 'Scaricamento promozioni',
- 'Scaricamento clienti',
- 'Scaricamento filiali',
- 'Scaricamento scadenze',
- 'Scaricamento listini cliente',
- 'Scaricamento sconti cliente',
- 'Scaricamento statistiche',
- ];
- class SyncState {
- final bool isSyncing;
- final double progress;
- final String message;
- final String? error;
- final List<SyncPhaseInfo> phases;
- final DateTime? lastSyncTime;
- final String? currentAgentCode;
- final List<String> agentiCodici;
- const SyncState({
- this.isSyncing = false,
- this.progress = 0,
- this.message = '',
- this.error,
- this.phases = const [],
- this.lastSyncTime,
- this.currentAgentCode,
- this.agentiCodici = const [],
- });
- }
- class SyncNotifier extends Notifier<SyncState> {
- DateTime _lastProgressUpdate = DateTime.fromMillisecondsSinceEpoch(0);
- @override
- SyncState build() {
- Future.microtask(() async {
- try {
- final parametriDao = ref.read(parametriDaoProvider);
- final lastSyncTimeStr = await parametriDao.read(AppConstants.kLastUpdateParam);
- if (lastSyncTimeStr != null) {
- final lastSyncTime = DateTime.tryParse(lastSyncTimeStr);
- if (lastSyncTime != null) {
- state = SyncState(
- phases: state.phases,
- lastSyncTime: lastSyncTime,
- );
- }
- }
- } catch (_) {}
- });
- return const SyncState();
- }
- List<SyncPhaseInfo> _initPhases() {
- return syncPhaseNames.map((name) => SyncPhaseInfo(name: name)).toList();
- }
- void _setPhase(
- int index,
- SyncPhaseStatus status, {
- int? currentRecord,
- int? totalRecords,
- }) {
- final phases = List<SyncPhaseInfo>.from(state.phases);
- phases[index] = phases[index].copyWith(
- status: status,
- currentRecord: currentRecord,
- totalRecords: totalRecords,
- );
- final progress = (index + 1) / syncPhaseNames.length;
- state = SyncState(
- isSyncing: true,
- progress: progress,
- phases: phases,
- lastSyncTime: state.lastSyncTime,
- currentAgentCode: state.currentAgentCode,
- agentiCodici: state.agentiCodici,
- );
- }
- void _resetAgentPhases() {
- final phases = List<SyncPhaseInfo>.from(state.phases);
- for (int i = 10; i <= 14; i++) {
- phases[i] = SyncPhaseInfo(name: syncPhaseNames[i]);
- }
- state = SyncState(
- isSyncing: true,
- progress: state.progress,
- phases: phases,
- lastSyncTime: state.lastSyncTime,
- currentAgentCode: state.currentAgentCode,
- agentiCodici: state.agentiCodici,
- );
- }
- void _setCurrentAgent(String codAgente) {
- state = SyncState(
- isSyncing: true,
- progress: state.progress,
- phases: state.phases,
- lastSyncTime: state.lastSyncTime,
- currentAgentCode: codAgente,
- agentiCodici: state.agentiCodici,
- );
- }
- void Function(int current, int total) _throttledProgress(int phaseIndex) {
- return (int current, int total) {
- final now = DateTime.now();
- if (now.difference(_lastProgressUpdate).inMilliseconds >= 500) {
- _lastProgressUpdate = now;
- _setPhase(
- phaseIndex,
- SyncPhaseStatus.processing,
- currentRecord: current,
- totalRecords: total,
- );
- }
- };
- }
- Future<void> startFullSync(
- int idAgente,
- String codAgente, {
- bool forceFullSync = false,
- }) async {
- _lastProgressUpdate = DateTime.fromMillisecondsSinceEpoch(0);
- final parametriDao = ref.read(parametriDaoProvider);
- final lastSyncTimeStr = await parametriDao.read(AppConstants.kLastUpdateParam);
- final lastSyncTime = lastSyncTimeStr != null ? DateTime.tryParse(lastSyncTimeStr) : null;
- state = SyncState(
- isSyncing: true,
- phases: _initPhases(),
- message: 'Avvio sincronizzazione...',
- lastSyncTime: lastSyncTime,
- );
- try {
- final agentiDao = ref.read(agentiDaoProvider);
- final agente = await agentiDao.findByCode(codAgente);
- if (agente?.pwd != null) {
- final authService = ref.read(authServiceProvider);
- final token = await authService.loginRemote(codAgente, agente!.pwd!);
- ref.read(apiServiceProvider).token = token;
- }
- final agenteSync = ref.read(agenteSyncServiceProvider);
- final ordineSync = ref.read(ordineSyncServiceProvider);
- final filialeSync = ref.read(filialeSyncServiceProvider);
- final clienteSync = ref.read(clienteSyncServiceProvider);
- final refSync = ref.read(referenceSyncServiceProvider);
- final lastUpdateStr = forceFullSync ? '200001010000' : (await parametriDao.read(AppConstants.kLastUpdateParam) ?? '200001010000');
- await agenteSync.downloadAgenti(idAgente, codAgente, lastUpdateStr);
- // Phase 0: Invio nuovi clienti
- _setPhase(0, SyncPhaseStatus.processing);
- await clienteSync.uploadNewCustomers(
- idAgente,
- codAgente,
- onProgress: _throttledProgress(0),
- );
- _setPhase(0, SyncPhaseStatus.completed);
- // Phase 1: Invio nuove filiali
- _setPhase(1, SyncPhaseStatus.processing);
- await filialeSync.uploadNewDestinations(
- idAgente,
- codAgente,
- onProgress: _throttledProgress(1),
- );
- _setPhase(1, SyncPhaseStatus.completed);
- // Phase 2: Invio nuovi ordini
- _setPhase(2, SyncPhaseStatus.processing);
- await ordineSync.uploadNewOrders(
- idAgente,
- codAgente,
- onProgress: _throttledProgress(2),
- );
- _setPhase(2, SyncPhaseStatus.completed);
- // Phases 3-9: Reference downloads
- _setPhase(3, SyncPhaseStatus.downloading);
- await refSync.downloadTabelle(
- lastUpdateStr,
- onProgress: _throttledProgress(3),
- );
- _setPhase(3, SyncPhaseStatus.completed);
- _setPhase(4, SyncPhaseStatus.downloading);
- await refSync.downloadPagamenti(
- lastUpdateStr,
- onProgress: _throttledProgress(4),
- );
- _setPhase(4, SyncPhaseStatus.completed);
- _setPhase(5, SyncPhaseStatus.downloading);
- await refSync.downloadSconti(
- lastUpdateStr,
- onProgress: _throttledProgress(5),
- );
- _setPhase(5, SyncPhaseStatus.completed);
- _setPhase(6, SyncPhaseStatus.downloading);
- await refSync.downloadArticoli(
- lastUpdateStr,
- onProgress: _throttledProgress(6),
- );
- _setPhase(6, SyncPhaseStatus.completed);
- _setPhase(7, SyncPhaseStatus.downloading);
- await refSync.downloadListini(
- lastUpdateStr,
- onProgress: _throttledProgress(7),
- );
- _setPhase(7, SyncPhaseStatus.completed);
- _setPhase(8, SyncPhaseStatus.downloading);
- await refSync.downloadListiniArticolo(
- lastUpdateStr,
- onProgress: _throttledProgress(8),
- );
- _setPhase(8, SyncPhaseStatus.completed);
- _setPhase(9, SyncPhaseStatus.downloading);
- await refSync.downloadPromozioni(
- lastUpdateStr,
- onProgress: _throttledProgress(9),
- );
- _setPhase(9, SyncPhaseStatus.completed);
- // Per-agent downloads
- final List<Agente> agenti = await agentiDao.listAll();
- final agentiCodici = agenti.map((a) => a.codAgente!).toList();
- state = SyncState(
- isSyncing: true,
- progress: state.progress,
- phases: state.phases,
- lastSyncTime: state.lastSyncTime,
- agentiCodici: agentiCodici,
- );
- for (final a in agenti) {
- _resetAgentPhases();
- _setCurrentAgent(a.codAgente!);
- // Phase 10: Scaricamento clienti
- _setPhase(10, SyncPhaseStatus.downloading);
- await clienteSync.downloadCustomers(
- a.idAgente,
- a.codAgente!,
- lastUpdateStr,
- onProgress: _throttledProgress(10),
- );
- _setPhase(10, SyncPhaseStatus.completed);
- // Phase 11: Scaricamento filiali
- _setPhase(11, SyncPhaseStatus.downloading);
- await filialeSync.downloadDestinations(
- a.idAgente,
- a.codAgente!,
- lastUpdateStr,
- onProgress: _throttledProgress(11),
- );
- _setPhase(11, SyncPhaseStatus.completed);
- // Phase 12: Scaricamento scadenze
- _setPhase(12, SyncPhaseStatus.downloading);
- await refSync.downloadScadenze(
- a.idAgente,
- a.codAgente!,
- lastUpdateStr,
- onProgress: _throttledProgress(12),
- );
- _setPhase(12, SyncPhaseStatus.completed);
- // Phase 13: Scaricamento listini cliente
- _setPhase(13, SyncPhaseStatus.downloading);
- await refSync.downloadListiniCliente(
- a.idAgente,
- a.codAgente!,
- lastUpdateStr,
- onProgress: _throttledProgress(13),
- );
- _setPhase(13, SyncPhaseStatus.completed);
- // Phase 14: Scaricamento sconti cliente
- _setPhase(14, SyncPhaseStatus.downloading);
- await refSync.downloadScontiCliente(
- a.idAgente,
- a.codAgente!,
- lastUpdateStr,
- onProgress: _throttledProgress(14),
- );
- _setPhase(14, SyncPhaseStatus.completed);
- }
- // Phase 15: Scaricamento statistiche
- _setPhase(15, SyncPhaseStatus.downloading);
- await refSync.downloadStatistiche(
- idAgente,
- codAgente,
- lastUpdateStr,
- onProgress: _throttledProgress(15),
- );
- _setPhase(15, SyncPhaseStatus.completed);
- // Download ordini remoti (no visual phase)
- for (final a in agenti) {
- await ordineSync.downloadRemoteOrders(
- a.idAgente,
- a.codAgente!,
- lastUpdateStr,
- );
- }
- // Download parametro
- await refSync.downloadParametro('PALLET_PERC');
- // Save timestamps
- final now = DateTime.now();
- final newLastUpdate = '${now.year}${_pad(now.month)}${_pad(now.day)}${_pad(now.hour)}${_pad(now.minute)}';
- await parametriDao.set(AppConstants.kLastUpdateParam, newLastUpdate);
- // await parametriDao.set(AppConstants.kLastUpdateParam, now.toIso8601String());
- state = SyncState(
- message: 'Sincronizzazione completata!',
- progress: 1.0,
- phases: state.phases,
- lastSyncTime: now,
- agentiCodici: state.agentiCodici,
- );
- ref.invalidate(articleSearchProvider);
- ref.invalidate(activeArticleSearchProvider);
- ref.invalidate(priceCodesProvider);
- ref.invalidate(customerListProvider);
- ref.invalidate(customerSearchProvider);
- ref.invalidate(customerDestinationsProvider);
- ref.invalidate(customerPaymentsProvider);
- ref.invalidate(ordersViewProvider);
- ref.invalidate(ordersSearchViewProvider);
- ref.invalidate(orderStatsProvider);
- ref.invalidate(orderProvider);
- ref.invalidate(dashboardProvider);
- ref.invalidate(paymentsProvider);
- } catch (e) {
- state = SyncState(
- error: e.toString(),
- message: 'Errore durante la sincronizzazione',
- );
- }
- }
- String _pad(int n) => n.toString().padLeft(2, '0');
- }
- final syncProvider = NotifierProvider<SyncNotifier, SyncState>(
- SyncNotifier.new,
- );
|