123 lines
3.2 KiB
Dart
123 lines
3.2 KiB
Dart
/// Domain models for the personal ingtrak bank-data API.
|
|
library;
|
|
|
|
/// 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+'), ' ');
|
|
|
|
/// 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,
|
|
);
|
|
}
|
|
}
|