Transactions
This commit is contained in:
parent
0dccbd19f5
commit
1497c2382b
|
|
@ -52,18 +52,15 @@ import 'package:invoiceninja_flutter/redux/ui/pref_state.dart';
|
|||
import 'package:invoiceninja_flutter/redux/user/user_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/vendor/vendor_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/webhook/webhook_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/transaction/transaction_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/bank_account/bank_account_middleware.dart';
|
||||
import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_middleware.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
// STARTER: import - do not remove comment
|
||||
|
||||
import 'package:invoiceninja_flutter/utils/web_stub.dart'
|
||||
if (dart.library.html) 'package:invoiceninja_flutter/utils/web.dart';
|
||||
|
||||
// STARTER: import - do not remove comment
|
||||
import 'package:invoiceninja_flutter/redux/transaction/transaction_middleware.dart';
|
||||
|
||||
import 'package:invoiceninja_flutter/redux/bank_account/bank_account_middleware.dart';
|
||||
|
||||
import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_middleware.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
void main({bool isTesting = false}) async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,21 @@ class ViewBankAccount implements PersistUI, PersistPrefs {
|
|||
final bool force;
|
||||
}
|
||||
|
||||
class EditBankAccount implements PersistUI, PersistPrefs {
|
||||
EditBankAccount(
|
||||
{@required this.bankAccount, this.completer, this.force = false});
|
||||
|
||||
final BankAccountEntity bankAccount;
|
||||
final Completer completer;
|
||||
final bool force;
|
||||
}
|
||||
|
||||
class UpdateBankAccount implements PersistUI {
|
||||
UpdateBankAccount(this.bankAccount);
|
||||
|
||||
final BankAccountEntity bankAccount;
|
||||
}
|
||||
|
||||
class LoadBankAccount {
|
||||
LoadBankAccount({this.completer, this.bankAccountId});
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,10 @@ Reducer<String> selectedIdReducer = combineReducers([
|
|||
final editingReducer = combineReducers<BankAccountEntity>([
|
||||
TypedReducer<BankAccountEntity, SaveBankAccountSuccess>(_updateEditing),
|
||||
TypedReducer<BankAccountEntity, AddBankAccountSuccess>(_updateEditing),
|
||||
TypedReducer<BankAccountEntity, EditBankAccount>(_updateEditing),
|
||||
TypedReducer<BankAccountEntity, UpdateBankAccount>((bankAccount, action) {
|
||||
return action.bankAccount.rebuild((b) => b..isChanged = true);
|
||||
}),
|
||||
TypedReducer<BankAccountEntity, RestoreBankAccountsSuccess>(
|
||||
(bankAccounts, action) {
|
||||
return action.bankAccounts[0];
|
||||
|
|
|
|||
|
|
@ -0,0 +1,135 @@
|
|||
// Flutter imports:
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Project imports:
|
||||
import 'package:invoiceninja_flutter/data/models/entities.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/edit_scaffold.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/form_card.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/app_form.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/custom_field.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/invoice/tax_rate_dropdown.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/scrollable_listview.dart';
|
||||
import 'package:invoiceninja_flutter/ui/bank_account/edit/bank_account_edit_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||
|
||||
class BankAccountEdit extends StatefulWidget {
|
||||
const BankAccountEdit({
|
||||
Key key,
|
||||
@required this.viewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
final BankAccountEditVM viewModel;
|
||||
|
||||
@override
|
||||
_BankAccountEditState createState() => _BankAccountEditState();
|
||||
}
|
||||
|
||||
class _BankAccountEditState extends State<BankAccountEdit> {
|
||||
static final GlobalKey<FormState> _formKey =
|
||||
GlobalKey<FormState>(debugLabel: '_bankAccountEdit');
|
||||
final FocusScopeNode _focusNode = FocusScopeNode();
|
||||
bool _autoValidate = false;
|
||||
|
||||
final _nameController = TextEditingController();
|
||||
|
||||
List<TextEditingController> _controllers = [];
|
||||
final _debouncer = Debouncer();
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
_controllers = [
|
||||
_nameController,
|
||||
];
|
||||
|
||||
_controllers
|
||||
.forEach((dynamic controller) => controller.removeListener(_onChanged));
|
||||
|
||||
final bankAccount = widget.viewModel.bankAccount;
|
||||
_nameController.text = bankAccount.name;
|
||||
|
||||
_controllers
|
||||
.forEach((dynamic controller) => controller.addListener(_onChanged));
|
||||
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controllers.forEach((dynamic controller) {
|
||||
controller.removeListener(_onChanged);
|
||||
controller.dispose();
|
||||
});
|
||||
_focusNode.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onChanged() {
|
||||
final bankAccount = widget.viewModel.bankAccount
|
||||
.rebuild((b) => b..name = _nameController.text.trim());
|
||||
if (bankAccount != widget.viewModel.bankAccount) {
|
||||
_debouncer.run(() {
|
||||
widget.viewModel.onChanged(bankAccount);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final viewModel = widget.viewModel;
|
||||
final bankAccount = viewModel.bankAccount;
|
||||
final company = viewModel.company;
|
||||
|
||||
return EditScaffold(
|
||||
entity: bankAccount,
|
||||
title: viewModel.bankAccount.isNew
|
||||
? localization.newBankAccount
|
||||
: localization.editBankAccount,
|
||||
onCancelPressed: (context) => viewModel.onCancelPressed(context),
|
||||
onSavePressed: (context) {
|
||||
final bool isValid = _formKey.currentState.validate();
|
||||
|
||||
setState(() {
|
||||
_autoValidate = !isValid;
|
||||
});
|
||||
|
||||
if (!isValid) {
|
||||
return;
|
||||
}
|
||||
|
||||
viewModel.onSavePressed(context);
|
||||
},
|
||||
body: AppForm(
|
||||
formKey: _formKey,
|
||||
focusNode: _focusNode,
|
||||
child: ScrollableListView(
|
||||
key: ValueKey(
|
||||
'__bankAccount_${bankAccount.id}_${bankAccount.updatedAt}__'),
|
||||
children: <Widget>[
|
||||
FormCard(
|
||||
isLast: true,
|
||||
children: <Widget>[
|
||||
DecoratedFormField(
|
||||
autofocus: true,
|
||||
label: localization.name,
|
||||
controller: _nameController,
|
||||
validator: (val) => val.isEmpty || val.trim().isEmpty
|
||||
? localization.pleaseEnterAName
|
||||
: null,
|
||||
autovalidate: _autoValidate,
|
||||
onSavePressed: viewModel.onSavePressed,
|
||||
keyboardType: TextInputType.text,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
// Dart imports:
|
||||
import 'dart:async';
|
||||
|
||||
// Flutter imports:
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
// Package imports:
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:flutter_styled_toast/flutter_styled_toast.dart';
|
||||
import 'package:invoiceninja_flutter/redux/bank_account/bank_account_actions.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
|
||||
// Project imports:
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/main_app.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/app/app_state.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/dialogs/error_dialog.dart';
|
||||
import 'package:invoiceninja_flutter/ui/bank_account/edit/bank_account_edit.dart';
|
||||
import 'package:invoiceninja_flutter/ui/bank_account/view/bank_account_view_vm.dart';
|
||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||
|
||||
class BankAccountEditScreen extends StatelessWidget {
|
||||
const BankAccountEditScreen({Key key}) : super(key: key);
|
||||
|
||||
static const String route = '/bankAccount/edit';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return StoreConnector<AppState, BankAccountEditVM>(
|
||||
converter: (Store<AppState> store) {
|
||||
return BankAccountEditVM.fromStore(store);
|
||||
},
|
||||
builder: (context, vm) {
|
||||
return BankAccountEdit(
|
||||
viewModel: vm,
|
||||
key: ValueKey(vm.bankAccount.id),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class BankAccountEditVM {
|
||||
BankAccountEditVM({
|
||||
@required this.state,
|
||||
@required this.company,
|
||||
@required this.bankAccount,
|
||||
@required this.origBankAccount,
|
||||
@required this.onChanged,
|
||||
@required this.onSavePressed,
|
||||
@required this.onCancelPressed,
|
||||
@required this.onEntityAction,
|
||||
@required this.isSaving,
|
||||
@required this.isDirty,
|
||||
});
|
||||
|
||||
factory BankAccountEditVM.fromStore(Store<AppState> store) {
|
||||
final state = store.state;
|
||||
final bankAccount = state.bankAccountUIState.editing;
|
||||
|
||||
return BankAccountEditVM(
|
||||
state: state,
|
||||
company: state.company,
|
||||
isSaving: state.isSaving,
|
||||
isDirty: bankAccount.isNew,
|
||||
bankAccount: bankAccount,
|
||||
origBankAccount: state.bankAccountState.map[bankAccount.id],
|
||||
onChanged: (BankAccountEntity bankAccount) {
|
||||
store.dispatch(UpdateBankAccount(bankAccount));
|
||||
},
|
||||
onCancelPressed: (BuildContext context) {
|
||||
createEntity(
|
||||
context: context, entity: BankAccountEntity(), force: true);
|
||||
store.dispatch(UpdateCurrentRoute(state.uiState.previousRoute));
|
||||
},
|
||||
onSavePressed: (BuildContext context) {
|
||||
Debouncer.runOnComplete(() {
|
||||
final bankAccount = store.state.bankAccountUIState.editing;
|
||||
final localization = navigatorKey.localization;
|
||||
final navigator = navigatorKey.currentState;
|
||||
final Completer<BankAccountEntity> completer =
|
||||
new Completer<BankAccountEntity>();
|
||||
store.dispatch(SaveBankAccountRequest(
|
||||
completer: completer, bankAccount: bankAccount));
|
||||
return completer.future.then((savedBankAccount) {
|
||||
showToast(bankAccount.isNew
|
||||
? localization.createdBankAccount
|
||||
: localization.updatedBankAccount);
|
||||
|
||||
if (state.prefState.isMobile) {
|
||||
store.dispatch(UpdateCurrentRoute(BankAccountViewScreen.route));
|
||||
if (bankAccount.isNew) {
|
||||
navigator.pushReplacementNamed(BankAccountViewScreen.route);
|
||||
} else {
|
||||
navigator.pop(savedBankAccount);
|
||||
}
|
||||
} else {
|
||||
viewEntity(entity: savedBankAccount);
|
||||
}
|
||||
}).catchError((Object error) {
|
||||
showDialog<ErrorDialog>(
|
||||
context: navigatorKey.currentContext,
|
||||
builder: (BuildContext context) {
|
||||
return ErrorDialog(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
onEntityAction: (BuildContext context, EntityAction action) {
|
||||
// TODO Add view page for bankAccounts
|
||||
// Prevent duplicate global key error
|
||||
if (action == EntityAction.clone) {
|
||||
Navigator.pop(context);
|
||||
WidgetsBinding.instance.addPostFrameCallback((duration) {
|
||||
handleBankAccountAction(context, [bankAccount], action);
|
||||
});
|
||||
} else {
|
||||
handleBankAccountAction(context, [bankAccount], action);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
final AppState state;
|
||||
final CompanyEntity company;
|
||||
final BankAccountEntity bankAccount;
|
||||
final BankAccountEntity origBankAccount;
|
||||
final Function(BankAccountEntity) onChanged;
|
||||
final Function(BuildContext) onSavePressed;
|
||||
final Function(BuildContext) onCancelPressed;
|
||||
final Function(BuildContext, EntityAction) onEntityAction;
|
||||
final bool isSaving;
|
||||
final bool isDirty;
|
||||
}
|
||||
|
|
@ -16,6 +16,9 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
static final Map<String, Map<String, String>> _localizedValues = {
|
||||
'en': {
|
||||
// STARTER: lang key - do not remove comment
|
||||
'created_bank_account': 'Successfully created bank account',
|
||||
'updated_bank_account': 'Successfully updated bank account',
|
||||
'edit_bank_account': 'Edit Bank Account',
|
||||
'default_category': 'Default Category',
|
||||
'account_type': 'Account Type',
|
||||
'new_bank_account': 'New Bank Account',
|
||||
|
|
@ -87384,6 +87387,10 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
_localizedValues[localeCode]['new_bank_account'] ??
|
||||
_localizedValues['en']['new_bank_account'];
|
||||
|
||||
String get editBankAccount =>
|
||||
_localizedValues[localeCode]['edit_bank_account'] ??
|
||||
_localizedValues['en']['edit_bank_account'];
|
||||
|
||||
String get accountType =>
|
||||
_localizedValues[localeCode]['account_type'] ??
|
||||
_localizedValues['en']['account_type'];
|
||||
|
|
@ -87392,6 +87399,14 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
|||
_localizedValues[localeCode]['default_category'] ??
|
||||
_localizedValues['en']['default_category'];
|
||||
|
||||
String get createdBankAccount =>
|
||||
_localizedValues[localeCode]['created_bank_account'] ??
|
||||
_localizedValues['en']['created_bank_account'];
|
||||
|
||||
String get updatedBankAccount =>
|
||||
_localizedValues[localeCode]['updated_bank_account'] ??
|
||||
_localizedValues['en']['updated_bank_account'];
|
||||
|
||||
// STARTER: lang field - do not remove comment
|
||||
|
||||
String lookup(String key) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue