Files
ingtrakapp/lib/models/bank.dart
T

128 lines
3.4 KiB
Dart

/// Domain models for the personal ingtrak bank-data API.
library;
import 'transaction_meta.dart' show merchantKeyFromDescription;
/// Result of `POST /api/connect`.
class ConnectStart {
const ConnectStart({required this.authUrl, required this.state});
final String authUrl;
final String state;
factory ConnectStart.fromJson(Map<String, dynamic> json) {
return ConnectStart(
authUrl: json['authUrl'] as String? ?? '',
state: json['state'] as String? ?? '',
);
}
}
/// Link status from `/api/connect/status` or `/api/connect/complete`.
class ConnectStatus {
const ConnectStatus({
required this.status,
required this.linked,
this.accountId,
this.sessionId,
this.state,
this.validUntil,
});
final String status;
final bool linked;
final String? accountId;
final String? sessionId;
final String? state;
final String? validUntil;
factory ConnectStatus.fromJson(Map<String, dynamic> json) {
return ConnectStatus(
status: json['status'] as String? ?? 'unknown',
linked: json['linked'] as bool? ?? false,
accountId: json['accountId'] as String?,
sessionId: json['sessionId'] as String?,
state: json['state'] as String?,
validUntil: json['validUntil'] as String?,
);
}
static const disconnected = ConnectStatus(
status: 'disconnected',
linked: false,
);
}
/// `GET /api/balance` payload.
class BankBalance {
const BankBalance({
required this.amount,
required this.currency,
this.balanceType,
});
final double amount;
final String currency;
final String? balanceType;
factory BankBalance.fromJson(Map<String, dynamic> json) {
final rawAmount = json['amount'];
final amount = switch (rawAmount) {
num n => n.toDouble(),
String s => double.tryParse(s) ?? 0,
_ => 0.0,
};
return BankBalance(
amount: amount,
currency: json['currency'] as String? ?? 'EUR',
balanceType: json['balanceType'] as String?,
);
}
}
/// One row from `GET /api/transactions`.
class BankTransaction {
const BankTransaction({
required this.date,
required this.description,
required this.amount,
required this.currency,
required this.pending,
});
final String date;
final String description;
final double amount;
final String currency;
final bool pending;
/// Stable local key used for per-occurrence annotations.
String get id => '$date|$currency|$amount|$description';
/// Match key for auto-category (same merchant / description).
String get descriptionKey =>
description.trim().toLowerCase().replaceAll(RegExp(r'\s+'), ' ');
/// Fuzzy merchant key — strips dates / terminal / SEPA noise.
String get merchantKey => merchantKeyFromDescription(description);
/// Exact match for copying notes (same description + amount + currency).
String get exactKey => '$descriptionKey|$amount|$currency';
factory BankTransaction.fromJson(Map<String, dynamic> json) {
final rawAmount = json['amount'];
final amount = switch (rawAmount) {
num n => n.toDouble(),
String s => double.tryParse(s) ?? 0,
_ => 0.0,
};
return BankTransaction(
date: json['date'] as String? ?? '',
description: json['description'] as String? ?? 'Unknown',
amount: amount,
currency: json['currency'] as String? ?? 'EUR',
pending: json['pending'] as bool? ?? false,
);
}
}