import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/widgets/async_value_view.dart'; import '../../core/widgets/empty_state.dart'; import '../../core/widgets/money_text.dart'; import '../../core/widgets/section_card.dart'; import '../portfolio/labels.dart' show formatPercent, signColor; import 'data/tax_api.dart'; import 'providers.dart'; /// Сводка: the estimated tax picture for the year, per account and in total. class TaxSummaryTab extends ConsumerWidget { const TaxSummaryTab({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final summary = ref.watch(taxSummaryProvider); return RefreshIndicator( onRefresh: () async => ref.invalidate(taxSummaryProvider), child: AsyncValueView( value: summary, onRetry: () => ref.invalidate(taxSummaryProvider), data: (cached) { final data = cached.data; final totals = data.totals; return ListView( padding: const EdgeInsets.all(16), children: [ Row( children: [ Text('${data.year} год', style: Theme.of(context).textTheme.titleLarge), const SizedBox(width: 12), if (data.estimated) const Chip( avatar: Icon(Icons.calculate_outlined, size: 16), label: Text('оценка'), visualDensity: VisualDensity.compact, ), const Spacer(), if (data.taxRate != null) Text('ставка ${formatPercent(data.taxRate, signed: false)}'), ], ), const SizedBox(height: 12), if (totals != null) ...[ Wrap( spacing: 12, runSpacing: 12, children: [ StatTile( label: 'Оценка налога', value: _money(totals.estimatedTaxRub), note: 'по всем счетам за год', ), StatTile( label: 'Удержано брокером', value: _money(totals.taxWithheldRub), note: 'по данным операций', ), StatTile( label: 'Налоговая база', value: _money(totals.taxableBaseRub), note: 'после вычета ЛДВ', ), StatTile( label: 'Освобождено по ЛДВ', value: _money(totals.ldvExemptRub), note: 'оценка по ст. 219.1', ), StatTile( label: 'Дивиденды и купоны', value: _money(totals.dividendsGrossRub), note: 'купоны ${_moneyText(totals.couponsGrossRub)}', ), StatTile( label: 'Реализовано', value: _money(totals.realizedGainRub), note: 'убыток ${_moneyText(totals.realizedLossRub)}', ), ], ), const SizedBox(height: 16), ], if (data.accounts.isEmpty) const Padding( padding: EdgeInsets.only(top: 32), child: EmptyState( icon: Icons.receipt_long_outlined, message: 'За этот год нет ни сделок, ни выплат.', ), ) else SectionCard( title: 'По счетам', subtitle: 'все суммы — оценка; авторитет — справка брокера', child: _AccountsTable(rows: data.accounts, totals: totals), ), ], ); }, ), ); } static Widget _money(String? value) => value == null ? const Text('—') : MoneyText(value, currency: 'RUB'); static String _moneyText(String? value) => value == null ? '—' : MoneyText.format(value, 'RUB'); } class _AccountsTable extends StatelessWidget { const _AccountsTable({required this.rows, this.totals}); final List rows; final TaxRow? totals; @override Widget build(BuildContext context) { final headerStyle = Theme.of(context).textTheme.labelMedium; return SingleChildScrollView( scrollDirection: Axis.horizontal, child: DataTable( columns: [ DataColumn(label: Text('Счёт', style: headerStyle)), DataColumn(label: Text('Дивиденды', style: headerStyle), numeric: true), DataColumn(label: Text('Купоны', style: headerStyle), numeric: true), DataColumn(label: Text('Удержано', style: headerStyle), numeric: true), DataColumn(label: Text('Прибыль', style: headerStyle), numeric: true), DataColumn(label: Text('Убыток', style: headerStyle), numeric: true), DataColumn(label: Text('ЛДВ', style: headerStyle), numeric: true), DataColumn(label: Text('База', style: headerStyle), numeric: true), DataColumn(label: Text('Налог (оценка)', style: headerStyle), numeric: true), ], rows: [ for (final r in rows) _row(context, r, bold: false), if (totals != null) _row(context, totals!, bold: true), ], ), ); } DataRow _row(BuildContext context, TaxRow r, {required bool bold}) { final style = bold ? Theme.of(context).textTheme.titleSmall : null; Widget cell(String? v, {bool signed = false}) => v == null ? Text('—', style: style) : MoneyText( v, currency: 'RUB', style: signed ? (style ?? const TextStyle()).copyWith(color: signColor(context, v)) : style, ); return DataRow( cells: [ DataCell(Text(bold ? 'Итого' : r.title, style: style)), DataCell(cell(r.dividendsGrossRub)), DataCell(cell(r.couponsGrossRub)), DataCell(cell(r.taxWithheldRub)), DataCell(cell(r.realizedGainRub, signed: true)), DataCell(cell(r.realizedLossRub, signed: true)), DataCell(cell(r.ldvExemptRub)), DataCell(cell(r.taxableBaseRub)), DataCell(cell(r.estimatedTaxRub)), ], ); } }