/// Providers: Invoices /// /// Riverpod providers for managing invoices state. library; import 'package:riverpod_annotation/riverpod_annotation.dart'; import 'package:worker/core/database/models/enums.dart'; import 'package:worker/features/orders/data/datasources/invoices_local_datasource.dart'; import 'package:worker/features/orders/data/models/invoice_model.dart'; part 'invoices_provider.g.dart'; /// Invoices Local Data Source Provider @riverpod InvoicesLocalDataSource invoicesLocalDataSource(Ref ref) { return InvoicesLocalDataSource(); } /// Invoices Provider /// /// Provides list of all invoices from local data source. @riverpod class Invoices extends _$Invoices { @override Future> build() async { return await ref.read(invoicesLocalDataSourceProvider).getAllInvoices(); } /// Refresh invoices Future refresh() async { state = const AsyncValue.loading(); state = await AsyncValue.guard(() async { return await ref.read(invoicesLocalDataSourceProvider).getAllInvoices(); }); } } /// Selected Invoice Status Filter Provider /// /// Tracks the currently selected invoice status filter for tabs. /// null means "All" invoices. @riverpod class SelectedInvoiceStatusFilter extends _$SelectedInvoiceStatusFilter { @override String? build() { return null; // Default: show all invoices } /// Select a status filter void selectStatus(String? status) { state = status; } /// Clear selection (show all) void clearSelection() { state = null; } } /// Filtered Invoices Provider /// /// Filters invoices by selected status tab. @riverpod Future> filteredInvoices(Ref ref) async { final invoicesAsync = ref.watch(invoicesProvider); final selectedStatus = ref.watch(selectedInvoiceStatusFilterProvider); return invoicesAsync.when( data: (invoices) { var filtered = invoices; // Filter by status tab if (selectedStatus != null) { if (selectedStatus == 'unpaid') { // Unpaid tab: issued status only filtered = filtered .where( (invoice) => invoice.status == InvoiceStatus.issued && !invoice.isPaid, ) .toList(); } else if (selectedStatus == 'overdue') { // Overdue tab: overdue status filtered = filtered .where( (invoice) => invoice.status == InvoiceStatus.overdue || invoice.isOverdue, ) .toList(); } else if (selectedStatus == 'paid') { // Paid tab: paid status filtered = filtered .where( (invoice) => invoice.status == InvoiceStatus.paid || invoice.isPaid, ) .toList(); } } // Sort by issue date (newest first) filtered.sort((a, b) => b.issueDate.compareTo(a.issueDate)); return filtered; }, loading: () => [], error: (error, stack) => [], ); } /// Invoices Count by Status Provider /// /// Returns count of invoices for each status tab. @riverpod Future> invoicesCountByStatus(Ref ref) async { final invoicesAsync = ref.watch(invoicesProvider); return invoicesAsync.when( data: (invoices) { final counts = {}; // All tab counts['all'] = invoices.length; // Unpaid tab (issued status) counts['unpaid'] = invoices .where( (invoice) => invoice.status == InvoiceStatus.issued && !invoice.isPaid, ) .length; // Overdue tab counts['overdue'] = invoices .where( (invoice) => invoice.status == InvoiceStatus.overdue || invoice.isOverdue, ) .length; // Paid tab counts['paid'] = invoices .where( (invoice) => invoice.status == InvoiceStatus.paid || invoice.isPaid, ) .length; return counts; }, loading: () => {}, error: (error, stack) => {}, ); } /// Total Invoices Amount Provider /// /// Returns total amount of all invoices. @riverpod Future totalInvoicesAmount(Ref ref) async { final invoicesAsync = ref.watch(invoicesProvider); return invoicesAsync.when( data: (invoices) { return invoices.fold( 0.0, (sum, invoice) => sum + invoice.totalAmount, ); }, loading: () => 0.0, error: (error, stack) => 0.0, ); } /// Total Unpaid Amount Provider /// /// Returns total amount remaining across all unpaid invoices. @riverpod Future totalUnpaidAmount(Ref ref) async { final invoicesAsync = ref.watch(invoicesProvider); return invoicesAsync.when( data: (invoices) { return invoices.fold( 0.0, (sum, invoice) => sum + invoice.amountRemaining, ); }, loading: () => 0.0, error: (error, stack) => 0.0, ); }