LCOV - code coverage report
Current view: top level - lib/pages/bill/widgets - expenses_tab.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 128 130 98.5 %
Date: 2024-10-04 11:12:13 Functions: 0 0 -

          Line data    Source code
       1             : // Copyright 2023 The terCAD team. All rights reserved.
       2             : // Use of this source code is governed by a CC BY-NC-ND 4.0 license that can be found in the LICENSE file.
       3             : 
       4             : import 'package:app_finance/_classes/controller/exchange_controller.dart';
       5             : import 'package:app_finance/_classes/herald/app_design.dart';
       6             : import 'package:app_finance/_classes/herald/app_locale.dart';
       7             : import 'package:app_finance/_classes/structure/currency/exchange.dart';
       8             : import 'package:app_finance/_classes/structure/navigation/app_route.dart';
       9             : import 'package:app_finance/_classes/structure/bill_app_data.dart';
      10             : import 'package:app_finance/_classes/controller/focus_controller.dart';
      11             : import 'package:app_finance/_classes/storage/app_preferences.dart';
      12             : import 'package:app_finance/_classes/storage/app_data.dart';
      13             : import 'package:app_finance/_configs/account_type.dart';
      14             : import 'package:app_finance/_configs/screen_helper.dart';
      15             : import 'package:app_finance/_configs/theme_helper.dart';
      16             : import 'package:app_finance/_ext/build_context_ext.dart';
      17             : import 'package:app_finance/_ext/double_ext.dart';
      18             : import 'package:app_finance/design/wrapper/input_wrapper.dart';
      19             : import 'package:app_finance/pages/bill/widgets/interface_bill_page_inject.dart';
      20             : import 'package:app_finance/design/form/currency_exchange_input.dart';
      21             : import 'package:app_finance/design/form/date_time_input.dart';
      22             : import 'package:app_finance/design/button/full_sized_button_widget.dart';
      23             : import 'package:app_finance/design/form/list_account_selector.dart';
      24             : import 'package:app_finance/design/form/simple_input.dart';
      25             : import 'package:app_finance/design/wrapper/row_widget.dart';
      26             : import 'package:app_finance/design/wrapper/single_scroll_wrapper.dart';
      27             : import 'package:flutter/material.dart';
      28             : import 'package:flutter_currency_picker/flutter_currency_picker.dart';
      29             : 
      30             : class ExpensesTab<T> extends StatefulWidget {
      31             :   final String? account;
      32             :   final String? budget;
      33             :   final Currency? currency;
      34             :   final double? bill;
      35             :   final String? description;
      36             :   final DateTime? createdAt;
      37             :   final AppData state;
      38             :   final bool isLeft;
      39             :   final FnBillPageCallback callback;
      40             : 
      41           1 :   const ExpensesTab({
      42             :     super.key,
      43             :     required this.state,
      44             :     required this.callback,
      45             :     this.account,
      46             :     this.budget,
      47             :     this.currency,
      48             :     this.bill,
      49             :     this.description,
      50             :     this.createdAt,
      51             :     this.isLeft = false,
      52             :   });
      53             : 
      54           1 :   @override
      55           1 :   ExpensesTabState createState() => ExpensesTabState();
      56             : }
      57             : 
      58             : class ExpensesTabState<T extends ExpensesTab> extends State<T> {
      59             :   late FocusController focus;
      60             :   String? account;
      61             :   Currency? accountCurrency;
      62             :   String? budget;
      63             :   Currency? budgetCurrency;
      64             :   Currency? currency;
      65             :   late TextEditingController bill;
      66             :   late TextEditingController description;
      67             :   late ExchangeController exchange;
      68             :   DateTime? createdAt;
      69             :   bool hasErrors = false;
      70             :   bool isPushed = false;
      71           3 :   late List<ListAccountSelectorItem> accountList = widget.state
      72           1 :       .getList(AppDataType.accounts)
      73           7 :       .where((e) => ![AppAccountType.deposit.toString(), AppAccountType.credit.toString()].contains(e.type))
      74           3 :       .map((item) => ListAccountSelectorItem(item: item))
      75           1 :       .toList();
      76             : 
      77           1 :   @override
      78             :   void initState() {
      79           2 :     focus = FocusController();
      80           1 :     final accountId = AppPreferences.get(AppPreferences.prefAccount);
      81           3 :     final objAccount = widget.state.getByUuid(accountId ?? '');
      82           1 :     final budgetId = AppPreferences.get(AppPreferences.prefBudget);
      83           3 :     final objBudget = widget.state.getByUuid(budgetId ?? '');
      84           3 :     account = widget.account ?? objAccount?.uuid;
      85           3 :     budget = widget.budget ?? objBudget?.uuid;
      86           3 :     currency = widget.currency ?? objAccount?.currency ?? objBudget?.currency ?? Exchange.defaultCurrency;
      87           4 :     bill = TextEditingController(text: widget.bill != null ? widget.bill.toString() : '');
      88           4 :     description = TextEditingController(text: widget.description);
      89           3 :     createdAt = widget.createdAt;
      90           5 :     accountCurrency = widget.state.getByUuid(account ?? '')?.currency;
      91           5 :     budgetCurrency = widget.state.getByUuid(budget ?? '')?.currency;
      92           3 :     exchange = ExchangeController({},
      93           7 :         store: widget.state, targetController: bill, target: currency, source: [accountCurrency, budgetCurrency]);
      94             : 
      95           3 :     widget.callback((
      96           1 :       buildButton: buildButton,
      97           1 :       buttonName: getButtonName(),
      98           1 :       title: getTitle(),
      99             :     ));
     100           1 :     super.initState();
     101             :   }
     102             : 
     103           1 :   @override
     104             :   dispose() {
     105           1 :     isPushed = false;
     106           2 :     description.dispose();
     107           2 :     exchange.dispose();
     108           2 :     bill.dispose();
     109           2 :     focus.dispose();
     110           1 :     super.dispose();
     111             :   }
     112             : 
     113           3 :   String getTitle() => AppLocale.labels.createBillHeader;
     114             : 
     115           1 :   bool hasFormErrors() {
     116           8 :     setState(() => hasErrors = account == null || budget == null || bill.text.isEmpty);
     117           1 :     return hasErrors;
     118             :   }
     119             : 
     120           1 :   void updateStorage() {
     121           2 :     AppPreferences.set(AppPreferences.prefAccount, account ?? '');
     122           2 :     AppPreferences.set(AppPreferences.prefBudget, budget ?? '');
     123           1 :     if (currency != null) {
     124           2 :       CurrencyProvider.pin(currency!);
     125             :     }
     126           2 :     exchange.save();
     127           4 :     widget.state.add(BillAppData(
     128           1 :       account: account ?? '',
     129           1 :       category: budget ?? '',
     130           1 :       currency: currency,
     131           2 :       title: description.text,
     132           5 :       details: double.tryParse(bill.text)?.toFixed(currency?.decimalDigits) ?? 0.0,
     133           2 :       createdAt: createdAt ?? DateTime.now(),
     134             :     ));
     135             :   }
     136             : 
     137           3 :   String getButtonName() => AppLocale.labels.createBillTooltip;
     138             : 
     139           1 :   Widget buildButton(BuildContext context, BoxConstraints constraints) {
     140           1 :     NavigatorState nav = Navigator.of(context);
     141           1 :     return FullSizedButtonWidget(
     142             :       constraints: constraints,
     143           1 :       controller: focus,
     144           1 :       onPressed: () => {
     145           2 :         setState(() {
     146           1 :           if (hasFormErrors()) {
     147             :             return;
     148             :           }
     149           1 :           updateStorage();
     150           1 :           nav.popAndPushNamed(AppRoute.homeRoute);
     151             :         })
     152             :       },
     153           1 :       title: getButtonName(),
     154             :       icon: Icons.save,
     155             :     );
     156             :   }
     157             : 
     158           1 :   @override
     159             :   Widget build(BuildContext context) {
     160           1 :     final textTheme = context.textTheme;
     161           1 :     final indent = ThemeHelper.getIndent(2);
     162           3 :     double width = ScreenHelper.state().width - indent * 3;
     163           2 :     if (widget.isLeft) {
     164           0 :       width -= ThemeHelper.barHeight;
     165             :     }
     166             : 
     167           1 :     return SingleScrollWrapper(
     168           1 :       controller: focus,
     169           1 :       child: Container(
     170           1 :         margin: EdgeInsets.fromLTRB(indent, indent, indent, 240),
     171           1 :         child: Column(
     172           1 :           crossAxisAlignment: AppDesign.getAlignment(),
     173           1 :           children: [
     174           1 :             InputWrapper(
     175             :               type: NamedInputType.accountSelector,
     176             :               isRequired: true,
     177           5 :               value: account != null ? widget.state.getByUuid(account!) : null,
     178           2 :               title: AppLocale.labels.account,
     179           2 :               tooltip: AppLocale.labels.titleAccountTooltip,
     180           1 :               showError: hasErrors && account == null,
     181           2 :               state: widget.state,
     182           1 :               options: accountList,
     183           3 :               onChange: (value) => setState(() {
     184           2 :                 account = value?.uuid;
     185             :                 if (value != null) {
     186           2 :                   accountCurrency = value.currency;
     187           2 :                   currency = accountCurrency;
     188             :                 }
     189             :               }),
     190             :               width: width,
     191             :             ),
     192           1 :             RowWidget(
     193             :               indent: indent,
     194           1 :               maxWidth: width + indent,
     195             :               chunk: const [125, null],
     196           1 :               children: [
     197           1 :                 [
     198           1 :                   InputWrapper.currency(
     199             :                     type: NamedInputType.currencyShort,
     200           1 :                     value: currency,
     201           2 :                     title: AppLocale.labels.currency,
     202           2 :                     tooltip: AppLocale.labels.currencyTooltip,
     203           4 :                     onChange: (value) => setState(() => currency = value),
     204             :                   ),
     205             :                 ],
     206           1 :                 [
     207           1 :                   InputWrapper.text(
     208           2 :                     title: AppLocale.labels.expense,
     209             :                     isRequired: true,
     210           1 :                     controller: bill,
     211           1 :                     showError: hasErrors && bill.text.isEmpty,
     212           2 :                     tooltip: AppLocale.labels.billSetTooltip,
     213             :                     inputType: const TextInputType.numberWithOptions(decimal: true),
     214           2 :                     formatter: [SimpleInputFormatter.filterDouble],
     215             :                   ),
     216             :                 ],
     217             :               ],
     218             :             ),
     219           1 :             CurrencyExchangeInput(
     220           5 :               key: ValueKey('expense${currency?.code}${accountCurrency?.code}${budgetCurrency?.code}'),
     221           1 :               width: width + indent,
     222             :               indent: indent,
     223           1 :               target: currency,
     224           1 :               controller: exchange,
     225           3 :               source: [accountCurrency, budgetCurrency],
     226             :             ),
     227           1 :             InputWrapper.text(
     228           2 :               title: AppLocale.labels.description,
     229           1 :               controller: description,
     230           2 :               tooltip: AppLocale.labels.descriptionTooltip,
     231             :             ),
     232           1 :             InputWrapper(
     233             :               type: NamedInputType.budgetSelector,
     234             :               isRequired: true,
     235           5 :               value: budget != null ? widget.state.getByUuid(budget!) : null,
     236           2 :               title: AppLocale.labels.budget,
     237           1 :               showError: hasErrors && budget == null,
     238           2 :               tooltip: AppLocale.labels.titleBudgetTooltip,
     239           2 :               state: widget.state,
     240           3 :               onChange: (value) => setState(() {
     241           2 :                 budget = value?.uuid;
     242             :                 if (value != null) {
     243           2 :                   budgetCurrency = value.currency;
     244           1 :                   currency ??= budgetCurrency;
     245             :                 }
     246             :               }),
     247             :               width: width,
     248             :             ),
     249           1 :             Text(
     250           2 :               AppLocale.labels.expenseDateTime,
     251           1 :               style: textTheme.bodyLarge,
     252             :             ),
     253           1 :             DateTimeInput(
     254             :               width: width,
     255           2 :               value: createdAt ?? DateTime.now(),
     256           0 :               setState: (value) => setState(() => createdAt = value),
     257             :             ),
     258             :             ThemeHelper.formEndBox,
     259             :           ],
     260             :         ),
     261             :       ),
     262             :     );
     263             :   }
     264             : }

Generated by: LCOV version 1.14