diff --git a/lib/data/models/purchase_order_model.dart b/lib/data/models/purchase_order_model.dart index db8e320c0..f4bf8e6c5 100644 --- a/lib/data/models/purchase_order_model.dart +++ b/lib/data/models/purchase_order_model.dart @@ -13,7 +13,7 @@ class PurchaseOrderFields { static const String discount = 'discount'; static const String poNumber = 'po_number'; static const String date = 'date'; - static const String dueDate = 'due_date'; + static const String validUntil = 'valid_until'; static const String nextSendDate = 'next_send_date'; static const String lastSentDate = 'last_sent_date'; static const String terms = 'terms'; diff --git a/lib/redux/purchase_order/purchase_order_selectors.dart b/lib/redux/purchase_order/purchase_order_selectors.dart index b45966b02..3a80a6bc0 100644 --- a/lib/redux/purchase_order/purchase_order_selectors.dart +++ b/lib/redux/purchase_order/purchase_order_selectors.dart @@ -6,6 +6,23 @@ import 'package:built_collection/built_collection.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; +ClientEntity purchaseOrderClientSelector( + InvoiceEntity purchaseOrder, BuiltMap clientMap) { + return clientMap[purchaseOrder.clientId]; +} + +ClientContactEntity purchaseOrderContactSelector( + InvoiceEntity purchaseOrder, ClientEntity client) { + var contactIds = purchaseOrder.invitations + .map((invitation) => invitation.clientContactId) + .toList(); + if (contactIds.contains(client.primaryContact.id)) { + contactIds = [client.primaryContact.id]; + } + return client.contacts + .firstWhere((contact) => contactIds.contains(contact.id), orElse: null); +} + var memoizedDropdownPurchaseOrderList = memo6( (BuiltMap purchaseOrderMap, BuiltList purchaseOrderList, diff --git a/lib/ui/app/tables/entity_list.dart b/lib/ui/app/tables/entity_list.dart index 62158ed16..07cc08ad6 100644 --- a/lib/ui/app/tables/entity_list.dart +++ b/lib/ui/app/tables/entity_list.dart @@ -219,10 +219,6 @@ class _EntityListState extends State { ], ); } else { - if (widget.tableColumns.isEmpty) { - return SizedBox(); - } - final rowsPerPage = state.prefState.rowsPerPage; return Column( diff --git a/lib/ui/purchase_order/purchase_order_presenter.dart b/lib/ui/purchase_order/purchase_order_presenter.dart index fef86daa9..912dae3d9 100644 --- a/lib/ui/purchase_order/purchase_order_presenter.dart +++ b/lib/ui/purchase_order/purchase_order_presenter.dart @@ -1,25 +1,157 @@ +// Flutter imports: import 'package:flutter/material.dart'; + +// Package imports: +import 'package:flutter_redux/flutter_redux.dart'; + +// Project imports: import 'package:invoiceninja_flutter/data/models/models.dart'; +import 'package:invoiceninja_flutter/data/models/purchase_order_model.dart'; +import 'package:invoiceninja_flutter/redux/app/app_state.dart'; +import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_selectors.dart'; +import 'package:invoiceninja_flutter/ui/app/copy_to_clipboard.dart'; +import 'package:invoiceninja_flutter/ui/app/entities/entity_status_chip.dart'; +import 'package:invoiceninja_flutter/ui/app/link_text.dart'; import 'package:invoiceninja_flutter/ui/app/presenters/entity_presenter.dart'; +import 'package:invoiceninja_flutter/utils/formatting.dart'; +import 'package:invoiceninja_flutter/utils/localization.dart'; +import 'package:url_launcher/url_launcher.dart'; class PurchaseOrderPresenter extends EntityPresenter { static List getDefaultTableFields(UserCompanyEntity userCompany) { - return []; + return [ + PurchaseOrderFields.status, + PurchaseOrderFields.number, + PurchaseOrderFields.client, + PurchaseOrderFields.amount, + PurchaseOrderFields.date, + PurchaseOrderFields.validUntil, + ]; } static List getAllTableFields(UserCompanyEntity userCompany) { return [ ...getDefaultTableFields(userCompany), ...EntityPresenter.getBaseFields(), + PurchaseOrderFields.discount, + PurchaseOrderFields.poNumber, + PurchaseOrderFields.publicNotes, + PurchaseOrderFields.privateNotes, + PurchaseOrderFields.documents, + PurchaseOrderFields.customValue1, + PurchaseOrderFields.customValue2, + PurchaseOrderFields.customValue3, + PurchaseOrderFields.customValue4, + PurchaseOrderFields.taxAmount, + PurchaseOrderFields.exchangeRate, + PurchaseOrderFields.isViewed, + PurchaseOrderFields.lastSentDate, + PurchaseOrderFields.project, + PurchaseOrderFields.vendor, + PurchaseOrderFields.contactName, + PurchaseOrderFields.contactEmail, + PurchaseOrderFields.clientState, + PurchaseOrderFields.clientCity, + PurchaseOrderFields.clientPostalCode, + PurchaseOrderFields.clientCountry, + PurchaseOrderFields.partial, + PurchaseOrderFields.partialDueDate, ]; } @override Widget getField({String field, BuildContext context}) { - //final state = StoreProvider.of(context).state; - //final purchaseOrder = entity as InvoiceEntity; + final localization = AppLocalization.of(context); + final state = StoreProvider.of(context).state; + final purchaseOrder = entity as InvoiceEntity; + final client = state.clientState.get(purchaseOrder.clientId); switch (field) { + case PurchaseOrderFields.status: + return EntityStatusChip(entity: purchaseOrder, showState: true); + case PurchaseOrderFields.number: + return Text((purchaseOrder.number ?? '').isEmpty + ? localization.pending + : purchaseOrder.number); + case PurchaseOrderFields.client: + return LinkTextRelatedEntity(entity: client, relation: purchaseOrder); + case PurchaseOrderFields.date: + return Text(formatDate(purchaseOrder.date, context)); + case PurchaseOrderFields.lastSentDate: + return Text(formatDate(purchaseOrder.lastSentDate, context)); + case PurchaseOrderFields.amount: + return Align( + alignment: Alignment.centerRight, + child: Text(formatNumber(purchaseOrder.amount, context, + clientId: purchaseOrder.clientId)), + ); + case PurchaseOrderFields.validUntil: + return Text(formatDate(purchaseOrder.dueDate, context)); + case PurchaseOrderFields.customValue1: + return Text(presentCustomField(context, purchaseOrder.customValue1)); + case PurchaseOrderFields.customValue2: + return Text(presentCustomField(context, purchaseOrder.customValue2)); + case PurchaseOrderFields.customValue3: + return Text(presentCustomField(context, purchaseOrder.customValue3)); + case PurchaseOrderFields.customValue4: + return Text(presentCustomField(context, purchaseOrder.customValue4)); + case PurchaseOrderFields.publicNotes: + return TableTooltip(message: purchaseOrder.publicNotes); + case PurchaseOrderFields.privateNotes: + return TableTooltip(message: purchaseOrder.privateNotes); + case PurchaseOrderFields.discount: + return Text(purchaseOrder.isAmountDiscount + ? formatNumber(purchaseOrder.discount, context, + formatNumberType: FormatNumberType.money, + clientId: purchaseOrder.clientId) + : formatNumber(purchaseOrder.discount, context, + formatNumberType: FormatNumberType.percent)); + case PurchaseOrderFields.poNumber: + return Text(purchaseOrder.poNumber); + case PurchaseOrderFields.documents: + return Text('${purchaseOrder.documents.length}'); + case PurchaseOrderFields.taxAmount: + return Text(formatNumber(purchaseOrder.taxAmount, context, + clientId: purchaseOrder.clientId)); + case PurchaseOrderFields.exchangeRate: + return Text(formatNumber(purchaseOrder.exchangeRate, context, + formatNumberType: FormatNumberType.double)); + case PurchaseOrderFields.isViewed: + return Text( + purchaseOrder.isViewed ? localization.yes : localization.no); + case PurchaseOrderFields.project: + final project = state.projectState.get(purchaseOrder.projectId); + return LinkTextRelatedEntity(entity: project, relation: purchaseOrder); + case PurchaseOrderFields.vendor: + final vendor = state.vendorState.get(purchaseOrder.vendorId); + return LinkTextRelatedEntity(entity: vendor, relation: purchaseOrder); + case PurchaseOrderFields.clientState: + return Text(client.state); + case PurchaseOrderFields.clientCity: + return Text(client.city); + case PurchaseOrderFields.clientPostalCode: + return Text(client.postalCode); + case PurchaseOrderFields.clientCountry: + return Text(state.staticState.countryMap[client.countryId]?.name ?? ''); + case PurchaseOrderFields.contactName: + case PurchaseOrderFields.contactEmail: + final contact = purchaseOrderContactSelector( + purchaseOrder, state.clientState.get(purchaseOrder.clientId)); + if (contact == null) { + return SizedBox(); + } + if (field == PurchaseOrderFields.contactName) { + return Text(contact.fullName); + } + return CopyToClipboard( + value: contact.email, + showBorder: true, + onLongPress: () => launch('mailto:${contact.email}'), + ); + case PurchaseOrderFields.partial: + return Text(formatNumber(purchaseOrder.partial, context)); + case PurchaseOrderFields.partialDueDate: + return Text(formatDate(purchaseOrder.partialDueDate, context)); } return super.getField(field: field, context: context); diff --git a/lib/ui/purchase_order/purchase_order_screen.dart b/lib/ui/purchase_order/purchase_order_screen.dart index 079ac324b..1750ef8dc 100644 --- a/lib/ui/purchase_order/purchase_order_screen.dart +++ b/lib/ui/purchase_order/purchase_order_screen.dart @@ -96,7 +96,7 @@ class PurchaseOrderScreen extends StatelessWidget { sortFields: [ PurchaseOrderFields.number, PurchaseOrderFields.date, - PurchaseOrderFields.dueDate, + PurchaseOrderFields.validUntil, EntityFields.updatedAt, ], onSelectedState: (EntityState state, value) {