import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../../app/providers.dart'; import '../../../domain/entities/shift_report.dart'; import '../../../domain/entities/sync_event.dart'; import '../../../domain/repositories/sync_repository.dart'; import '../../auth/providers/auth_controller.dart'; import '../../pos/providers/catalog_providers.dart'; /// Bumped after every import so catalogue-backed providers refetch. final catalogueVersionProvider = StateProvider((ref) => 0); /// Bumped after every sale or sync so order-backed providers refetch. final orderVersionProvider = StateProvider((ref) => 0); /// Whether products exist on this terminal. Billing is gated on it. final catalogueReadyProvider = Provider((ref) { ref.watch(catalogueVersionProvider); return ref.watch(syncRepositoryProvider).hasCatalogue; }); final lastImportAtProvider = Provider((ref) { ref.watch(catalogueVersionProvider); return ref.watch(syncRepositoryProvider).lastImportAt; }); // ------------------------------------------------------- Morning: import sealed class ImportState { const ImportState(); } class ImportIdle extends ImportState { const ImportIdle(); } class ImportRunning extends ImportState { const ImportRunning(this.progress, this.stage); final double progress; final String stage; } class ImportDone extends ImportState { const ImportDone(this.event); final SyncEvent event; } class ImportFailed extends ImportState { const ImportFailed(this.message); final String message; } class CatalogueImportController extends StateNotifier { CatalogueImportController(this._ref) : super(const ImportIdle()); final Ref _ref; Future run() async { if (state is ImportRunning) return false; state = const ImportRunning(0, 'Starting…'); final event = await _ref.read(syncRepositoryProvider).importCatalogue( onProgress: (progress, stage) { if (mounted) state = ImportRunning(progress, stage); }, ); if (event.status == SyncStatus.synced) { _ref.read(catalogueVersionProvider.notifier).state++; _ref.invalidate(allProductsProvider); _ref.invalidate(visibleProductsProvider); _ref.invalidate(categoryCountsProvider); _ref.invalidate(lowStockProductsProvider); state = ImportDone(event); return true; } state = ImportFailed(event.error ?? 'Import failed.'); return false; } } final catalogueImportProvider = StateNotifierProvider( (ref) => CatalogueImportController(ref), ); // ---------------------------------------------------- Business hours: read /// Bills still held on this terminal at sync_status = 0. final unsyncedCountProvider = FutureProvider((ref) { ref.watch(orderVersionProvider); return ref.watch(syncRepositoryProvider).unsyncedCount(); }); /// Today's totals, read back from SQLite. final todayReportProvider = FutureProvider((ref) { ref.watch(orderVersionProvider); final session = ref.watch(cashierSessionProvider); final user = ref.watch(currentUserProvider); return ref.watch(syncRepositoryProvider).todayReport( terminalId: session.terminalId, cashierName: user?.name ?? session.name, ); }); /// Per-order sync state for the events log. final orderSyncRowsProvider = FutureProvider>((ref) { ref.watch(orderVersionProvider); return ref.watch(syncRepositoryProvider).orderSyncRows(); }); final syncEventsProvider = Provider>((ref) { ref.watch(orderVersionProvider); ref.watch(catalogueVersionProvider); return ref.watch(syncRepositoryProvider).events; }); // ------------------------------------------------------ End of day: upload sealed class OrderSyncState { const OrderSyncState(); } class SyncIdle extends OrderSyncState { const SyncIdle(); } class SyncRunning extends OrderSyncState { const SyncRunning(this.progress, this.stage); final double progress; final String stage; } class SyncFinished extends OrderSyncState { const SyncFinished(this.outcome); final SyncOutcome outcome; } class OrderSyncController extends StateNotifier { OrderSyncController(this._ref) : super(const SyncIdle()); final Ref _ref; bool get isRunning => state is SyncRunning; /// Uploads every bill at sync_status = 0 and flips the accepted ones to 1. Future run() async { if (isRunning) { return const SyncOutcome(attempted: 0, uploaded: 0); } state = const SyncRunning(0, 'Starting…'); final outcome = await _ref.read(syncRepositoryProvider).syncOrders( onProgress: (progress, stage) { if (mounted) state = SyncRunning(progress, stage); }, ); _ref.read(orderVersionProvider.notifier).state++; if (mounted) state = SyncFinished(outcome); return outcome; } void reset() => state = const SyncIdle(); } final orderSyncProvider = StateNotifierProvider( (ref) => OrderSyncController(ref), );