Доходы, ребалансировка, налоги, импорт, цели, здоровье данных, правила, транзакции, категории, cashflow, pending: новые виджеты и токены темы, dart format. Тесты подстроены под новые модели.
108 lines
3.3 KiB
Dart
108 lines
3.3 KiB
Dart
import 'package:fintracker_api/fintracker_api.dart';
|
|
import 'package:flutter/material.dart';
|
|
|
|
import '../../core/utils/ru_date.dart';
|
|
import '../../core/widgets/money_text.dart';
|
|
|
|
/// The amount/currency/RUB-equivalent a transaction is shown with, chosen by
|
|
/// its [FlowType]: the outgoing leg for expenses and transfers out, the
|
|
/// incoming leg for income.
|
|
({String amount, String currency, String? rub}) primaryAmount(
|
|
TransactionOut t,
|
|
) {
|
|
switch (t.flowType) {
|
|
case FlowType.income:
|
|
return (
|
|
amount: t.income,
|
|
currency: t.incomeCurrency ?? 'RUB',
|
|
rub: t.incomeRub,
|
|
);
|
|
case FlowType.expense:
|
|
return (
|
|
amount: t.outcome,
|
|
currency: t.outcomeCurrency ?? 'RUB',
|
|
rub: t.outcomeRub,
|
|
);
|
|
case FlowType.internalTransfer:
|
|
case FlowType.savingsTransfer:
|
|
case FlowType.brokerExternalFlow:
|
|
case FlowType.other:
|
|
case FlowType.deleted:
|
|
case FlowType.unknownDefaultOpenApi:
|
|
if (t.outcome != '0' && t.outcomeCurrency != null) {
|
|
return (
|
|
amount: t.outcome,
|
|
currency: t.outcomeCurrency!,
|
|
rub: t.outcomeRub,
|
|
);
|
|
}
|
|
return (
|
|
amount: t.income,
|
|
currency: t.incomeCurrency ?? 'RUB',
|
|
rub: t.incomeRub,
|
|
);
|
|
}
|
|
}
|
|
|
|
(IconData, Color) flowTypeIcon(FlowType type, ColorScheme scheme) =>
|
|
switch (type) {
|
|
FlowType.income => (Icons.arrow_circle_down_outlined, Colors.green),
|
|
FlowType.expense => (Icons.arrow_circle_up_outlined, scheme.error),
|
|
FlowType.internalTransfer => (Icons.swap_horiz, scheme.primary),
|
|
FlowType.savingsTransfer => (
|
|
Icons.savings_outlined,
|
|
Colors.amber.shade800,
|
|
),
|
|
FlowType.brokerExternalFlow => (Icons.trending_up, Colors.deepPurple),
|
|
FlowType.other ||
|
|
FlowType.deleted ||
|
|
FlowType.unknownDefaultOpenApi => (Icons.help_outline, scheme.outline),
|
|
};
|
|
|
|
/// One row of the Операции list: date, payee, category, native amount with
|
|
/// its RUB equivalent when the currency differs, and a flow-type icon.
|
|
class TransactionRow extends StatelessWidget {
|
|
const TransactionRow({
|
|
required this.transaction,
|
|
required this.categoryName,
|
|
required this.onTap,
|
|
super.key,
|
|
});
|
|
|
|
final TransactionOut transaction;
|
|
final String? categoryName;
|
|
final VoidCallback onTap;
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final t = transaction;
|
|
final scheme = Theme.of(context).colorScheme;
|
|
final (icon, color) = flowTypeIcon(t.flowType, scheme);
|
|
final amount = primaryAmount(t);
|
|
final showRub = amount.currency != 'RUB' && amount.rub != null;
|
|
final payee = t.payeeCanonical?.isNotEmpty == true
|
|
? t.payeeCanonical!
|
|
: (t.payee ?? '—');
|
|
|
|
return ListTile(
|
|
onTap: onTap,
|
|
leading: Icon(icon, color: color),
|
|
title: Text(payee),
|
|
subtitle: Text([ruDate(t.date), ?categoryName].join(' · ')),
|
|
trailing: Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
crossAxisAlignment: CrossAxisAlignment.end,
|
|
children: [
|
|
MoneyText(amount.amount, currency: amount.currency),
|
|
if (showRub)
|
|
Text(
|
|
MoneyText.format(amount.rub!, 'RUB'),
|
|
style: Theme.of(context).textTheme.bodySmall
|
|
?.copyWith(color: scheme.outline),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
}
|