diff --git a/lib/data/models/client_model.dart b/lib/data/models/client_model.dart index 4505b722a17..dd6cd52bef0 100644 --- a/lib/data/models/client_model.dart +++ b/lib/data/models/client_model.dart @@ -648,6 +648,10 @@ abstract class ClientEntity extends Object actions.add(EntityAction.newInvoice); } + if (userCompany.canCreate(EntityType.quote)) { + actions.add(EntityAction.newQuote); + } + if (userCompany.canCreate(EntityType.payment)) { actions.add(EntityAction.newPayment); } diff --git a/lib/data/models/transaction_model.dart b/lib/data/models/transaction_model.dart index 96481b16244..7eba91ba5f1 100644 --- a/lib/data/models/transaction_model.dart +++ b/lib/data/models/transaction_model.dart @@ -139,7 +139,13 @@ abstract class TransactionEntity extends Object } int compareTo( - TransactionEntity transaction, String sortField, bool sortAscending) { + TransactionEntity transaction, + String sortField, + bool sortAscending, + BuiltMap invoiceMap, + BuiltMap expenseMap, + BuiltMap bankAccountMap, + ) { int response = 0; final transactionA = sortAscending ? this : transaction; final transactionB = sortAscending ? transaction : this; @@ -161,6 +167,29 @@ abstract class TransactionEntity extends Object case TransactionFields.date: response = transactionA.date.compareTo(transactionB.date); break; + case TransactionFields.invoice: + final invoiceA = invoiceMap[transactionA.invoiceId] ?? InvoiceEntity(); + final invoiceB = invoiceMap[transactionB.invoiceId] ?? InvoiceEntity(); + response = invoiceA.listDisplayName + .toLowerCase() + .compareTo(invoiceB.listDisplayName.toLowerCase()); + break; + case TransactionFields.expense: + final expenseA = expenseMap[transactionA.expenseId] ?? ExpenseEntity(); + final expenseB = expenseMap[transactionB.expenseId] ?? ExpenseEntity(); + response = expenseA.listDisplayName + .toLowerCase() + .compareTo(expenseB.listDisplayName.toLowerCase()); + break; + case TransactionFields.bankAccount: + final bankAccountA = + bankAccountMap[transactionA.bankAccountId] ?? BankAccountEntity(); + final bankAccountB = + bankAccountMap[transactionB.bankAccountId] ?? BankAccountEntity(); + response = bankAccountA.listDisplayName + .toLowerCase() + .compareTo(bankAccountB.listDisplayName.toLowerCase()); + break; default: print('## ERROR: sort by transaction.$sortField is not implemented'); break; diff --git a/lib/redux/app/app_state.dart b/lib/redux/app/app_state.dart index 9ff36d7034d..cfe89d15220 100644 --- a/lib/redux/app/app_state.dart +++ b/lib/redux/app/app_state.dart @@ -17,58 +17,36 @@ import 'package:invoiceninja_flutter/constants.dart'; import 'package:invoiceninja_flutter/data/models/account_model.dart'; import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/auth/auth_state.dart'; -import 'package:invoiceninja_flutter/redux/client/client_selectors.dart'; import 'package:invoiceninja_flutter/redux/client/client_state.dart'; import 'package:invoiceninja_flutter/redux/company/company_state.dart'; -import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_selectors.dart'; import 'package:invoiceninja_flutter/redux/company_gateway/company_gateway_state.dart'; -import 'package:invoiceninja_flutter/redux/credit/credit_selectors.dart'; import 'package:invoiceninja_flutter/redux/credit/credit_state.dart'; import 'package:invoiceninja_flutter/redux/dashboard/dashboard_state.dart'; -import 'package:invoiceninja_flutter/redux/design/design_selectors.dart'; import 'package:invoiceninja_flutter/redux/design/design_state.dart'; import 'package:invoiceninja_flutter/redux/document/document_state.dart'; -import 'package:invoiceninja_flutter/redux/expense/expense_selectors.dart'; import 'package:invoiceninja_flutter/redux/expense/expense_state.dart'; -import 'package:invoiceninja_flutter/redux/expense_category/expense_category_selectors.dart'; import 'package:invoiceninja_flutter/redux/expense_category/expense_category_state.dart'; -import 'package:invoiceninja_flutter/redux/group/group_selectors.dart'; import 'package:invoiceninja_flutter/redux/group/group_state.dart'; -import 'package:invoiceninja_flutter/redux/invoice/invoice_selectors.dart'; import 'package:invoiceninja_flutter/redux/invoice/invoice_state.dart'; -import 'package:invoiceninja_flutter/redux/payment/payment_selectors.dart'; import 'package:invoiceninja_flutter/redux/payment/payment_state.dart'; -import 'package:invoiceninja_flutter/redux/payment_term/payment_term_selectors.dart'; import 'package:invoiceninja_flutter/redux/payment_term/payment_term_state.dart'; -import 'package:invoiceninja_flutter/redux/product/product_selectors.dart'; import 'package:invoiceninja_flutter/redux/product/product_state.dart'; -import 'package:invoiceninja_flutter/redux/project/project_selectors.dart'; import 'package:invoiceninja_flutter/redux/project/project_state.dart'; -import 'package:invoiceninja_flutter/redux/quote/quote_selectors.dart'; import 'package:invoiceninja_flutter/redux/quote/quote_state.dart'; -import 'package:invoiceninja_flutter/redux/recurring_expense/recurring_expense_selectors.dart'; import 'package:invoiceninja_flutter/redux/recurring_expense/recurring_expense_state.dart'; -import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_selectors.dart'; import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_state.dart'; import 'package:invoiceninja_flutter/redux/static/static_state.dart'; -import 'package:invoiceninja_flutter/redux/subscription/subscription_selectors.dart'; import 'package:invoiceninja_flutter/redux/subscription/subscription_state.dart'; -import 'package:invoiceninja_flutter/redux/task/task_selectors.dart'; import 'package:invoiceninja_flutter/redux/task/task_state.dart'; -import 'package:invoiceninja_flutter/redux/task_status/task_status_selectors.dart'; import 'package:invoiceninja_flutter/redux/task_status/task_status_state.dart'; -import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_selectors.dart'; import 'package:invoiceninja_flutter/redux/tax_rate/tax_rate_state.dart'; -import 'package:invoiceninja_flutter/redux/token/token_selectors.dart'; import 'package:invoiceninja_flutter/redux/token/token_state.dart'; import 'package:invoiceninja_flutter/redux/ui/entity_ui_state.dart'; import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart'; import 'package:invoiceninja_flutter/redux/ui/pref_state.dart'; import 'package:invoiceninja_flutter/redux/ui/ui_state.dart'; import 'package:invoiceninja_flutter/redux/user/user_state.dart'; -import 'package:invoiceninja_flutter/redux/vendor/vendor_selectors.dart'; import 'package:invoiceninja_flutter/redux/vendor/vendor_state.dart'; -import 'package:invoiceninja_flutter/redux/webhook/webhook_selectors.dart'; import 'package:invoiceninja_flutter/redux/webhook/webhook_state.dart'; import 'package:invoiceninja_flutter/ui/app/screen_imports.dart'; import 'package:invoiceninja_flutter/ui/credit/credit_screen.dart'; @@ -87,15 +65,12 @@ import 'package:invoiceninja_flutter/ui/webhook/edit/webhook_edit_vm.dart'; import 'package:invoiceninja_flutter/utils/colors.dart'; import 'package:invoiceninja_flutter/utils/formatting.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart'; -// STARTER: import - do not remove comment import 'package:invoiceninja_flutter/redux/transaction/transaction_state.dart'; import 'package:invoiceninja_flutter/ui/transaction/edit/transaction_edit_vm.dart'; -import 'package:invoiceninja_flutter/redux/transaction/transaction_selectors.dart'; - import 'package:invoiceninja_flutter/redux/bank_account/bank_account_state.dart'; import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_state.dart'; import 'package:invoiceninja_flutter/ui/purchase_order/edit/purchase_order_edit_vm.dart'; -import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_selectors.dart'; +// STARTER: import - do not remove comment part 'app_state.g.dart'; @@ -702,64 +677,54 @@ abstract class AppState implements Built { bool hasChanges() { switch (uiState.currentRoute) { case ClientEditScreen.route: - return hasClientChanges(clientUIState.editing, clientState.map); + return clientUIState.editing.isChanged == true; case ProductEditScreen.route: - return hasProductChanges(productUIState.editing, productState.map); + return productUIState.editing.isChanged == true; case InvoiceEditScreen.route: - return hasInvoiceChanges(invoiceUIState.editing, invoiceState.map); + return invoiceUIState.editing.isChanged == true; case PaymentEditScreen.route: - return hasPaymentChanges(paymentUIState.editing, paymentState.map); + return paymentUIState.editing.isChanged == true; case QuoteEditScreen.route: - return hasQuoteChanges(quoteUIState.editing, quoteState.map); + return quoteUIState.editing.isChanged == true; case ProjectEditScreen.route: - return hasProjectChanges(projectUIState.editing, projectState.map); + return projectUIState.editing.isChanged == true; case TaskEditScreen.route: - return hasTaskChanges(taskUIState.editing, taskState.map); + return taskUIState.editing.isChanged == true; case VendorEditScreen.route: - return hasVendorChanges(vendorUIState.editing, vendorState.map); + return vendorUIState.editing.isChanged == true; case ExpenseEditScreen.route: - return hasExpenseChanges(expenseUIState.editing, expenseState.map); + return expenseUIState.editing.isChanged == true; case GroupEditScreen.route: - return hasGroupChanges(groupUIState.editing, groupState.map); + return groupUIState.editing.isChanged == true; case TaxRateEditScreen.route: - return hasTaxRateChanges(taxRateUIState.editing, taxRateState.map); + return taxRateUIState.editing.isChanged == true; case CompanyGatewayEditScreen.route: - return hasCompanyGatewayChanges( - companyGatewayUIState.editing, companyGatewayState.map); + return companyGatewayUIState.editing.isChanged == true; case CreditEditScreen.route: - return hasCreditChanges(creditUIState.editing, creditState.map); + return creditUIState.editing.isChanged == true; // STARTER: has changes - do not remove comment case TransactionEditScreen.route: - return hasTransactionChanges( - transactionUIState.editing, transactionState.map); - + return transactionUIState.editing.isChanged == true; case PurchaseOrderEditScreen.route: - return hasPurchaseOrderChanges( - purchaseOrderUIState.editing, purchaseOrderState.map); + return purchaseOrderUIState.editing.isChanged == true; case RecurringExpenseEditScreen.route: - return hasRecurringExpenseChanges( - recurringExpenseUIState.editing, recurringExpenseState.map); + return recurringExpenseUIState.editing.isChanged == true; case SubscriptionEditScreen.route: - return hasSubscriptionChanges( - subscriptionUIState.editing, subscriptionState.map); + return subscriptionUIState.editing.isChanged == true; case TaskStatusEditScreen.route: - return hasTaskStatusChanges( - taskStatusUIState.editing, taskStatusState.map); + return taskStatusUIState.editing.isChanged == true; case ExpenseCategoryEditScreen.route: - return hasExpenseCategoryChanges( - expenseCategoryUIState.editing, expenseCategoryState.map); + return expenseCategoryUIState.editing.isChanged == true; case RecurringInvoiceEditScreen.route: - return hasRecurringInvoiceChanges( - recurringInvoiceUIState.editing, recurringInvoiceState.map); + return recurringInvoiceUIState.editing.isChanged == true; case WebhookEditScreen.route: - return hasWebhookChanges(webhookUIState.editing, webhookState.map); + return webhookUIState.editing.isChanged == true; case TokenEditScreen.route: - return hasTokenChanges(tokenUIState.editing, tokenState.map); + return tokenUIState.editing.isChanged == true; case PaymentTermEditScreen.route: - return hasPaymentTermChanges( - paymentTermUIState.editing, paymentTermState.map); + return paymentTermUIState.editing.isChanged == true; case DesignEditScreen.route: - return hasDesignChanges(designUIState.editing, designState.map); + return designUIState.editing.isChanged == true; } if (uiState.isInSettings) { diff --git a/lib/redux/company_gateway/company_gateway_selectors.dart b/lib/redux/company_gateway/company_gateway_selectors.dart index 053dc401506..e481c8efb09 100644 --- a/lib/redux/company_gateway/company_gateway_selectors.dart +++ b/lib/redux/company_gateway/company_gateway_selectors.dart @@ -86,12 +86,6 @@ List filteredCompanyGatewaysSelector( return gatewaysIds; } -bool hasCompanyGatewayChanges(CompanyGatewayEntity companyGateway, - BuiltMap companyGatewayMap) => - companyGateway.isNew - ? companyGateway.isChanged - : companyGateway != companyGatewayMap[companyGateway.id]; - var memoizedCalculateCompanyGatewayProcessed = memo2( (String companyGatewayId, BuiltMap paymentMap) => calculateCompanyGatewayProcessed( diff --git a/lib/redux/credit/credit_selectors.dart b/lib/redux/credit/credit_selectors.dart index ad4bf26419e..d5b01363bbd 100644 --- a/lib/redux/credit/credit_selectors.dart +++ b/lib/redux/credit/credit_selectors.dart @@ -238,7 +238,3 @@ EntityStats creditStatsForUser( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasCreditChanges( - InvoiceEntity credit, BuiltMap creditMap) => - credit.isNew ? credit.isChanged : credit != creditMap[credit.id]; diff --git a/lib/redux/design/design_selectors.dart b/lib/redux/design/design_selectors.dart index 90d02470987..b87f6c632c5 100644 --- a/lib/redux/design/design_selectors.dart +++ b/lib/redux/design/design_selectors.dart @@ -64,10 +64,6 @@ List filteredDesignsSelector(BuiltMap designMap, return list; } -bool hasDesignChanges( - DesignEntity design, BuiltMap designMap) => - design.isNew ? design.isChanged : design != designMap[design.id]; - String getDesignIdForClientByEntity( {AppState state, String clientId, EntityType entityType}) { final client = state.clientState.get(clientId); diff --git a/lib/redux/expense/expense_selectors.dart b/lib/redux/expense/expense_selectors.dart index bf564d65224..6534a9eba1a 100644 --- a/lib/redux/expense/expense_selectors.dart +++ b/lib/redux/expense/expense_selectors.dart @@ -381,7 +381,3 @@ EntityStats expenseStatsForUser( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasExpenseChanges( - ExpenseEntity expense, BuiltMap expenseMap) => - expense.isNew ? expense.isChanged : expense != expenseMap[expense.id]; diff --git a/lib/redux/expense_category/expense_category_selectors.dart b/lib/redux/expense_category/expense_category_selectors.dart index eeb7ef25640..faf329de881 100644 --- a/lib/redux/expense_category/expense_category_selectors.dart +++ b/lib/redux/expense_category/expense_category_selectors.dart @@ -124,8 +124,3 @@ EntityStats expenseStatsForExpenseCategory( return EntityStats(countActive: countActive, countArchived: countArchived); } -bool hasExpenseCategoryChanges(ExpenseCategoryEntity expenseCategory, - BuiltMap expenseCategoryMap) => - expenseCategory.isNew - ? expenseCategory.isChanged - : expenseCategory != expenseCategoryMap[expenseCategory.id]; diff --git a/lib/redux/group/group_selectors.dart b/lib/redux/group/group_selectors.dart index 8cddf89cf0b..ddcbd6cda13 100644 --- a/lib/redux/group/group_selectors.dart +++ b/lib/redux/group/group_selectors.dart @@ -89,7 +89,3 @@ EntityStats clientStatsForGroup( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasGroupChanges( - GroupEntity group, BuiltMap groupMap) => - group.isNew ? group.isChanged : group != groupMap[group.id]; diff --git a/lib/redux/invoice/invoice_selectors.dart b/lib/redux/invoice/invoice_selectors.dart index 7632c3f79ef..eb88e233eb5 100644 --- a/lib/redux/invoice/invoice_selectors.dart +++ b/lib/redux/invoice/invoice_selectors.dart @@ -350,7 +350,3 @@ int precisionForInvoice(AppState state, InvoiceEntity invoice) { final currency = state.staticState.currencyMap[client.currencyId]; return currency?.precision ?? 2; } - -bool hasInvoiceChanges( - InvoiceEntity invoice, BuiltMap invoiceMap) => - invoice.isNew ? invoice.isChanged : invoice != invoiceMap[invoice.id]; diff --git a/lib/redux/payment/payment_selectors.dart b/lib/redux/payment/payment_selectors.dart index 97c8a55eadc..31238ef68b4 100644 --- a/lib/redux/payment/payment_selectors.dart +++ b/lib/redux/payment/payment_selectors.dart @@ -211,7 +211,3 @@ EntityStats paymentStatsForUser( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasPaymentChanges( - PaymentEntity payment, BuiltMap paymentMap) => - payment.isNew ? payment.isChanged : payment != paymentMap[payment.id]; diff --git a/lib/redux/payment_term/payment_term_selectors.dart b/lib/redux/payment_term/payment_term_selectors.dart index 8d2f7fd5d82..73550b28f9f 100644 --- a/lib/redux/payment_term/payment_term_selectors.dart +++ b/lib/redux/payment_term/payment_term_selectors.dart @@ -68,9 +68,3 @@ List filteredPaymentTermsSelector( return list; } - -bool hasPaymentTermChanges(PaymentTermEntity paymentTerm, - BuiltMap paymentTermMap) => - paymentTerm.isNew - ? paymentTerm.isChanged - : paymentTerm != paymentTermMap[paymentTerm.id]; diff --git a/lib/redux/product/product_selectors.dart b/lib/redux/product/product_selectors.dart index 23884e0acc4..81949dc4ef5 100644 --- a/lib/redux/product/product_selectors.dart +++ b/lib/redux/product/product_selectors.dart @@ -154,6 +154,3 @@ List filteredProductsSelector( return list; } -bool hasProductChanges( - ProductEntity product, BuiltMap productMap) => - product.isNew ? product.isChanged : product != productMap[product.id]; diff --git a/lib/redux/project/project_selectors.dart b/lib/redux/project/project_selectors.dart index 6790491acb9..fdad76f3d52 100644 --- a/lib/redux/project/project_selectors.dart +++ b/lib/redux/project/project_selectors.dart @@ -273,7 +273,3 @@ EntityStats projectStatsForUser( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasProjectChanges( - ProjectEntity project, BuiltMap projectMap) => - project.isNew ? project.isChanged : project != projectMap[project.id]; diff --git a/lib/redux/purchase_order/purchase_order_selectors.dart b/lib/redux/purchase_order/purchase_order_selectors.dart index 874186b5a3b..e4a6cb462f7 100644 --- a/lib/redux/purchase_order/purchase_order_selectors.dart +++ b/lib/redux/purchase_order/purchase_order_selectors.dart @@ -198,8 +198,3 @@ EntityStats purchaseOrderStatsForVendor( return EntityStats(countActive: countActive, countArchived: countArchived); } -bool hasPurchaseOrderChanges(InvoiceEntity purchaseOrder, - BuiltMap purchaseOrderMap) => - purchaseOrder.isNew - ? purchaseOrder.isChanged - : purchaseOrder != purchaseOrderMap[purchaseOrder.id]; diff --git a/lib/redux/quote/quote_selectors.dart b/lib/redux/quote/quote_selectors.dart index 91885f4b97d..98a32936e68 100644 --- a/lib/redux/quote/quote_selectors.dart +++ b/lib/redux/quote/quote_selectors.dart @@ -180,7 +180,3 @@ EntityStats quoteStatsForUser( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasQuoteChanges( - InvoiceEntity quote, BuiltMap quoteMap) => - quote.isNew ? quote.isChanged : quote != quoteMap[quote.id]; diff --git a/lib/redux/recurring_expense/recurring_expense_selectors.dart b/lib/redux/recurring_expense/recurring_expense_selectors.dart index b60d1191dbc..6ce77c318b0 100644 --- a/lib/redux/recurring_expense/recurring_expense_selectors.dart +++ b/lib/redux/recurring_expense/recurring_expense_selectors.dart @@ -271,9 +271,3 @@ EntityStats recurringExpenseStatsForExpense( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasRecurringExpenseChanges(ExpenseEntity recurringExpense, - BuiltMap recurringExpenseMap) => - recurringExpense.isNew - ? recurringExpense.isChanged - : recurringExpense != recurringExpenseMap[recurringExpense.id]; diff --git a/lib/redux/recurring_invoice/recurring_invoice_selectors.dart b/lib/redux/recurring_invoice/recurring_invoice_selectors.dart index f616f2a87aa..fe447424074 100644 --- a/lib/redux/recurring_invoice/recurring_invoice_selectors.dart +++ b/lib/redux/recurring_invoice/recurring_invoice_selectors.dart @@ -215,9 +215,3 @@ EntityStats recurringInvoiceStatsForSubscription( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasRecurringInvoiceChanges(InvoiceEntity recurringInvoice, - BuiltMap recurringInvoiceMap) => - recurringInvoice.isNew - ? recurringInvoice.isChanged - : recurringInvoice != recurringInvoiceMap[recurringInvoice.id]; diff --git a/lib/redux/subscription/subscription_selectors.dart b/lib/redux/subscription/subscription_selectors.dart index 48dff4335ed..7341be00c3e 100644 --- a/lib/redux/subscription/subscription_selectors.dart +++ b/lib/redux/subscription/subscription_selectors.dart @@ -87,9 +87,3 @@ List filteredSubscriptionsSelector( return list; } - -bool hasSubscriptionChanges(SubscriptionEntity subscription, - BuiltMap subscriptionMap) => - subscription.isNew - ? subscription.isChanged - : subscription != subscriptionMap[subscription.id]; diff --git a/lib/redux/task/task_selectors.dart b/lib/redux/task/task_selectors.dart index 4d50d66c444..64fe47a1b8e 100644 --- a/lib/redux/task/task_selectors.dart +++ b/lib/redux/task/task_selectors.dart @@ -468,6 +468,3 @@ EntityStats taskStatsForUser( return EntityStats(countActive: countActive, countArchived: countArchived); } - -bool hasTaskChanges(TaskEntity task, BuiltMap taskMap) => - task.isNew ? task.isChanged : task != taskMap[task.id]; diff --git a/lib/redux/task_status/task_status_selectors.dart b/lib/redux/task_status/task_status_selectors.dart index 615de725624..96135f482fc 100644 --- a/lib/redux/task_status/task_status_selectors.dart +++ b/lib/redux/task_status/task_status_selectors.dart @@ -153,12 +153,6 @@ EntityStats taskStatsForTaskStatus( return EntityStats(countActive: countActive, countArchived: countArchived); } -bool hasTaskStatusChanges(TaskStatusEntity taskStatus, - BuiltMap taskStatusMap) => - taskStatus.isNew - ? taskStatus.isChanged - : taskStatus != taskStatusMap[taskStatus.id]; - String defaultTaskStatusId(BuiltMap taskStatusMap) { final statusIds = taskStatusMap.keys.where((statusId) { final status = taskStatusMap[statusId]; diff --git a/lib/redux/tax_rate/tax_rate_selectors.dart b/lib/redux/tax_rate/tax_rate_selectors.dart index 84340a395b7..9c431b79c90 100644 --- a/lib/redux/tax_rate/tax_rate_selectors.dart +++ b/lib/redux/tax_rate/tax_rate_selectors.dart @@ -69,7 +69,3 @@ List filteredTaxRatesSelector( return list; } - -bool hasTaxRateChanges( - TaxRateEntity taxRate, BuiltMap taxRateMap) => - taxRate.isNew ? taxRate.isChanged : taxRate != taxRateMap[taxRate.id]; diff --git a/lib/redux/token/token_selectors.dart b/lib/redux/token/token_selectors.dart index b88c90821c0..76a7fb121c2 100644 --- a/lib/redux/token/token_selectors.dart +++ b/lib/redux/token/token_selectors.dart @@ -76,7 +76,3 @@ List filteredTokensSelector( return list; } - -bool hasTokenChanges( - TokenEntity token, BuiltMap tokenMap) => - token.isNew ? token.isChanged : token != tokenMap[token.id]; diff --git a/lib/redux/transaction/transaction_selectors.dart b/lib/redux/transaction/transaction_selectors.dart index e026d6648aa..55d4264bc5d 100644 --- a/lib/redux/transaction/transaction_selectors.dart +++ b/lib/redux/transaction/transaction_selectors.dart @@ -5,20 +5,33 @@ 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'; -var memoizedDropdownTransactionList = memo5( +var memoizedDropdownTransactionList = memo8( (BuiltMap transactionMap, BuiltList transactionList, StaticState staticState, BuiltMap userMap, + BuiltMap invoiceMap, + BuiltMap expenseMap, + BuiltMap bankAccountMap, String clientId) => dropdownTransactionsSelector( - transactionMap, transactionList, staticState, userMap, clientId)); + transactionMap, + transactionList, + staticState, + userMap, + invoiceMap, + expenseMap, + bankAccountMap, + clientId)); List dropdownTransactionsSelector( BuiltMap transactionMap, BuiltList transactionList, StaticState staticState, BuiltMap userMap, + BuiltMap invoiceMap, + BuiltMap expenseMap, + BuiltMap bankAccountMap, String clientId) { final list = transactionList.where((transactionId) { final transaction = transactionMap[transactionId]; @@ -33,23 +46,36 @@ List dropdownTransactionsSelector( list.sort((transactionAId, transactionBId) { final transactionA = transactionMap[transactionAId]; final transactionB = transactionMap[transactionBId]; - return transactionA.compareTo(transactionB, TransactionFields.date, true); + return transactionA.compareTo(transactionB, TransactionFields.date, true, + invoiceMap, expenseMap, bankAccountMap); }); return list; } -var memoizedFilteredTransactionList = memo4((SelectionState selectionState, +var memoizedFilteredTransactionList = memo7((SelectionState selectionState, BuiltMap transactionMap, BuiltList transactionList, + BuiltMap invoiceMap, + BuiltMap expenseMap, + BuiltMap bankAccountMap, ListUIState transactionListState) => filteredTransactionsSelector( - selectionState, transactionMap, transactionList, transactionListState)); + selectionState, + transactionMap, + transactionList, + invoiceMap, + expenseMap, + bankAccountMap, + transactionListState)); List filteredTransactionsSelector( SelectionState selectionState, BuiltMap transactionMap, BuiltList transactionList, + BuiltMap invoiceMap, + BuiltMap expenseMap, + BuiltMap bankAccountMap, ListUIState transactionListState) { final filterEntityId = selectionState.filterEntityId; //final filterEntityType = selectionState.filterEntityType; @@ -70,15 +96,14 @@ List filteredTransactionsSelector( list.sort((transactionAId, transactionBId) { final transactionA = transactionMap[transactionAId]; final transactionB = transactionMap[transactionBId]; - return transactionA.compareTo(transactionB, transactionListState.sortField, - transactionListState.sortAscending); + return transactionA.compareTo( + transactionB, + transactionListState.sortField, + transactionListState.sortAscending, + invoiceMap, + expenseMap, + bankAccountMap); }); return list; } - -bool hasTransactionChanges(TransactionEntity transaction, - BuiltMap transactionMap) => - transaction.isNew - ? transaction.isChanged - : transaction != transactionMap[transaction.id]; diff --git a/lib/redux/vendor/vendor_selectors.dart b/lib/redux/vendor/vendor_selectors.dart index 87388871391..2d7e2060b3b 100644 --- a/lib/redux/vendor/vendor_selectors.dart +++ b/lib/redux/vendor/vendor_selectors.dart @@ -129,7 +129,3 @@ double calculateVendorBalance(String vendorId, String currencyId, return total; } - -bool hasVendorChanges( - VendorEntity vendor, BuiltMap vendorMap) => - vendor.isNew ? vendor.isChanged : vendor != vendorMap[vendor.id]; diff --git a/lib/redux/webhook/webhook_selectors.dart b/lib/redux/webhook/webhook_selectors.dart index 2e39f1ee9f2..2311f520730 100644 --- a/lib/redux/webhook/webhook_selectors.dart +++ b/lib/redux/webhook/webhook_selectors.dart @@ -77,7 +77,3 @@ List filteredWebhooksSelector( return list; } - -bool hasWebhookChanges( - WebhookEntity webhook, BuiltMap webhookMap) => - webhook.isNew ? webhook.isChanged : webhook != webhookMap[webhook.id]; diff --git a/lib/ui/app/main_screen.dart b/lib/ui/app/main_screen.dart index 835010daf7f..fcbd3a17c2a 100644 --- a/lib/ui/app/main_screen.dart +++ b/lib/ui/app/main_screen.dart @@ -17,6 +17,7 @@ import 'package:invoiceninja_flutter/ui/settings/bank_accounts_vm.dart'; import 'package:invoiceninja_flutter/ui/settings/payment_settings_vm.dart'; import 'package:invoiceninja_flutter/ui/transaction/transaction_screen.dart'; import 'package:invoiceninja_flutter/ui/transaction/transaction_screen_vm.dart'; +import 'package:invoiceninja_flutter/ui/transaction/view/transaction_view_vm.dart'; import 'package:invoiceninja_flutter/utils/platforms.dart'; import 'package:redux/redux.dart'; @@ -571,6 +572,9 @@ class EntityScreens extends StatelessWidget { case EntityType.recurringExpense: child = RecurringExpenseViewScreen(); break; + case EntityType.transaction: + child = TransactionViewScreen(); + break; default: print('## View screen not defined for $previewEntityType'); } diff --git a/lib/ui/client/view/client_view_fullwidth.dart b/lib/ui/client/view/client_view_fullwidth.dart index d8521f381c1..9f05962928c 100644 --- a/lib/ui/client/view/client_view_fullwidth.dart +++ b/lib/ui/client/view/client_view_fullwidth.dart @@ -135,11 +135,12 @@ class _ClientViewFullwidthState extends State if (client.website.isNotEmpty) Padding( padding: const EdgeInsets.symmetric(vertical: 4), - child: CopyToClipboard( - value: client.website, + child: InkWell( + onTap: () => launchUrl(Uri.parse(client.website)), child: IconText( - icon: MdiIcons.earth, - text: trimUrl(client.website)), + icon: MdiIcons.earth, + text: trimUrl(client.website), + ), ), ), SizedBox(height: 4), @@ -192,6 +193,10 @@ class _ClientViewFullwidthState extends State ), SizedBox(height: 8), if (billingAddress.isNotEmpty) ...[ + Text( + localization.billingAddress, + style: TextStyle(color: Colors.grey), + ), Row( children: [ Expanded( @@ -224,6 +229,10 @@ class _ClientViewFullwidthState extends State SizedBox(height: 8), ], if (shippingAddress.isNotEmpty) ...[ + Text( + localization.shippingAddress, + style: TextStyle(color: Colors.grey), + ), Row( children: [ Expanded( @@ -270,7 +279,10 @@ class _ClientViewFullwidthState extends State controller: _scrollController3, children: [ Text( - localization.contacts, + localization.contacts + + (client.contacts.length > 1 + ? ' (${client.contacts.length})' + : ''), style: Theme.of(context).textTheme.headline6, ), SizedBox(height: 8), diff --git a/lib/ui/credit/edit/credit_edit_vm.dart b/lib/ui/credit/edit/credit_edit_vm.dart index 76976c1eaa7..65d17e75923 100644 --- a/lib/ui/credit/edit/credit_edit_vm.dart +++ b/lib/ui/credit/edit/credit_edit_vm.dart @@ -18,7 +18,6 @@ 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/credit/credit_actions.dart'; -import 'package:invoiceninja_flutter/redux/credit/credit_selectors.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/credit/edit/credit_edit.dart'; @@ -100,7 +99,7 @@ class CreditEditVM extends AbstractInvoiceEditVM { } if (credit.isOld && - !hasCreditChanges(credit, state.creditState.map) && + credit.isChanged == false && action != null && action.isClientSide) { handleEntityAction(credit, action); diff --git a/lib/ui/expense/edit/expense_edit_vm.dart b/lib/ui/expense/edit/expense_edit_vm.dart index ec2911a2480..67d7e48e2af 100644 --- a/lib/ui/expense/edit/expense_edit_vm.dart +++ b/lib/ui/expense/edit/expense_edit_vm.dart @@ -9,7 +9,6 @@ import 'package:flutter_redux/flutter_redux.dart'; import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:http/http.dart'; import 'package:invoiceninja_flutter/redux/document/document_actions.dart'; -import 'package:invoiceninja_flutter/redux/expense/expense_selectors.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; import 'package:redux/redux.dart'; @@ -154,7 +153,7 @@ class ExpenseEditVM extends AbstractExpenseEditVM { final navigator = navigatorKey.currentState; if (expense.isOld && - !hasExpenseChanges(expense, state.expenseState.map) && + expense.isChanged == false && action != null && action.isClientSide) { handleEntityAction(expense, action); diff --git a/lib/ui/invoice/edit/invoice_edit_vm.dart b/lib/ui/invoice/edit/invoice_edit_vm.dart index a3a95244919..6f1f1eaf639 100644 --- a/lib/ui/invoice/edit/invoice_edit_vm.dart +++ b/lib/ui/invoice/edit/invoice_edit_vm.dart @@ -19,7 +19,6 @@ 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/invoice/invoice_actions.dart'; -import 'package:invoiceninja_flutter/redux/invoice/invoice_selectors.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/invoice/edit/invoice_edit.dart'; @@ -153,7 +152,7 @@ class InvoiceEditVM extends AbstractInvoiceEditVM { } if (invoice.isOld && - !hasInvoiceChanges(invoice, state.invoiceState.map) && + invoice.isChanged == false && action != null && action.isClientSide) { handleEntityAction(invoice, action); diff --git a/lib/ui/purchase_order/edit/purchase_order_edit_vm.dart b/lib/ui/purchase_order/edit/purchase_order_edit_vm.dart index 07210f8c3fd..dc70e971a73 100644 --- a/lib/ui/purchase_order/edit/purchase_order_edit_vm.dart +++ b/lib/ui/purchase_order/edit/purchase_order_edit_vm.dart @@ -10,7 +10,6 @@ import 'package:flutter_styled_toast/flutter_styled_toast.dart'; import 'package:http/http.dart'; import 'package:invoiceninja_flutter/redux/document/document_actions.dart'; import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_actions.dart'; -import 'package:invoiceninja_flutter/redux/purchase_order/purchase_order_selectors.dart'; import 'package:invoiceninja_flutter/ui/purchase_order/edit/purchase_order_edit.dart'; import 'package:invoiceninja_flutter/ui/purchase_order/view/purchase_order_view_vm.dart'; import 'package:invoiceninja_flutter/utils/localization.dart'; @@ -99,8 +98,7 @@ class PurchaseOrderEditVM extends AbstractInvoiceEditVM { return null; } if (purchaseOrder.isOld && - !hasPurchaseOrderChanges( - purchaseOrder, state.purchaseOrderState.map) && + purchaseOrder.isChanged == false && action != null && action.isClientSide) { handleEntityAction(purchaseOrder, action); diff --git a/lib/ui/quote/edit/quote_edit_vm.dart b/lib/ui/quote/edit/quote_edit_vm.dart index 85eb1eca525..0eafdb32d09 100644 --- a/lib/ui/quote/edit/quote_edit_vm.dart +++ b/lib/ui/quote/edit/quote_edit_vm.dart @@ -18,7 +18,6 @@ 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/quote/quote_actions.dart'; -import 'package:invoiceninja_flutter/redux/quote/quote_selectors.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/invoice/edit/invoice_edit_vm.dart'; @@ -99,7 +98,7 @@ class QuoteEditVM extends AbstractInvoiceEditVM { return null; } if (quote.isOld && - !hasQuoteChanges(quote, state.quoteState.map) && + quote.isChanged == false && action != null && action.isClientSide) { handleEntityAction(quote, action); diff --git a/lib/ui/recurring_expense/edit/recurring_expense_edit_vm.dart b/lib/ui/recurring_expense/edit/recurring_expense_edit_vm.dart index f1b92582e72..3e9bc71b0d8 100644 --- a/lib/ui/recurring_expense/edit/recurring_expense_edit_vm.dart +++ b/lib/ui/recurring_expense/edit/recurring_expense_edit_vm.dart @@ -16,7 +16,6 @@ import 'package:invoiceninja_flutter/data/models/models.dart'; import 'package:invoiceninja_flutter/redux/app/app_actions.dart'; import 'package:invoiceninja_flutter/redux/app/app_state.dart'; import 'package:invoiceninja_flutter/redux/recurring_expense/recurring_expense_actions.dart'; -import 'package:invoiceninja_flutter/redux/recurring_expense/recurring_expense_selectors.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/expense/edit/expense_edit.dart'; @@ -133,8 +132,7 @@ class RecurringExpenseEditVM extends AbstractExpenseEditVM { final recurringExpense = store.state.recurringExpenseUIState.editing; final localization = AppLocalization.of(context); if (recurringExpense.isOld && - !hasRecurringExpenseChanges( - recurringExpense, state.recurringExpenseState.map) && + recurringExpense.isChanged == false && action != null && action.isClientSide) { handleEntityAction(recurringExpense, action); diff --git a/lib/ui/recurring_invoice/edit/recurring_invoice_edit_vm.dart b/lib/ui/recurring_invoice/edit/recurring_invoice_edit_vm.dart index 0e611b23ca9..6af67e96d96 100644 --- a/lib/ui/recurring_invoice/edit/recurring_invoice_edit_vm.dart +++ b/lib/ui/recurring_invoice/edit/recurring_invoice_edit_vm.dart @@ -18,7 +18,6 @@ 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/recurring_invoice/recurring_invoice_actions.dart'; -import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_selectors.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/invoice/edit/invoice_edit_vm.dart'; @@ -100,8 +99,7 @@ class RecurringInvoiceEditVM extends AbstractInvoiceEditVM { return null; } if (recurringInvoice.isOld && - !hasRecurringInvoiceChanges( - recurringInvoice, state.recurringInvoiceState.map) && + recurringInvoice.isChanged == false && action != null && action.isClientSide) { handleEntityAction(recurringInvoice, action); diff --git a/lib/ui/task/edit/task_edit_vm.dart b/lib/ui/task/edit/task_edit_vm.dart index 45e212f35e2..68aef45fd20 100644 --- a/lib/ui/task/edit/task_edit_vm.dart +++ b/lib/ui/task/edit/task_edit_vm.dart @@ -7,7 +7,6 @@ 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/task/task_selectors.dart'; import 'package:redux/redux.dart'; // Project imports: @@ -98,7 +97,7 @@ class TaskEditVM { } if (task.isOld && - !hasTaskChanges(task, state.taskState.map) && + task.isChanged == false && action != null && action.isClientSide) { handleEntityAction(task, action); diff --git a/lib/ui/transaction/transaction_list_vm.dart b/lib/ui/transaction/transaction_list_vm.dart index b881d75ff27..056b18b2cef 100644 --- a/lib/ui/transaction/transaction_list_vm.dart +++ b/lib/ui/transaction/transaction_list_vm.dart @@ -90,6 +90,9 @@ class TransactionListVM { state.getUISelection(EntityType.transaction), state.transactionState.map, state.transactionState.list, + state.invoiceState.map, + state.expenseState.map, + state.bankAccountState.map, state.transactionListState), transactionMap: state.transactionState.map, isLoading: state.isLoading, diff --git a/lib/ui/transaction/transaction_screen_vm.dart b/lib/ui/transaction/transaction_screen_vm.dart index 53d2ca76f7e..8473403fb64 100644 --- a/lib/ui/transaction/transaction_screen_vm.dart +++ b/lib/ui/transaction/transaction_screen_vm.dart @@ -50,6 +50,9 @@ class TransactionScreenVM { state.getUISelection(EntityType.transaction), state.transactionState.map, state.transactionState.list, + state.invoiceState.map, + state.expenseState.map, + state.bankAccountState.map, state.transactionListState, ), userCompany: state.userCompany, diff --git a/lib/ui/vendor/view/vendor_view_fullwidth.dart b/lib/ui/vendor/view/vendor_view_fullwidth.dart index b89c316a2af..615938ca227 100644 --- a/lib/ui/vendor/view/vendor_view_fullwidth.dart +++ b/lib/ui/vendor/view/vendor_view_fullwidth.dart @@ -112,10 +112,12 @@ class _VendorViewFullwidthState extends State if (vendor.website.isNotEmpty) Padding( padding: const EdgeInsets.symmetric(vertical: 4), - child: CopyToClipboard( - value: vendor.website, + child: InkWell( + onTap: () => launchUrl(Uri.parse(vendor.website)), child: IconText( - icon: MdiIcons.earth, text: trimUrl(vendor.website)), + icon: MdiIcons.earth, + text: trimUrl(vendor.website), + ), ), ), SizedBox(height: 4), @@ -151,6 +153,10 @@ class _VendorViewFullwidthState extends State ), SizedBox(height: 8), if (billingAddress.isNotEmpty) ...[ + Text( + localization.billingAddress, + style: TextStyle(color: Colors.grey), + ), Row( children: [ Expanded( @@ -201,7 +207,10 @@ class _VendorViewFullwidthState extends State controller: _scrollController3, children: [ Text( - localization.contacts, + localization.contacts + + (vendor.contacts.length > 1 + ? ' (${vendor.contacts.length})' + : ''), style: Theme.of(context).textTheme.headline6, ), SizedBox(height: 8), diff --git a/starter.sh b/starter.sh index 2cefc926a92..8623ebf7720 100644 --- a/starter.sh +++ b/starter.sh @@ -187,7 +187,7 @@ else # Link in new module echo "app_state: import" comment="STARTER: import - do not remove comment" - code="import 'package:${package}\/redux\/${module_snake}\/${module_snake}_state.dart';import 'package:${package}\/ui\/${module_snake}\/edit\/${module_snake}_edit_vm.dart';import 'package:${package}\/redux\/${module_snake}\/${module_snake}_selectors.dart';${lineBreak}" + code="import 'package:${package}\/redux\/${module_snake}\/${module_snake}_state.dart';import 'package:${package}\/ui\/${module_snake}\/edit\/${module_snake}_edit_vm.dart';${lineBreak}" sed -i -e "s/$comment/$code${lineBreak}$comment/g" ./lib/redux/app/app_state.dart echo "app_state: list" @@ -214,7 +214,7 @@ else echo "app_state: has changes" comment="STARTER: has changes - do not remove comment" - code="case ${Module}EditScreen.route: return has${Module}Changes(${module_camel}UIState.editing, ${module_camel}State.map);${lineBreak}" + code="case ${Module}EditScreen.route: return has${module_camel}UIState.editing.isChanged == true;${lineBreak}" sed -i -e "s/$comment/$comment${lineBreak}$code/g" ./lib/redux/app/app_state.dart for (( idx=${#fieldsArray[@]}-1 ; idx>=0 ; idx-- )) ; do diff --git a/stubs/redux/stub/stub_selectors b/stubs/redux/stub/stub_selectors index 97181bf4d40..e33e818f4f1 100644 --- a/stubs/redux/stub/stub_selectors +++ b/stubs/redux/stub/stub_selectors @@ -90,7 +90,3 @@ List filteredStubsSelector( return list; } - -bool hasStubChanges( - StubEntity stub, BuiltMap stubMap) => - stub.isNew ? stub.isChanged : stub != stubMap[stub.id];