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/invoice_app_data.dart';
9 : import 'package:app_finance/_classes/structure/navigation/app_route.dart';
10 : import 'package:app_finance/_classes/controller/focus_controller.dart';
11 : import 'package:app_finance/_classes/storage/app_data.dart';
12 : import 'package:app_finance/_configs/screen_helper.dart';
13 : import 'package:app_finance/_configs/theme_helper.dart';
14 : import 'package:app_finance/_ext/build_context_ext.dart';
15 : import 'package:app_finance/_ext/double_ext.dart';
16 : import 'package:app_finance/design/wrapper/input_wrapper.dart';
17 : import 'package:app_finance/pages/bill/widgets/interface_bill_page_inject.dart';
18 : import 'package:app_finance/design/form/currency_exchange_input.dart';
19 : import 'package:app_finance/design/form/date_time_input.dart';
20 : import 'package:app_finance/design/button/full_sized_button_widget.dart';
21 : import 'package:app_finance/design/wrapper/row_widget.dart';
22 : import 'package:app_finance/design/form/simple_input.dart';
23 : import 'package:app_finance/design/wrapper/single_scroll_wrapper.dart';
24 : import 'package:flutter/material.dart';
25 : import 'package:flutter_currency_picker/flutter_currency_picker.dart';
26 :
27 : class TransferTab<T> extends StatefulWidget {
28 : final String? accountFrom;
29 : final String? accountTo;
30 : final double? amount;
31 : final String? description;
32 : final Currency? currency;
33 : final DateTime? createdAt;
34 : final AppData state;
35 : final bool isLeft;
36 : final FnBillPageCallback callback;
37 :
38 1 : const TransferTab({
39 : super.key,
40 : required this.state,
41 : required this.callback,
42 : this.accountFrom,
43 : this.accountTo,
44 : this.amount,
45 : this.description,
46 : this.currency,
47 : this.createdAt,
48 : this.isLeft = false,
49 : });
50 :
51 0 : @override
52 0 : TransferTabState createState() => TransferTabState();
53 : }
54 :
55 : class TransferTabState<T extends TransferTab> extends State<T> {
56 : late FocusController focus;
57 : String? accountFrom;
58 : String? accountTo;
59 : Currency? accountFromCurrency;
60 : Currency? accountToCurrency;
61 : late TextEditingController amount;
62 : late TextEditingController description;
63 : late ExchangeController exchange;
64 : late DateTime createdAt;
65 : Currency? currency;
66 : bool hasErrors = false;
67 : bool isPushed = false;
68 :
69 0 : @override
70 : void initState() {
71 0 : focus = FocusController();
72 0 : accountFrom = widget.accountFrom;
73 0 : accountTo = widget.accountTo;
74 0 : createdAt = widget.createdAt ?? DateTime.now();
75 0 : amount = TextEditingController(text: widget.amount != null ? widget.amount.toString() : '');
76 0 : description = TextEditingController(text: widget.description);
77 0 : currency = widget.currency ?? Exchange.defaultCurrency;
78 0 : exchange = ExchangeController({}, store: widget.state, targetController: amount, target: currency, source: []);
79 :
80 0 : widget.callback((
81 0 : buildButton: buildButton,
82 0 : buttonName: getButtonName(),
83 0 : title: getTitle(),
84 : ));
85 0 : super.initState();
86 : }
87 :
88 0 : @override
89 : dispose() {
90 0 : isPushed = false;
91 0 : amount.dispose();
92 0 : description.dispose();
93 0 : focus.dispose();
94 0 : super.dispose();
95 : }
96 :
97 0 : bool hasFormErrors() {
98 0 : setState(() => hasErrors = accountFrom == null || accountTo == null);
99 0 : return hasErrors;
100 : }
101 :
102 0 : String getTitle() => AppLocale.labels.createBillHeader;
103 :
104 0 : void updateStorage() {
105 0 : final uuid = accountFrom ?? '';
106 0 : exchange.save();
107 0 : widget.state.add(InvoiceAppData(
108 0 : title: description.text,
109 0 : color: widget.state.getByUuid(uuid)?.color,
110 0 : account: accountTo ?? '',
111 : accountFrom: uuid,
112 0 : details: double.tryParse(amount.text)?.toFixed(currency?.decimalDigits) ?? 0.0,
113 0 : currency: currency,
114 0 : createdAt: createdAt,
115 : ));
116 : }
117 :
118 0 : String getButtonName() => AppLocale.labels.createTransferTooltip;
119 :
120 0 : Widget buildButton(BuildContext context, BoxConstraints constraints) {
121 0 : final nav = Navigator.of(context);
122 0 : return FullSizedButtonWidget(
123 : constraints: constraints,
124 0 : controller: focus,
125 0 : onPressed: () => {
126 0 : setState(() {
127 0 : if (hasFormErrors()) {
128 : return;
129 : }
130 0 : updateStorage();
131 0 : nav.popAndPushNamed(AppRoute.homeRoute);
132 : })
133 : },
134 0 : title: getButtonName(),
135 : icon: Icons.save,
136 : );
137 : }
138 :
139 0 : @override
140 : Widget build(BuildContext context) {
141 0 : final textTheme = context.textTheme;
142 0 : final indent = ThemeHelper.getIndent(2);
143 0 : double width = ScreenHelper.state().width - indent * 3;
144 0 : if (widget.isLeft) {
145 0 : width -= ThemeHelper.barHeight;
146 : }
147 0 : return SingleScrollWrapper(
148 0 : controller: focus,
149 0 : child: Container(
150 0 : margin: EdgeInsets.fromLTRB(indent, indent, indent, 240),
151 : width: width,
152 0 : child: Column(
153 0 : crossAxisAlignment: AppDesign.getAlignment(),
154 0 : children: [
155 0 : InputWrapper(
156 : type: NamedInputType.accountSelector,
157 : isRequired: true,
158 0 : value: accountFrom != null ? widget.state.getByUuid(accountFrom!) : null,
159 0 : title: AppLocale.labels.accountFrom,
160 0 : tooltip: '${AppLocale.labels.titleAccountTooltip} (${AppLocale.labels.accountFrom})',
161 0 : showError: hasErrors && accountFrom == null,
162 0 : state: widget.state,
163 0 : onChange: (value) => setState(() {
164 0 : accountFrom = value?.uuid;
165 : if (value != null) {
166 0 : accountFromCurrency = value.currency;
167 0 : currency = accountFromCurrency;
168 : }
169 : }),
170 : width: width,
171 : ),
172 0 : InputWrapper(
173 : type: NamedInputType.accountSelector,
174 : isRequired: true,
175 0 : value: accountTo != null ? widget.state.getByUuid(accountTo!) : null,
176 0 : title: AppLocale.labels.accountTo,
177 0 : tooltip: '${AppLocale.labels.titleAccountTooltip} (${AppLocale.labels.accountTo})',
178 0 : showError: hasErrors && accountTo == null,
179 0 : state: widget.state,
180 0 : onChange: (value) => setState(() {
181 0 : accountTo = value?.uuid;
182 : if (value != null) {
183 0 : accountToCurrency = value.currency;
184 0 : currency ??= accountToCurrency;
185 : }
186 : }),
187 : width: width,
188 : ),
189 0 : RowWidget(
190 : indent: indent,
191 0 : maxWidth: width + indent,
192 : chunk: const [125, null],
193 0 : children: [
194 0 : [
195 0 : InputWrapper.currency(
196 : type: NamedInputType.currencyShort,
197 0 : value: currency,
198 0 : title: AppLocale.labels.currency,
199 0 : tooltip: AppLocale.labels.currencyTooltip,
200 0 : onChange: (value) => setState(() => currency = value),
201 : ),
202 : ],
203 0 : [
204 0 : InputWrapper.text(
205 0 : title: AppLocale.labels.expenseTransfer,
206 0 : tooltip: AppLocale.labels.billSetTooltip,
207 0 : controller: amount,
208 : inputType: const TextInputType.numberWithOptions(decimal: true),
209 0 : formatter: [
210 0 : SimpleInputFormatter.filterDouble,
211 : ],
212 : ),
213 : ],
214 : ],
215 : ),
216 0 : CurrencyExchangeInput(
217 0 : key: ValueKey('transfer${currency?.code}${accountFromCurrency?.code}${accountToCurrency?.code}'),
218 0 : width: width + indent,
219 : indent: indent,
220 0 : target: currency,
221 0 : controller: exchange,
222 0 : source: [accountFromCurrency, accountToCurrency],
223 : ),
224 0 : InputWrapper.text(
225 0 : title: AppLocale.labels.description,
226 0 : tooltip: AppLocale.labels.transferTooltip,
227 0 : controller: description,
228 : ),
229 0 : Text(
230 0 : AppLocale.labels.balanceDate,
231 0 : style: textTheme.bodyLarge,
232 : ),
233 0 : DateTimeInput(
234 : width: width,
235 0 : value: createdAt,
236 0 : setState: (value) => setState(() => createdAt = value),
237 : ),
238 : ThemeHelper.formEndBox,
239 : ],
240 : ),
241 : ),
242 : );
243 : }
244 : }
|