Files
OnBudget/lib/src/features/notification_parsing/data/parser/account_resolver.dart
T
SandersandClaude Opus 4.8 4f99b80169 Replace account_bindings with per-app default account; build analytics charts
Notification parsing:
- Drop the account_bindings table/DAO/repo/entity/controller/screen; account
  resolution now goes senderToAccount rule -> source_apps.defaultAccountId
  (trusted) -> global default (untrusted -> Inbox), via v1->v2 migration.
- Add defaultAccountId to source_apps; per-app settings consolidated into
  source_app_detail_screen (/settings/parsing/apps/:pkg).
- Inbox auto-learns an app default on first Confirm/CreateRule; account picker
  on the card instead of a disabled button; parse_error_labels extracted.

Analytics:
- Replace placeholder screen with fl_chart cards (chart_card, chart_theme,
  month_stepper, month_math domain helper); slim down habit_analysis_screen.

Android: add launcher icon (adaptive foreground + colors.xml) and app_name.

Tests: migration_v2, analytics (screen/month_math), AI retry, inbox
visibility; update resolver/gate/inbox suites for the new resolution path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 00:09:12 +03:00

82 lines
3.0 KiB
Dart

import '../../domain/entities/parse_rule.dart';
import 'rule_lookup.dart';
/// Откуда взят разрешённый счёт (для диагностики / подсветки).
enum AccountSource { senderRule, appDefault, globalDefault, none }
/// Результат разрешения счёта из уведомления (шаг 3 pipeline, §B).
class AccountResolution {
const AccountResolution({
required this.accountId,
required this.score,
required this.trusted,
required this.source,
});
/// Разрешённый счёт (null, если ничего не нашлось).
final String? accountId;
/// Confidence по счёту 0..100 (§8.2) — для подсветки «?», НЕ для gate.
final int score;
/// Можно ли доверять счёту для авто-применения. Именно это поле (а не score)
/// решает судьбу в gate: правило и дефолт приложения — осознанный выбор
/// пользователя → true; глобальный дефолт — лишь догадка → false → Inbox
/// с префиллом (первый Confirm выучит его как дефолт приложения).
final bool trusted;
final AccountSource source;
}
/// Разрешает `accountId` по лестнице источников (§B): правило senderToAccount
/// (паттерн в теле → счёт) → дефолтный счёт приложения → глобальный дефолтный
/// счёт (без доверия) → ничего.
AccountResolution resolveAccount({
required String body,
String? merchantRaw,
List<ParseRule> senderRules = const [],
String? appDefaultAccountId,
String? globalDefaultAccountId,
}) {
// #1 — senderToAccount-правило (матч по телу).
final senderRule =
findSenderRule(senderRules, body: body, merchantRaw: merchantRaw);
if (senderRule?.accountId != null) {
return AccountResolution(
accountId: senderRule!.accountId,
score: 90,
trusted: true,
source: AccountSource.senderRule,
);
}
// #2 — дефолтный счёт приложения (выбран пользователем / авто-обучен).
if (appDefaultAccountId != null) {
return AccountResolution(
accountId: appDefaultAccountId,
score: 75,
trusted: true,
source: AccountSource.appDefault,
);
}
// #3 — глобальный Account.isDefault: префилл для Inbox, но НЕ trusted —
// авто-применение требует явно подтверждённого счёта приложения.
if (globalDefaultAccountId != null) {
return AccountResolution(
accountId: globalDefaultAccountId,
score: 40,
trusted: false,
source: AccountSource.globalDefault,
);
}
// #4 — ничего.
return const AccountResolution(
accountId: null,
score: 15,
trusted: false,
source: AccountSource.none,
);
}