fix recurrent spendings
This commit is contained in:
@@ -642,7 +642,7 @@ class TxMetaController extends ChangeNotifier {
|
||||
_schedulePersist();
|
||||
}
|
||||
|
||||
/// Recurring look-ahead templates (manual + auto from marked txs).
|
||||
/// Explicit look-ahead recurring spendings (never auto-linked from tx flags).
|
||||
List<RecurringTemplate> get recurringTemplates {
|
||||
final list = _recurringTemplates.values.toList(growable: false);
|
||||
list.sort((a, b) {
|
||||
@@ -656,6 +656,14 @@ class TxMetaController extends ChangeNotifier {
|
||||
RecurringTemplate? recurringTemplateById(String id) =>
|
||||
_recurringTemplates[id];
|
||||
|
||||
/// True when a spending already references this bank transaction.
|
||||
bool hasRecurringSpendingForTx(String txId) {
|
||||
for (final tpl in _recurringTemplates.values) {
|
||||
if (tpl.sourceTxId == txId) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void upsertRecurringTemplate(RecurringTemplate template) {
|
||||
if (template.id.isEmpty) return;
|
||||
_recurringTemplates[template.id] = template;
|
||||
@@ -668,42 +676,23 @@ class TxMetaController extends ChangeNotifier {
|
||||
_schedulePersist(invalidateLabels: false);
|
||||
}
|
||||
|
||||
/// Backfill auto templates for txs already marked recurring (upgrade path).
|
||||
void ensureAutoRecurringTemplates(List<BankTransaction> transactions) {
|
||||
var changed = false;
|
||||
final recurringIds = <String>{};
|
||||
for (final tx in transactions) {
|
||||
if (!metaFor(tx).recurring) continue;
|
||||
recurringIds.add(tx.id);
|
||||
final id = RecurringTemplate.autoIdForTx(tx.id);
|
||||
if (_recurringTemplates.containsKey(id)) continue;
|
||||
_recurringTemplates[id] = RecurringTemplate(
|
||||
id: id,
|
||||
name: tx.description,
|
||||
dayOfMonth: (DateTime.tryParse(tx.date)?.day ?? 1).clamp(1, 28),
|
||||
currency: tx.currency,
|
||||
source: RecurringTemplateSource.transaction,
|
||||
amountRule: RecurringAmountRule.lastValue,
|
||||
merchantKey: tx.merchantKey,
|
||||
sourceTxId: tx.id,
|
||||
avgCount: 5,
|
||||
autoFromRecurring: true,
|
||||
);
|
||||
changed = true;
|
||||
}
|
||||
// Drop orphaned auto templates whose source is no longer recurring.
|
||||
final orphanIds = [
|
||||
for (final e in _recurringTemplates.entries)
|
||||
if (e.value.autoFromRecurring &&
|
||||
(e.value.sourceTxId == null ||
|
||||
!recurringIds.contains(e.value.sourceTxId)))
|
||||
e.key,
|
||||
];
|
||||
for (final id in orphanIds) {
|
||||
_recurringTemplates.remove(id);
|
||||
changed = true;
|
||||
}
|
||||
if (changed) _schedulePersist(invalidateLabels: false);
|
||||
/// Create a spending seeded from [tx] (last-value amount rule).
|
||||
RecurringTemplate addRecurringSpendingFromTx(BankTransaction tx) {
|
||||
final id = 'rt_${DateTime.now().microsecondsSinceEpoch.toRadixString(16)}';
|
||||
final tpl = RecurringTemplate(
|
||||
id: id,
|
||||
name: tx.description,
|
||||
dayOfMonth: (DateTime.tryParse(tx.date)?.day ?? 1).clamp(1, 28),
|
||||
currency: tx.currency,
|
||||
source: RecurringTemplateSource.transaction,
|
||||
amountRule: RecurringAmountRule.lastValue,
|
||||
merchantKey: tx.merchantKey,
|
||||
sourceTxId: tx.id,
|
||||
avgCount: 5,
|
||||
autoFromRecurring: false,
|
||||
);
|
||||
upsertRecurringTemplate(tpl);
|
||||
return tpl;
|
||||
}
|
||||
|
||||
Future<void> setRecurring(BankTransaction tx, bool recurring) async {
|
||||
@@ -715,39 +704,6 @@ class TxMetaController extends ChangeNotifier {
|
||||
recurring: recurring,
|
||||
internalTransfer: existing.internalTransfer || current.internalTransfer,
|
||||
);
|
||||
if (recurring) {
|
||||
final id = RecurringTemplate.autoIdForTx(tx.id);
|
||||
final prev = _recurringTemplates[id];
|
||||
_recurringTemplates[id] = RecurringTemplate(
|
||||
id: id,
|
||||
name: prev?.name ?? tx.description,
|
||||
dayOfMonth: prev?.dayOfMonth ??
|
||||
(DateTime.tryParse(tx.date)?.day ?? 1).clamp(1, 28),
|
||||
currency: prev?.currency ?? tx.currency,
|
||||
source: RecurringTemplateSource.transaction,
|
||||
amountRule: prev?.amountRule ?? RecurringAmountRule.lastValue,
|
||||
merchantKey: prev?.merchantKey.isNotEmpty == true
|
||||
? prev!.merchantKey
|
||||
: tx.merchantKey,
|
||||
sourceTxId: tx.id,
|
||||
labelId: prev?.labelId,
|
||||
avgCount: prev?.avgCount ?? 5,
|
||||
autoFromRecurring: true,
|
||||
);
|
||||
} else {
|
||||
final id = RecurringTemplate.autoIdForTx(tx.id);
|
||||
final existingTpl = _recurringTemplates[id];
|
||||
if (existingTpl != null && existingTpl.autoFromRecurring) {
|
||||
_recurringTemplates.remove(id);
|
||||
}
|
||||
// Also drop auto templates that still point at this tx under another id.
|
||||
_recurringTemplates.removeWhere(
|
||||
(_, tpl) =>
|
||||
tpl.autoFromRecurring &&
|
||||
tpl.sourceTxId == tx.id &&
|
||||
tpl.id != id,
|
||||
);
|
||||
}
|
||||
_schedulePersist(invalidateLabels: false);
|
||||
}
|
||||
|
||||
@@ -835,10 +791,6 @@ class TxMetaController extends ChangeNotifier {
|
||||
_schedulePersist();
|
||||
}
|
||||
|
||||
Future<void> toggleRecurring(BankTransaction tx) {
|
||||
return setRecurring(tx, !metaFor(tx).recurring);
|
||||
}
|
||||
|
||||
Future<void> toggleInternalTransfer(BankTransaction tx) {
|
||||
return setInternalTransfer(tx, !metaFor(tx).internalTransfer);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user