LCOV - code coverage report
Current view: top level - lib/pages/bill/widgets - expenses_tab.dart (source / functions) Hit Total Coverage
Test: lcov.info Lines: 98 130 75.4 %
Date: 2024-10-04 11:09:33 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           2 :   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           2 :   @override
      55           2 :   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           6 :   late List<ListAccountSelectorItem> accountList = widget.state
      72           2 :       .getList(AppDataType.accounts)
      73           2 :       .where((e) => ![AppAccountType.deposit.toString(), AppAccountType.credit.toString()].contains(e.type))
      74           2 :       .map((item) => ListAccountSelectorItem(item: item))
      75           2 :       .toList();
      76             : 
      77           2 :   @override
      78             :   void initState() {
      79           4 :     focus = FocusController();
      80           2 :     final accountId = AppPreferences.get(AppPreferences.prefAccount);
      81           6 :     final objAccount = widget.state.getByUuid(accountId ?? '');
      82           2 :     final budgetId = AppPreferences.get(AppPreferences.prefBudget);
      83           6 :     final objBudget = widget.state.getByUuid(budgetId ?? '');
      84           6 :     account = widget.account ?? objAccount?.uuid;
      85           6 :     budget = widget.budget ?? objBudget?.uuid;
      86           6 :     currency = widget.currency ?? objAccount?.currency ?? objBudget?.currency ?? Exchange.defaultCurrency;
      87           8 :     bill = TextEditingController(text: widget.bill != null ? widget.bill.toString() : '');
      88           8 :     description = TextEditingController(text: widget.description);
      89           6 :     createdAt = widget.createdAt;
      90          10 :     accountCurrency = widget.state.getByUuid(account ?? '')?.currency;
      91          10 :     budgetCurrency = widget.state.getByUuid(budget ?? '')?.currency;
      92           6 :     exchange = ExchangeController({},
      93          14 :         store: widget.state, targetController: bill, target: currency, source: [accountCurrency, budgetCurrency]);
      94             : 
      95           6 :     widget.callback((
      96           2 :       buildButton: buildButton,
      97           2 :       buttonName: getButtonName(),
      98           2 :       title: getTitle(),
      99             :     ));
     100           2 :     super.initState();
     101             :   }
     102             : 
     103           2 :   @override
     104             :   dispose() {
     105           2 :     isPushed = false;
     106           4 :     description.dispose();
     107           4 :     exchange.dispose();
     108           4 :     bill.dispose();
     109           4 :     focus.dispose();
     110           2 :     super.dispose();
     111             :   }
     112             : 
     113           6 :   String getTitle() => AppLocale.labels.createBillHeader;
     114             : 
     115           0 :   bool hasFormErrors() {
     116           0 :     setState(() => hasErrors = account == null || budget == null || bill.text.isEmpty);
     117           0 :     return hasErrors;
     118             :   }
     119             : 
     120           0 :   void updateStorage() {
     121           0 :     AppPreferences.set(AppPreferences.prefAccount, account ?? '');
     122           0 :     AppPreferences.set(AppPreferences.prefBudget, budget ?? '');
     123           0 :     if (currency != null) {
     124           0 :       CurrencyProvider.pin(currency!);
     125             :     }
     126           0 :     exchange.save();
     127           0 :     widget.state.add(BillAppData(
     128           0 :       account: account ?? '',
     129           0 :       category: budget ?? '',
     130           0 :       currency: currency,
     131           0 :       title: description.text,
     132           0 :       details: double.tryParse(bill.text)?.toFixed(currency?.decimalDigits) ?? 0.0,
     133           0 :       createdAt: createdAt ?? DateTime.now(),
     134             :     ));
     135             :   }
     136             : 
     137           6 :   String getButtonName() => AppLocale.labels.createBillTooltip;
     138             : 
     139           2 :   Widget buildButton(BuildContext context, BoxConstraints constraints) {
     140           2 :     NavigatorState nav = Navigator.of(context);
     141           2 :     return FullSizedButtonWidget(
     142             :       constraints: constraints,
     143           2 :       controller: focus,
     144           0 :       onPressed: () => {
     145           0 :         setState(() {
     146           0 :           if (hasFormErrors()) {
     147             :             return;
     148             :           }
     149           0 :           updateStorage();
     150           0 :           nav.popAndPushNamed(AppRoute.homeRoute);
     151             :         })
     152             :       },
     153           2 :       title: getButtonName(),
     154             :       icon: Icons.save,
     155             :     );
     156             :   }
     157             : 
     158           2 :   @override
     159             :   Widget build(BuildContext context) {
     160           2 :     final textTheme = context.textTheme;
     161           2 :     final indent = ThemeHelper.getIndent(2);
     162           6 :     double width = ScreenHelper.state().width - indent * 3;
     163           4 :     if (widget.isLeft) {
     164           0 :       width -= ThemeHelper.barHeight;
     165             :     }
     166             : 
     167           2 :     return SingleScrollWrapper(
     168           2 :       controller: focus,
     169           2 :       child: Container(
     170           2 :         margin: EdgeInsets.fromLTRB(indent, indent, indent, 240),
     171           2 :         child: Column(
     172           2 :           crossAxisAlignment: AppDesign.getAlignment(),
     173           2 :           children: [
     174           2 :             InputWrapper(
     175             :               type: NamedInputType.accountSelector,
     176             :               isRequired: true,
     177           2 :               value: account != null ? widget.state.getByUuid(account!) : null,
     178           4 :               title: AppLocale.labels.account,
     179           4 :               tooltip: AppLocale.labels.titleAccountTooltip,
     180           2 :               showError: hasErrors && account == null,
     181           4 :               state: widget.state,
     182           2 :               options: accountList,
     183           0 :               onChange: (value) => setState(() {
     184           0 :                 account = value?.uuid;
     185             :                 if (value != null) {
     186           0 :                   accountCurrency = value.currency;
     187           0 :                   currency = accountCurrency;
     188             :                 }
     189             :               }),
     190             :               width: width,
     191             :             ),
     192           2 :             RowWidget(
     193             :               indent: indent,
     194           2 :               maxWidth: width + indent,
     195             :               chunk: const [125, null],
     196           2 :               children: [
     197           2 :                 [
     198           2 :                   InputWrapper.currency(
     199             :                     type: NamedInputType.currencyShort,
     200           2 :                     value: currency,
     201           4 :                     title: AppLocale.labels.currency,
     202           4 :                     tooltip: AppLocale.labels.currencyTooltip,
     203           0 :                     onChange: (value) => setState(() => currency = value),
     204             :                   ),
     205             :                 ],
     206           2 :                 [
     207           2 :                   InputWrapper.text(
     208           4 :                     title: AppLocale.labels.expense,
     209             :                     isRequired: true,
     210           2 :                     controller: bill,
     211           2 :                     showError: hasErrors && bill.text.isEmpty,
     212           4 :                     tooltip: AppLocale.labels.billSetTooltip,
     213             :                     inputType: const TextInputType.numberWithOptions(decimal: true),
     214           4 :                     formatter: [SimpleInputFormatter.filterDouble],
     215             :                   ),
     216             :                 ],
     217             :               ],
     218             :             ),
     219           2 :             CurrencyExchangeInput(
     220          10 :               key: ValueKey('expense${currency?.code}${accountCurrency?.code}${budgetCurrency?.code}'),
     221           2 :               width: width + indent,
     222             :               indent: indent,
     223           2 :               target: currency,
     224           2 :               controller: exchange,
     225           6 :               source: [accountCurrency, budgetCurrency],
     226             :             ),
     227           2 :             InputWrapper.text(
     228           4 :               title: AppLocale.labels.description,
     229           2 :               controller: description,
     230           4 :               tooltip: AppLocale.labels.descriptionTooltip,
     231             :             ),
     232           2 :             InputWrapper(
     233             :               type: NamedInputType.budgetSelector,
     234             :               isRequired: true,
     235           2 :               value: budget != null ? widget.state.getByUuid(budget!) : null,
     236           4 :               title: AppLocale.labels.budget,
     237           2 :               showError: hasErrors && budget == null,
     238           4 :               tooltip: AppLocale.labels.titleBudgetTooltip,
     239           4 :               state: widget.state,
     240           0 :               onChange: (value) => setState(() {
     241           0 :                 budget = value?.uuid;
     242             :                 if (value != null) {
     243           0 :                   budgetCurrency = value.currency;
     244           0 :                   currency ??= budgetCurrency;
     245             :                 }
     246             :               }),
     247             :               width: width,
     248             :             ),
     249           2 :             Text(
     250           4 :               AppLocale.labels.expenseDateTime,
     251           2 :               style: textTheme.bodyLarge,
     252             :             ),
     253           2 :             DateTimeInput(
     254             :               width: width,
     255           4 :               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