import 'package:flutter/material.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/static/currency_model.dart'; import 'package:invoiceninja_flutter/ui/app/entity_dropdown.dart'; import 'package:invoiceninja_flutter/ui/app/forms/date_picker.dart'; import 'package:invoiceninja_flutter/ui/app/invoice/tax_rate_dropdown.dart'; import 'package:invoiceninja_flutter/ui/expense/edit/expense_edit_vm.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:invoiceninja_flutter/ui/app/form_card.dart'; import 'package:invoiceninja_flutter/redux/static/static_selectors.dart'; import 'package:invoiceninja_flutter/utils/money.dart'; class ExpenseEditSettings extends StatefulWidget { const ExpenseEditSettings({ Key key, @required this.viewModel, }) : super(key: key); final ExpenseEditVM viewModel; @override ExpenseEditSettingsState createState() => ExpenseEditSettingsState(); } class ExpenseEditSettingsState extends State { bool showPaymentFields = false; bool showConvertCurrencyFields = false; final _transactionReferenceController = TextEditingController(); final _exchangeRateController = TextEditingController(); final List _controllers = []; @override void didChangeDependencies() { final List _controllers = [ _transactionReferenceController, _exchangeRateController, ]; _controllers .forEach((dynamic controller) => controller.removeListener(_onChanged)); final expense = widget.viewModel.expense; _transactionReferenceController.text = expense.transactionReference; _exchangeRateController.text = formatNumber(expense.exchangeRate, context, formatNumberType: FormatNumberType.input); _controllers .forEach((dynamic controller) => controller.addListener(_onChanged)); showPaymentFields = expense.paymentDate.isNotEmpty; showConvertCurrencyFields = expense.exchangeRate != 0 && expense.exchangeRate != 1; super.didChangeDependencies(); } @override void dispose() { _controllers.forEach((dynamic controller) { controller.removeListener(_onChanged); controller.dispose(); }); super.dispose(); } void _onChanged() { final viewModel = widget.viewModel; final expense = viewModel.expense.rebuild((b) => b ..transactionReference = _transactionReferenceController.text.trim() ..exchangeRate = parseDouble(_exchangeRateController.text)); if (expense != viewModel.expense) { viewModel.onChanged(expense); } } void _setCurrency(CurrencyEntity currency) { final viewModel = widget.viewModel; final expense = viewModel.expense; final exchangeRate = currency == null ? 0.0 : getExchangeRate(context, fromCurrencyId: expense.expenseCurrencyId, toCurrencyId: currency.id); viewModel.onChanged(expense.rebuild((b) => b ..invoiceCurrencyId = currency?.id ?? 0 ..exchangeRate = exchangeRate)); WidgetsBinding.instance.addPostFrameCallback((duration) { _exchangeRateController.text = formatNumber(exchangeRate, context, formatNumberType: FormatNumberType.input); }); } @override Widget build(BuildContext context) { final localization = AppLocalization.of(context); final viewModel = widget.viewModel; final staticState = viewModel.state.staticState; final company = viewModel.company; final expense = viewModel.expense; return ListView( shrinkWrap: true, children: [ FormCard( children: [ TaxRateDropdown( taxRates: company.taxRates, onSelected: (taxRate) => viewModel.onChanged(expense.rebuild((b) => b ..taxRate1 = taxRate.rate ..taxName1 = taxRate.name)), labelText: localization.tax, initialTaxName: expense.taxName1, initialTaxRate: expense.taxRate1, ), company.settings.enableSecondTaxRate ? TaxRateDropdown( taxRates: company.taxRates, onSelected: (taxRate) => viewModel.onChanged(expense.rebuild((b) => b ..taxRate2 = taxRate.rate ..taxName2 = taxRate.name)), labelText: localization.tax, initialTaxName: expense.taxName2, initialTaxRate: expense.taxRate2, ) : SizedBox(), SizedBox(height: 16), expense.isInvoiced ? SizedBox() : SwitchListTile( activeColor: Theme.of(context).accentColor, title: Text(localization.markBillable), value: expense.shouldBeInvoiced, onChanged: (value) { viewModel.onChanged( expense.rebuild((b) => b..shouldBeInvoiced = value)); }, ), SwitchListTile( activeColor: Theme.of(context).accentColor, title: Text(localization.markPaid), value: showPaymentFields, onChanged: (value) { if (value) { if (expense.paymentDate.isEmpty) { viewModel.onChanged(expense.rebuild( (b) => b..paymentDate = convertDateTimeToSqlDate())); } } else { viewModel .onChanged(expense.rebuild((b) => b..paymentDate = '')); WidgetsBinding.instance.addPostFrameCallback((duration) { _transactionReferenceController.text = ''; }); } setState(() => showPaymentFields = value); }, ), showPaymentFields ? Column( children: [ SizedBox(height: 8), EntityDropdown( entityType: EntityType.paymentType, entityMap: staticState.paymentTypeMap, entityList: memoizedPaymentTypeList(staticState.paymentTypeMap), labelText: localization.paymentType, initialValue: staticState .paymentTypeMap[expense.paymentTypeId]?.name, onSelected: (paymentType) => viewModel.onChanged(expense .rebuild((b) => b..paymentTypeId = paymentType.id)), ), DatePicker( labelText: localization.date, selectedDate: expense.paymentDate, onSelected: (date) { viewModel.onChanged( expense.rebuild((b) => b..paymentDate = date)); }, ), TextFormField( controller: _transactionReferenceController, keyboardType: TextInputType.text, decoration: InputDecoration( labelText: localization.transactionReference, ), ), SizedBox(height: 16), ], ) : SizedBox(), SwitchListTile( activeColor: Theme.of(context).accentColor, title: Text(localization.convertCurrency), value: showConvertCurrencyFields, onChanged: (value) { setState(() => showConvertCurrencyFields = value); if (value) { _setCurrency( staticState.currencyMap[expense.invoiceCurrencyId]); } else { viewModel .onChanged(expense.rebuild((b) => b..exchangeRate = 0)); WidgetsBinding.instance.addPostFrameCallback((duration) { _exchangeRateController.text = ''; }); } }, ), showConvertCurrencyFields ? Column( children: [ SizedBox(height: 8), EntityDropdown( entityType: EntityType.currency, entityMap: staticState.currencyMap, entityList: memoizedCurrencyList(staticState.currencyMap), labelText: localization.currency, initialValue: staticState .currencyMap[viewModel.expense.invoiceCurrencyId] ?.name, onSelected: (SelectableEntity currency) => _setCurrency(currency), ), TextFormField( key: ValueKey('__${expense.invoiceCurrencyId}__'), controller: _exchangeRateController, keyboardType: TextInputType.numberWithOptions(decimal: true), decoration: InputDecoration( labelText: localization.exchangeRate, ), ), SizedBox(height: 16), ], ) : SizedBox(), SwitchListTile( activeColor: Theme.of(context).accentColor, title: Text(localization.addDocumentsToInvoice), value: expense.invoiceDocuments, onChanged: (value) { viewModel.onChanged( expense.rebuild((b) => b..invoiceDocuments = value)); viewModel.onAddDocumentsChanged(value); }) ], ), ], ); } }