193 lines
4.9 KiB
Dart
193 lines
4.9 KiB
Dart
/// 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<List<InvoiceModel>> build() async {
|
|
return await ref.read(invoicesLocalDataSourceProvider).getAllInvoices();
|
|
}
|
|
|
|
/// Refresh invoices
|
|
Future<void> 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<List<InvoiceModel>> 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<Map<String, int>> invoicesCountByStatus(Ref ref) async {
|
|
final invoicesAsync = ref.watch(invoicesProvider);
|
|
|
|
return invoicesAsync.when(
|
|
data: (invoices) {
|
|
final counts = <String, int>{};
|
|
|
|
// 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<double> totalInvoicesAmount(Ref ref) async {
|
|
final invoicesAsync = ref.watch(invoicesProvider);
|
|
|
|
return invoicesAsync.when(
|
|
data: (invoices) {
|
|
return invoices.fold<double>(
|
|
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<double> totalUnpaidAmount(Ref ref) async {
|
|
final invoicesAsync = ref.watch(invoicesProvider);
|
|
|
|
return invoicesAsync.when(
|
|
data: (invoices) {
|
|
return invoices.fold<double>(
|
|
0.0,
|
|
(sum, invoice) => sum + invoice.amountRemaining,
|
|
);
|
|
},
|
|
loading: () => 0.0,
|
|
error: (error, stack) => 0.0,
|
|
);
|
|
}
|