Payments
This commit is contained in:
parent
a81cb5891f
commit
d04386974b
|
|
@ -198,3 +198,10 @@ class FilterPaymentsByCustom2 implements PersistUI {
|
|||
|
||||
FilterPaymentsByCustom2(this.value);
|
||||
}
|
||||
|
||||
class FilterPaymentsByClient implements PersistUI {
|
||||
final int clientId;
|
||||
|
||||
FilterPaymentsByClient([this.clientId]);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,17 +68,27 @@ class ClientOverview extends StatelessWidget {
|
|||
EntityListTile(
|
||||
icon: FontAwesomeIcons.filePdfO,
|
||||
title: localization.invoices,
|
||||
onTap: () => viewModel.onInvoicesPressed(context),
|
||||
onTap: () => viewModel.onEntityPressed(context, EntityType.invoice),
|
||||
subtitle: memoizedInvoiceStatsForClient(
|
||||
client.id,
|
||||
state.invoiceState.map,
|
||||
localization.active,
|
||||
localization.archived),
|
||||
),
|
||||
EntityListTile(
|
||||
icon: FontAwesomeIcons.creditCard,
|
||||
title: localization.payments,
|
||||
onTap: () => viewModel.onEntityPressed(context, EntityType.payment),
|
||||
subtitle: memoizedQuoteStatsForClient(
|
||||
client.id,
|
||||
state.quoteState.map,
|
||||
localization.active,
|
||||
localization.archived),
|
||||
),
|
||||
company.isModuleEnabled(EntityType.quote) ? EntityListTile(
|
||||
icon: FontAwesomeIcons.fileAltO,
|
||||
title: localization.quotes,
|
||||
onTap: () => viewModel.onQuotesPressed(context),
|
||||
onTap: () => viewModel.onEntityPressed(context, EntityType.quote),
|
||||
subtitle: memoizedQuoteStatsForClient(
|
||||
client.id,
|
||||
state.quoteState.map,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import 'package:flutter/foundation.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_redux/flutter_redux.dart';
|
||||
import 'package:invoiceninja_flutter/redux/invoice/invoice_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/payment/payment_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/quote/quote_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/ui_actions.dart';
|
||||
import 'package:invoiceninja_flutter/ui/client/client_screen.dart';
|
||||
|
|
@ -42,8 +43,7 @@ class ClientViewVM {
|
|||
final Function(BuildContext, EntityAction) onActionSelected;
|
||||
final Function(BuildContext) onEditPressed;
|
||||
final Function onBackPressed;
|
||||
final Function(BuildContext) onInvoicesPressed;
|
||||
final Function(BuildContext) onQuotesPressed;
|
||||
final Function(BuildContext, EntityType) onEntityPressed;
|
||||
final Function(BuildContext, bool) onRefreshed;
|
||||
final bool isSaving;
|
||||
final bool isLoading;
|
||||
|
|
@ -53,9 +53,8 @@ class ClientViewVM {
|
|||
@required this.client,
|
||||
@required this.company,
|
||||
@required this.onActionSelected,
|
||||
@required this.onEntityPressed,
|
||||
@required this.onEditPressed,
|
||||
@required this.onInvoicesPressed,
|
||||
@required this.onQuotesPressed,
|
||||
@required this.onBackPressed,
|
||||
@required this.isSaving,
|
||||
@required this.isLoading,
|
||||
|
|
@ -94,13 +93,21 @@ class ClientViewVM {
|
|||
)));
|
||||
});
|
||||
},
|
||||
onInvoicesPressed: (BuildContext context) {
|
||||
onEntityPressed: (BuildContext context, EntityType entityType) {
|
||||
switch (entityType) {
|
||||
case EntityType.invoice:
|
||||
store.dispatch(FilterInvoicesByClient(client.id));
|
||||
store.dispatch(ViewInvoiceList(context));
|
||||
},
|
||||
onQuotesPressed: (BuildContext context) {
|
||||
break;
|
||||
case EntityType.quote:
|
||||
store.dispatch(FilterQuotesByClient(client.id));
|
||||
store.dispatch(ViewQuoteList(context));
|
||||
break;
|
||||
case EntityType.payment:
|
||||
store.dispatch(FilterPaymentsByClient(client.id));
|
||||
store.dispatch(ViewPaymentList(context));
|
||||
break;
|
||||
}
|
||||
},
|
||||
onRefreshed: (context, loadActivities) =>
|
||||
_handleRefresh(context, loadActivities),
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/payment_model.dart';
|
||||
import 'package:invoiceninja_flutter/data/models/models.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/loading_indicator.dart';
|
||||
import 'package:invoiceninja_flutter/ui/app/snackbar_row.dart';
|
||||
|
|
@ -15,32 +16,21 @@ class PaymentList extends StatelessWidget {
|
|||
@required this.viewModel,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (!viewModel.isLoaded) {
|
||||
return LoadingIndicator();
|
||||
} else if (viewModel.paymentList.isEmpty) {
|
||||
return Opacity(
|
||||
opacity: 0.5,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalization.of(context).noRecordsFound,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return _buildListView(context);
|
||||
}
|
||||
|
||||
void _showMenu(BuildContext context, PaymentEntity payment) async {
|
||||
void _showMenu(
|
||||
BuildContext context, PaymentEntity payment, ClientEntity client) async {
|
||||
final user = viewModel.user;
|
||||
final message = await showDialog<String>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => SimpleDialog(children: <Widget>[
|
||||
user.canEditEntity(payment) && client.hasEmailAddress
|
||||
? ListTile(
|
||||
leading: Icon(Icons.send),
|
||||
title: Text(AppLocalization.of(context).email),
|
||||
onTap: () => viewModel.onEntityAction(
|
||||
context, payment, EntityAction.email),
|
||||
)
|
||||
: Container(),
|
||||
Divider(),
|
||||
user.canEditEntity(payment) && !payment.isActive
|
||||
? ListTile(
|
||||
leading: Icon(Icons.restore),
|
||||
|
|
@ -74,29 +64,96 @@ class PaymentList extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
Widget _buildListView(BuildContext context) {
|
||||
return RefreshIndicator(
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final localization = AppLocalization.of(context);
|
||||
final listState = viewModel.listState;
|
||||
final filteredClientId = listState.filterClientId;
|
||||
final filteredClient =
|
||||
filteredClientId != null ? viewModel.clientMap[filteredClientId] : null;
|
||||
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
filteredClient != null
|
||||
? Material(
|
||||
color: Colors.orangeAccent,
|
||||
elevation: 6.0,
|
||||
child: InkWell(
|
||||
onTap: () => viewModel.onViewClientFilterPressed(context),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
SizedBox(width: 18.0),
|
||||
Expanded(
|
||||
child: Text(
|
||||
localization.clientsPayments.replaceFirst(
|
||||
':client', filteredClient.displayName),
|
||||
style: TextStyle(
|
||||
color: Colors.white,
|
||||
fontSize: 16.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: Icon(
|
||||
Icons.close,
|
||||
color: Colors.white,
|
||||
),
|
||||
onPressed: () => viewModel.onClearClientFilterPressed(),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
)
|
||||
: Container(),
|
||||
Expanded(
|
||||
child: !viewModel.isLoaded
|
||||
? LoadingIndicator()
|
||||
: RefreshIndicator(
|
||||
onRefresh: () => viewModel.onRefreshed(context),
|
||||
child: ListView.builder(
|
||||
child: viewModel.paymentList.isEmpty
|
||||
? Opacity(
|
||||
opacity: 0.5,
|
||||
child: Center(
|
||||
child: Text(
|
||||
AppLocalization.of(context).noRecordsFound,
|
||||
style: TextStyle(
|
||||
fontSize: 18.0,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
shrinkWrap: true,
|
||||
itemCount: viewModel.paymentList.length,
|
||||
itemBuilder: (BuildContext context, index) {
|
||||
final paymentId = viewModel.paymentList[index];
|
||||
final payment = viewModel.paymentMap[paymentId];
|
||||
return Column(children: <Widget>[
|
||||
final client =
|
||||
viewModel.clientMap[payment.clientId];
|
||||
return Column(
|
||||
children: <Widget>[
|
||||
PaymentListItem(
|
||||
user: viewModel.user,
|
||||
filter: viewModel.filter,
|
||||
payment: payment,
|
||||
onDismissed: (DismissDirection direction) =>
|
||||
viewModel.onDismissed(context, payment, direction),
|
||||
onTap: () => viewModel.onPaymentTap(context, payment),
|
||||
onLongPress: () => _showMenu(context, payment),
|
||||
viewModel.onDismissed(
|
||||
context, payment, direction),
|
||||
onTap: () =>
|
||||
viewModel.onPaymentTap(context, payment),
|
||||
onLongPress: () =>
|
||||
_showMenu(context, payment, client),
|
||||
),
|
||||
Divider(
|
||||
height: 1.0,
|
||||
),
|
||||
]);
|
||||
}),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
import 'dart:async';
|
||||
import 'package:invoiceninja_flutter/redux/client/client_actions.dart';
|
||||
import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
|
||||
import 'package:redux/redux.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
@ -31,20 +33,25 @@ class PaymentListBuilder extends StatelessWidget {
|
|||
|
||||
class PaymentListVM {
|
||||
final UserEntity user;
|
||||
final ListUIState listState;
|
||||
final List<int> paymentList;
|
||||
final BuiltMap<int, PaymentEntity> paymentMap;
|
||||
final BuiltMap<int, ClientEntity> clientMap;
|
||||
final String filter;
|
||||
final bool isLoading;
|
||||
final bool isLoaded;
|
||||
final Function(BuildContext, PaymentEntity) onPaymentTap;
|
||||
final Function(BuildContext, PaymentEntity, DismissDirection) onDismissed;
|
||||
final Function(BuildContext) onRefreshed;
|
||||
final Function onClearClientFilterPressed;
|
||||
final Function(BuildContext) onViewClientFilterPressed;
|
||||
final Function(BuildContext, PaymentEntity, EntityAction) onEntityAction;
|
||||
|
||||
PaymentListVM({
|
||||
@required this.user,
|
||||
@required this.paymentList,
|
||||
@required this.paymentMap,
|
||||
@required this.clientMap,
|
||||
@required this.filter,
|
||||
@required this.isLoading,
|
||||
@required this.isLoaded,
|
||||
|
|
@ -52,6 +59,9 @@ class PaymentListVM {
|
|||
@required this.onDismissed,
|
||||
@required this.onRefreshed,
|
||||
@required this.onEntityAction,
|
||||
@required this.onClearClientFilterPressed,
|
||||
@required this.onViewClientFilterPressed,
|
||||
@required this.listState,
|
||||
});
|
||||
|
||||
static PaymentListVM fromStore(Store<AppState> store) {
|
||||
|
|
@ -72,9 +82,11 @@ class PaymentListVM {
|
|||
paymentList: memoizedFilteredPaymentList(state.paymentState.map,
|
||||
state.paymentState.list, state.paymentListState),
|
||||
paymentMap: state.paymentState.map,
|
||||
clientMap: state.clientState.map,
|
||||
isLoading: state.isLoading,
|
||||
isLoaded: state.paymentState.isLoaded,
|
||||
filter: state.paymentUIState.listUIState.filter,
|
||||
listState: state.paymentListState,
|
||||
onPaymentTap: (context, payment) {
|
||||
store.dispatch(ViewPayment(paymentId: payment.id, context: context));
|
||||
},
|
||||
|
|
@ -100,6 +112,12 @@ class PaymentListVM {
|
|||
break;
|
||||
}
|
||||
},
|
||||
onClearClientFilterPressed: () =>
|
||||
store.dispatch(FilterPaymentsByClient()),
|
||||
onViewClientFilterPressed: (BuildContext context) => store.dispatch(
|
||||
ViewClient(
|
||||
clientId: state.paymentListState.filterClientId,
|
||||
context: context)),
|
||||
onRefreshed: (context) => _handleRefresh(context),
|
||||
onDismissed: (BuildContext context, PaymentEntity payment,
|
||||
DismissDirection direction) {
|
||||
|
|
|
|||
|
|
@ -153,6 +153,7 @@ class AppLocalization {
|
|||
'please_enter_an_invoice_number': 'Please enter an invoice number',
|
||||
'please_enter_a_quote_number': 'Please enter a quote number',
|
||||
'clients_invoices': ':client\'s invoices',
|
||||
'clients_payments': ':client\'s payments',
|
||||
'past_due': 'Past Due',
|
||||
'draft': 'Draft',
|
||||
'sent': 'Sent',
|
||||
|
|
@ -7418,6 +7419,9 @@ class AppLocalization {
|
|||
String get clientsInvoices =>
|
||||
_localizedValues[locale.languageCode]['clients_invoices'];
|
||||
|
||||
String get clientsPayments =>
|
||||
_localizedValues[locale.languageCode]['clients_payments'];
|
||||
|
||||
String get pastDue => _localizedValues[locale.languageCode]['past_due'];
|
||||
|
||||
String get draft => _localizedValues[locale.languageCode]['draft'];
|
||||
|
|
|
|||
|
|
@ -198,3 +198,9 @@ class FilterStubsByCustom2 implements PersistUI {
|
|||
|
||||
FilterStubsByCustom2(this.value);
|
||||
}
|
||||
|
||||
class FilterStubsByClient implements PersistUI {
|
||||
final int clientId;
|
||||
|
||||
FilterStubsByClient([this.clientId]);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue