diff --git a/lib/data/models/transaction_model.dart b/lib/data/models/transaction_model.dart index 0418fa59e..fba5269f2 100644 --- a/lib/data/models/transaction_model.dart +++ b/lib/data/models/transaction_model.dart @@ -92,6 +92,7 @@ abstract class TransactionEntity extends Object transactionId: 0, categoryId: '', transactionRuleId: '', + paymentId: '', ); } @@ -131,6 +132,9 @@ abstract class TransactionEntity extends Object @BuiltValueField(wireName: 'invoice_ids') String get invoiceIds; + @BuiltValueField(wireName: 'payment_id') + String get paymentId; + @BuiltValueField(wireName: 'expense_id') String get expenseId; @@ -359,6 +363,7 @@ abstract class TransactionEntity extends Object ..baseType = '' ..bankAccountId = '' ..transactionRuleId = '' + ..paymentId = '' ..currencyId = ''; static Serializer get serializer => diff --git a/lib/data/models/transaction_model.g.dart b/lib/data/models/transaction_model.g.dart index b97092874..71f0298c5 100644 --- a/lib/data/models/transaction_model.g.dart +++ b/lib/data/models/transaction_model.g.dart @@ -151,6 +151,9 @@ class _$TransactionEntitySerializer 'invoice_ids', serializers.serialize(object.invoiceIds, specifiedType: const FullType(String)), + 'payment_id', + serializers.serialize(object.paymentId, + specifiedType: const FullType(String)), 'expense_id', serializers.serialize(object.expenseId, specifiedType: const FullType(String)), @@ -259,6 +262,10 @@ class _$TransactionEntitySerializer result.invoiceIds = serializers.deserialize(value, specifiedType: const FullType(String)) as String; break; + case 'payment_id': + result.paymentId = serializers.deserialize(value, + specifiedType: const FullType(String)) as String; + break; case 'expense_id': result.expenseId = serializers.deserialize(value, specifiedType: const FullType(String)) as String; @@ -577,6 +584,8 @@ class _$TransactionEntity extends TransactionEntity { @override final String invoiceIds; @override + final String paymentId; + @override final String expenseId; @override final String vendorId; @@ -620,6 +629,7 @@ class _$TransactionEntity extends TransactionEntity { this.statusId, this.categoryId, this.invoiceIds, + this.paymentId, this.expenseId, this.vendorId, this.transactionId, @@ -654,6 +664,8 @@ class _$TransactionEntity extends TransactionEntity { categoryId, 'TransactionEntity', 'categoryId'); BuiltValueNullFieldError.checkNotNull( invoiceIds, 'TransactionEntity', 'invoiceIds'); + BuiltValueNullFieldError.checkNotNull( + paymentId, 'TransactionEntity', 'paymentId'); BuiltValueNullFieldError.checkNotNull( expenseId, 'TransactionEntity', 'expenseId'); BuiltValueNullFieldError.checkNotNull( @@ -693,6 +705,7 @@ class _$TransactionEntity extends TransactionEntity { statusId == other.statusId && categoryId == other.categoryId && invoiceIds == other.invoiceIds && + paymentId == other.paymentId && expenseId == other.expenseId && vendorId == other.vendorId && transactionId == other.transactionId && @@ -730,12 +743,12 @@ class _$TransactionEntity extends TransactionEntity { $jc( $jc( $jc( - $jc($jc($jc($jc($jc($jc(0, amount.hashCode), currencyId.hashCode), category.hashCode), baseType.hashCode), date.hashCode), - bankAccountId.hashCode), - description.hashCode), - statusId.hashCode), - categoryId.hashCode), - invoiceIds.hashCode), + $jc($jc($jc($jc($jc($jc($jc(0, amount.hashCode), currencyId.hashCode), category.hashCode), baseType.hashCode), date.hashCode), bankAccountId.hashCode), + description.hashCode), + statusId.hashCode), + categoryId.hashCode), + invoiceIds.hashCode), + paymentId.hashCode), expenseId.hashCode), vendorId.hashCode), transactionId.hashCode), @@ -765,6 +778,7 @@ class _$TransactionEntity extends TransactionEntity { ..add('statusId', statusId) ..add('categoryId', categoryId) ..add('invoiceIds', invoiceIds) + ..add('paymentId', paymentId) ..add('expenseId', expenseId) ..add('vendorId', vendorId) ..add('transactionId', transactionId) @@ -828,6 +842,10 @@ class TransactionEntityBuilder String get invoiceIds => _$this._invoiceIds; set invoiceIds(String invoiceIds) => _$this._invoiceIds = invoiceIds; + String _paymentId; + String get paymentId => _$this._paymentId; + set paymentId(String paymentId) => _$this._paymentId = paymentId; + String _expenseId; String get expenseId => _$this._expenseId; set expenseId(String expenseId) => _$this._expenseId = expenseId; @@ -906,6 +924,7 @@ class TransactionEntityBuilder _statusId = $v.statusId; _categoryId = $v.categoryId; _invoiceIds = $v.invoiceIds; + _paymentId = $v.paymentId; _expenseId = $v.expenseId; _vendorId = $v.vendorId; _transactionId = $v.transactionId; @@ -956,9 +975,10 @@ class TransactionEntityBuilder description, 'TransactionEntity', 'description'), statusId: BuiltValueNullFieldError.checkNotNull( statusId, 'TransactionEntity', 'statusId'), - categoryId: BuiltValueNullFieldError.checkNotNull( - categoryId, 'TransactionEntity', 'categoryId'), + categoryId: + BuiltValueNullFieldError.checkNotNull(categoryId, 'TransactionEntity', 'categoryId'), invoiceIds: BuiltValueNullFieldError.checkNotNull(invoiceIds, 'TransactionEntity', 'invoiceIds'), + paymentId: BuiltValueNullFieldError.checkNotNull(paymentId, 'TransactionEntity', 'paymentId'), expenseId: BuiltValueNullFieldError.checkNotNull(expenseId, 'TransactionEntity', 'expenseId'), vendorId: BuiltValueNullFieldError.checkNotNull(vendorId, 'TransactionEntity', 'vendorId'), transactionId: BuiltValueNullFieldError.checkNotNull(transactionId, 'TransactionEntity', 'transactionId'), diff --git a/lib/ui/app/forms/bool_dropdown_button.dart b/lib/ui/app/forms/bool_dropdown_button.dart index 6ddc8cfd0..d86f3439e 100644 --- a/lib/ui/app/forms/bool_dropdown_button.dart +++ b/lib/ui/app/forms/bool_dropdown_button.dart @@ -11,9 +11,9 @@ import 'package:invoiceninja_flutter/utils/platforms.dart'; class BoolDropdownButton extends StatelessWidget { const BoolDropdownButton({ - @required this.label, @required this.value, @required this.onChanged, + this.label, this.showBlank, this.enabledLabel, this.helpLabel, @@ -57,87 +57,92 @@ class BoolDropdownButton extends StatelessWidget { ); } + final widget = _showBlank + ? DropdownButtonHideUnderline( + child: DropdownButton( + value: value, + isExpanded: true, + isDense: true, + onChanged: (value) => onChanged(value), + items: [ + DropdownMenuItem( + child: Text(''), + value: null, + ), + DropdownMenuItem( + child: Text(falseLabel), + value: false, + ), + DropdownMenuItem( + child: Text(trueLabel), + value: true, + ), + ].toList(), + ), + ) + : Padding( + padding: const EdgeInsets.only(top: 4), + child: Flex( + direction: isDesktop(context) ? Axis.horizontal : Axis.vertical, + children: [ + InkWell( + onTap: () => onChanged(false), + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: minWidth ?? 130, minHeight: 36), + child: Row( + children: [ + IgnorePointer( + child: Radio( + value: false, + onChanged: (value) => null, + groupValue: value, + activeColor: + Theme.of(context).colorScheme.secondary, + ), + ), + Text(falseLabel), + SizedBox(width: 16), + ], + ), + ), + ), + InkWell( + onTap: () => onChanged(true), + child: ConstrainedBox( + constraints: BoxConstraints( + minWidth: minWidth ?? 120, minHeight: 36), + child: Row( + children: [ + IgnorePointer( + child: Radio( + value: true, + onChanged: (value) => null, + groupValue: value, + activeColor: + Theme.of(context).colorScheme.secondary, + ), + ), + Text(trueLabel), + SizedBox(width: 16), + ], + ), + ), + ), + ], + ), + ); + + if (label == null) { + return widget; + } + return InputDecorator( decoration: InputDecoration( border: _showBlank ? null : InputBorder.none, labelText: label, ), isEmpty: '${value ?? ''}'.isEmpty, - child: _showBlank - ? DropdownButtonHideUnderline( - child: DropdownButton( - value: value, - isExpanded: true, - isDense: true, - onChanged: (value) => onChanged(value), - items: [ - DropdownMenuItem( - child: Text(''), - value: null, - ), - DropdownMenuItem( - child: Text(falseLabel), - value: false, - ), - DropdownMenuItem( - child: Text(trueLabel), - value: true, - ), - ].toList(), - ), - ) - : Padding( - padding: const EdgeInsets.only(top: 4), - child: Flex( - direction: - isDesktop(context) ? Axis.horizontal : Axis.vertical, - children: [ - InkWell( - onTap: () => onChanged(false), - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: minWidth ?? 130, minHeight: 36), - child: Row( - children: [ - IgnorePointer( - child: Radio( - value: false, - onChanged: (value) => null, - groupValue: value, - activeColor: - Theme.of(context).colorScheme.secondary, - ), - ), - Text(falseLabel), - SizedBox(width: 16), - ], - ), - ), - ), - InkWell( - onTap: () => onChanged(true), - child: ConstrainedBox( - constraints: BoxConstraints( - minWidth: minWidth ?? 120, minHeight: 36), - child: Row( - children: [ - IgnorePointer( - child: Radio( - value: true, - onChanged: (value) => null, - groupValue: value, - activeColor: - Theme.of(context).colorScheme.secondary, - ), - ), - Text(trueLabel), - SizedBox(width: 16), - ], - ), - ), - ), - ], - ), - )); + child: widget); } } diff --git a/lib/ui/transaction/view/transaction_view.dart b/lib/ui/transaction/view/transaction_view.dart index a1172bca9..fced969ce 100644 --- a/lib/ui/transaction/view/transaction_view.dart +++ b/lib/ui/transaction/view/transaction_view.dart @@ -11,6 +11,7 @@ import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart'; import 'package:invoiceninja_flutter/ui/app/buttons/elevated_button.dart'; import 'package:invoiceninja_flutter/ui/app/entities/entity_list_tile.dart'; import 'package:invoiceninja_flutter/ui/app/entity_header.dart'; +import 'package:invoiceninja_flutter/ui/app/forms/bool_dropdown_button.dart'; import 'package:invoiceninja_flutter/ui/app/forms/date_picker.dart'; import 'package:invoiceninja_flutter/ui/app/forms/decorated_form_field.dart'; import 'package:invoiceninja_flutter/ui/app/lists/list_divider.dart'; @@ -151,6 +152,7 @@ class _MatchDepositsState extends State<_MatchDeposits> { List _invoices; List _selectedInvoices; + bool _matchExisting = false; bool _showFilter = false; String _minAmount = ''; String _maxAmount = ''; @@ -284,12 +286,24 @@ class _MatchDepositsState extends State<_MatchDeposits> { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), + child: BoolDropdownButton( + value: _matchExisting, + onChanged: (value) { + setState(() => _matchExisting = value); + }, + enabledLabel: localization.matchPayment, + disabledLabel: localization.createPayment, + ), + ), + ListDivider(), Row( children: [ Expanded( child: Padding( padding: const EdgeInsets.only( - left: 18, top: 12, right: 10, bottom: 12), + left: 20, top: 12, right: 10, bottom: 12), child: SearchText( filterController: _filterController, focusNode: _focusNode, @@ -304,8 +318,10 @@ class _MatchDepositsState extends State<_MatchDeposits> { updateInvoiceList(); }); }, - placeholder: - localization.searchInvoices.replaceFirst(':count ', ''), + placeholder: (_matchExisting + ? localization.searchPayments + : localization.searchInvoices) + .replaceFirst(':count ', ''), ), ), ), @@ -477,6 +493,7 @@ class _MatchWithdrawalsState extends State<_MatchWithdrawals> { final _vendorScrollController = ScrollController(); final _categoryScrollController = ScrollController(); + bool _matchExisting = false; TextEditingController _vendorFilterController; TextEditingController _categoryFilterController; FocusNode _vendorFocusNode; @@ -605,6 +622,18 @@ class _MatchWithdrawalsState extends State<_MatchWithdrawals> { mainAxisSize: MainAxisSize.max, crossAxisAlignment: CrossAxisAlignment.stretch, children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12), + child: BoolDropdownButton( + value: _matchExisting, + onChanged: (value) { + setState(() => _matchExisting = value); + }, + enabledLabel: localization.matchExpense, + disabledLabel: localization.createExpense, + ), + ), + ListDivider(), Expanded( child: Column( children: [ @@ -613,7 +642,7 @@ class _MatchWithdrawalsState extends State<_MatchWithdrawals> { Expanded( child: Padding( padding: const EdgeInsets.only( - left: 18, top: 12, right: 10, bottom: 12), + left: 20, top: 12, right: 10, bottom: 12), child: SearchText( filterController: _vendorFilterController, focusNode: _vendorFocusNode, @@ -700,7 +729,7 @@ class _MatchWithdrawalsState extends State<_MatchWithdrawals> { Expanded( child: Padding( padding: const EdgeInsets.only( - left: 18, top: 12, right: 10, bottom: 12), + left: 20, top: 12, right: 10, bottom: 12), child: SearchText( filterController: _categoryFilterController, focusNode: _categoryFocusNode, diff --git a/lib/utils/i18n.dart b/lib/utils/i18n.dart index b104fd299..39a7787f1 100644 --- a/lib/utils/i18n.dart +++ b/lib/utils/i18n.dart @@ -16,8 +16,11 @@ mixin LocalizationsProvider on LocaleCodeAware { static final Map> _localizedValues = { 'en': { // STARTER: lang key - do not remove comment + 'match_payment': 'Match Payment', + 'match_expense': 'Match Expense', 'lock_invoiced_tasks': 'Lock Invoiced Tasks', - 'lock_invoiced_tasks_help': 'Prevent tasks from being edited once invoiced', + 'lock_invoiced_tasks_help': + 'Prevent tasks from being edited once invoiced', 'registration_required': 'Registration Required', 'registration_required_help': 'Require clients to register', 'use_inventory_management': 'Use Inventory Management', @@ -90617,6 +90620,14 @@ mixin LocalizationsProvider on LocaleCodeAware { _localizedValues[localeCode]['lock_invoiced_tasks_help'] ?? _localizedValues['en']['lock_invoiced_tasks_help']; + String get matchPayment => + _localizedValues[localeCode]['match_payment'] ?? + _localizedValues['en']['match_payment']; + + String get matchExpense => + _localizedValues[localeCode]['match_expense'] ?? + _localizedValues['en']['match_expense']; + // STARTER: lang field - do not remove comment String lookup(String key) {