108 lines
4.5 KiB
Dart
108 lines
4.5 KiB
Dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:drift/drift.dart';
|
|
import '../data/db.dart';
|
|
export '../data/db.dart' show Account, CategoryGroup, Category, Transaction, Budget;
|
|
|
|
// Singleton DB provider
|
|
final dbProvider = Provider<AppDatabase>((ref) {
|
|
final db = AppDatabase();
|
|
ref.onDispose(() => db.close());
|
|
db.seedIfEmpty();
|
|
return db;
|
|
});
|
|
|
|
// ── Accounts ────────────────────────────────────────────────────────────
|
|
final accountsProvider = StreamProvider<List<Account>>((ref) {
|
|
final db = ref.watch(dbProvider);
|
|
return db.select(db.accounts).watch();
|
|
});
|
|
|
|
final openAccountsProvider = Provider<List<Account>>((ref) {
|
|
final accounts = ref.watch(accountsProvider).valueOrNull ?? [];
|
|
return accounts.where((a) => !a.isClosed).toList();
|
|
});
|
|
|
|
final totalBalanceProvider = Provider<double>((ref) {
|
|
final accounts = ref.watch(openAccountsProvider);
|
|
return accounts.fold(0.0, (sum, a) => sum + a.balance);
|
|
});
|
|
|
|
// ── Category Groups ─────────────────────────────────────────────────────
|
|
final categoryGroupsProvider = StreamProvider<List<CategoryGroup>>((ref) {
|
|
final db = ref.watch(dbProvider);
|
|
return (db.select(db.categoryGroups)
|
|
..orderBy([(g) => OrderingTerm(expression: g.sortOrder)]))
|
|
.watch();
|
|
});
|
|
|
|
// ── Categories ──────────────────────────────────────────────────────────
|
|
final categoriesProvider = StreamProvider<List<Category>>((ref) {
|
|
final db = ref.watch(dbProvider);
|
|
return (db.select(db.categories)
|
|
..orderBy([(c) => OrderingTerm(expression: c.sortOrder)]))
|
|
.watch();
|
|
});
|
|
|
|
final categoriesByGroupProvider = Provider<Map<int, List<Category>>>((ref) {
|
|
final cats = ref.watch(categoriesProvider).valueOrNull ?? [];
|
|
final map = <int, List<Category>>{};
|
|
for (final c in cats) {
|
|
map.putIfAbsent(c.groupId, () => []).add(c);
|
|
}
|
|
return map;
|
|
});
|
|
|
|
// ── Transactions ────────────────────────────────────────────────────────
|
|
final allTransactionsProvider = StreamProvider<List<Transaction>>((ref) {
|
|
final db = ref.watch(dbProvider);
|
|
return (db.select(db.transactions)
|
|
..orderBy([(t) => OrderingTerm(expression: t.date, mode: OrderingMode.desc)]))
|
|
.watch();
|
|
});
|
|
|
|
final recentTransactionsProvider = Provider<List<Transaction>>((ref) {
|
|
final all = ref.watch(allTransactionsProvider).valueOrNull ?? [];
|
|
return all.take(10).toList();
|
|
});
|
|
|
|
final monthlyTransactionsProvider = Provider.family<List<Transaction>, String>((ref, month) {
|
|
final all = ref.watch(allTransactionsProvider).valueOrNull ?? [];
|
|
return all.where((t) {
|
|
final ym = '${t.date.year}-${t.date.month.toString().padLeft(2, '0')}';
|
|
return ym == month;
|
|
}).toList();
|
|
});
|
|
|
|
// ── Budget ──────────────────────────────────────────────────────────────
|
|
final budgetsProvider = StreamProvider<List<Budget>>((ref) {
|
|
final db = ref.watch(dbProvider);
|
|
return db.select(db.budgets).watch();
|
|
});
|
|
|
|
final currentMonthProvider = Provider<String>((ref) {
|
|
final now = DateTime.now();
|
|
return '${now.year}-${now.month.toString().padLeft(2, '0')}';
|
|
});
|
|
|
|
final monthlyBudgetProvider = Provider.family<double?, int>((ref, categoryId) {
|
|
final month = ref.watch(currentMonthProvider);
|
|
final budgets = ref.watch(budgetsProvider).valueOrNull ?? [];
|
|
final match = budgets.where(
|
|
(b) => b.categoryId == categoryId && b.month == month,
|
|
);
|
|
return match.isNotEmpty ? match.first.budgeted : null;
|
|
});
|
|
|
|
final categorySpentProvider = Provider.family<double, int>((ref, categoryId) {
|
|
final month = ref.watch(currentMonthProvider);
|
|
final txs = ref.watch(monthlyTransactionsProvider(month));
|
|
final filtered = txs.where((t) => t.categoryId == categoryId);
|
|
return filtered.fold(0.0, (sum, t) => sum + t.amount);
|
|
});
|
|
|
|
final categoryRemainingProvider = Provider.family<double, int>((ref, categoryId) {
|
|
final budgeted = ref.watch(monthlyBudgetProvider(categoryId)) ?? 0.0;
|
|
final spent = ref.watch(categorySpentProvider(categoryId));
|
|
return budgeted + spent; // spent is negative, so budgeted + spent = remaining
|
|
});
|