Recurring invoices

This commit is contained in:
Hillel Coren 2020-09-14 09:24:41 +03:00
parent 8ddc039897
commit 27f142e75d
4 changed files with 161 additions and 57 deletions

View File

@ -146,6 +146,14 @@ List<String> filteredInvoicesSelector(
!invoiceListState.custom2Filters.contains(invoice.customValue2)) {
return false;
}
if (invoiceListState.custom3Filters.isNotEmpty &&
!invoiceListState.custom3Filters.contains(invoice.customValue3)) {
return false;
}
if (invoiceListState.custom4Filters.isNotEmpty &&
!invoiceListState.custom4Filters.contains(invoice.customValue4)) {
return false;
}
return true;
}).toList();

View File

@ -32,31 +32,55 @@ List<String> filteredRecurringInvoicesSelector(
BuiltMap<String, InvoiceEntity> recurringInvoiceMap,
BuiltMap<String, ClientEntity> clientMap,
BuiltList<String> recurringInvoiceList,
ListUIState recurringInvoiceListState,
ListUIState invoiceListState,
StaticState staticState,
BuiltMap<String, UserEntity> userMap,
) {
final list = recurringInvoiceList.where((recurringInvoiceId) {
final recurringInvoice = recurringInvoiceMap[recurringInvoiceId];
if (filterEntityId != null && recurringInvoice.id != filterEntityId) {
return false;
} else {}
final invoice = recurringInvoiceMap[recurringInvoiceId];
final client =
clientMap[invoice.clientId] ?? ClientEntity(id: invoice.clientId);
if (!recurringInvoice
.matchesStates(recurringInvoiceListState.stateFilters)) {
if (!client.isActive &&
!client.matchesEntityFilter(filterEntityType, filterEntityId)) {
return false;
}
if (recurringInvoiceListState.custom1Filters.isNotEmpty &&
!recurringInvoiceListState.custom1Filters
.contains(recurringInvoice.customValue1)) {
if (filterEntityType == EntityType.client && client.id != filterEntityId) {
return false;
} else if (filterEntityType == EntityType.user &&
invoice.assignedUserId != filterEntityId) {
return false;
}
if (recurringInvoiceListState.custom2Filters.isNotEmpty &&
!recurringInvoiceListState.custom2Filters
.contains(recurringInvoice.customValue2)) {
if (!invoice.matchesStates(invoiceListState.stateFilters)) {
return false;
}
return recurringInvoice.matchesFilter(recurringInvoiceListState.filter);
if (!invoice.matchesStatuses(invoiceListState.statusFilters)) {
return false;
}
if (!invoice.matchesFilter(invoiceListState.filter) &&
!client.matchesFilter(invoiceListState.filter)) {
return false;
}
if (invoiceListState.custom1Filters.isNotEmpty &&
!invoiceListState.custom1Filters.contains(invoice.customValue1)) {
return false;
}
if (invoiceListState.custom2Filters.isNotEmpty &&
!invoiceListState.custom2Filters.contains(invoice.customValue2)) {
return false;
}
if (invoiceListState.custom3Filters.isNotEmpty &&
!invoiceListState.custom3Filters.contains(invoice.customValue3)) {
return false;
}
if (invoiceListState.custom4Filters.isNotEmpty &&
!invoiceListState.custom4Filters.contains(invoice.customValue4)) {
return false;
}
return true;
}).toList();
list.sort((recurringInvoiceAId, recurringInvoiceBId) {
@ -65,8 +89,8 @@ List<String> filteredRecurringInvoicesSelector(
return recurringInvoiceA.compareTo(
invoice: recurringInvoiceB,
sortField: recurringInvoiceListState.sortField,
sortAscending: recurringInvoiceListState.sortAscending,
sortField: invoiceListState.sortField,
sortAscending: invoiceListState.sortAscending,
clientMap: clientMap,
staticState: staticState,
userMap: userMap,
@ -76,8 +100,52 @@ List<String> filteredRecurringInvoicesSelector(
return list;
}
var memoizedRecurringInvoiceStatsForClient = memo2(
(String clientId, BuiltMap<String, InvoiceEntity> invoiceMap) =>
recurringInvoiceStatsForClient(clientId, invoiceMap));
EntityStats recurringInvoiceStatsForClient(
String clientId, BuiltMap<String, InvoiceEntity> invoiceMap) {
int countActive = 0;
int countArchived = 0;
invoiceMap.forEach((invoiceId, invoice) {
if (invoice.clientId == clientId) {
if (invoice.isActive) {
countActive++;
} else if (invoice.isArchived) {
countArchived++;
}
}
});
return EntityStats(countActive: countActive, countArchived: countArchived);
}
var memoizedRecurringInvoiceStatsForUser = memo2(
(String userId, BuiltMap<String, InvoiceEntity> invoiceMap) =>
recurringInvoiceStatsForUser(userId, invoiceMap));
EntityStats recurringInvoiceStatsForUser(
String userId, BuiltMap<String, InvoiceEntity> invoiceMap) {
int countActive = 0;
int countArchived = 0;
invoiceMap.forEach((invoiceId, invoice) {
if (invoice.assignedUserId == userId) {
if (invoice.isActive) {
countActive++;
} else if (invoice.isDeleted) {
countArchived++;
}
}
});
return EntityStats(countActive: countActive, countArchived: countArchived);
}
bool hasRecurringInvoiceChanges(InvoiceEntity recurringInvoice,
BuiltMap<String, InvoiceEntity> recurringInvoiceMap) =>
recurringInvoice.isNew
? recurringInvoice.isChanged
: recurringInvoice != recurringInvoiceMap[recurringInvoice.id];

View File

@ -10,6 +10,7 @@ import 'package:invoiceninja_flutter/redux/invoice/invoice_selectors.dart';
import 'package:invoiceninja_flutter/redux/payment/payment_selectors.dart';
import 'package:invoiceninja_flutter/redux/project/project_selectors.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_selectors.dart';
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_selectors.dart';
import 'package:invoiceninja_flutter/redux/task/task_selectors.dart';
import 'package:invoiceninja_flutter/ui/app/FieldGrid.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_list_tile.dart';
@ -176,6 +177,19 @@ class ClientOverview extends StatelessWidget {
memoizedInvoiceStatsForClient(client.id, state.invoiceState.map)
.present(localization.active, localization.archived),
),
if (company.isModuleEnabled(EntityType.recurringInvoice))
EntitiesListTile(
isFilter: isFilter,
entityType: EntityType.recurringInvoice,
title: localization.recurringInvoices,
onTap: () =>
viewModel.onEntityPressed(context, EntityType.recurringInvoice),
onLongPress: () => viewModel.onEntityPressed(
context, EntityType.recurringInvoice, true),
subtitle: memoizedRecurringInvoiceStatsForClient(
client.id, state.recurringInvoiceState.map)
.present(localization.active, localization.archived),
),
if (company.isModuleEnabled(EntityType.payment))
EntitiesListTile(
isFilter: isFilter,

View File

@ -4,6 +4,7 @@ import 'package:invoiceninja_flutter/redux/app/app_state.dart';
import 'package:invoiceninja_flutter/redux/credit/credit_selectors.dart';
import 'package:invoiceninja_flutter/redux/invoice/invoice_selectors.dart';
import 'package:invoiceninja_flutter/redux/quote/quote_selectors.dart';
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_selectors.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/lists/list_divider.dart';
@ -44,47 +45,60 @@ class UserView extends StatelessWidget {
secondValue: user.phone,
),
ListDivider(),
EntitiesListTile(
isFilter: isFilter,
title: localization.invoices,
entityType: EntityType.invoice,
onTap: () => viewModel.onEntityPressed(context, EntityType.invoice),
onLongPress: () =>
viewModel.onEntityPressed(context, EntityType.invoice, true),
subtitle:
memoizedInvoiceStatsForUser(user.id, state.invoiceState.map)
.present(localization.active, localization.archived),
),
userCompany.canViewOrCreate(EntityType.quote)
? EntitiesListTile(
isFilter: isFilter,
entityType: EntityType.quote,
title: localization.quotes,
onTap: () =>
viewModel.onEntityPressed(context, EntityType.quote),
onLongPress: () => viewModel.onEntityPressed(
context, EntityType.quote, true),
subtitle: memoizedQuoteStatsForUser(
user.id,
state.quoteState.map,
).present(localization.active, localization.archived),
)
: Container(),
userCompany.canViewOrCreate(EntityType.credit)
? EntitiesListTile(
isFilter: isFilter,
entityType: EntityType.credit,
title: localization.credits,
onTap: () =>
viewModel.onEntityPressed(context, EntityType.credit),
onLongPress: () => viewModel.onEntityPressed(
context, EntityType.credit, true),
subtitle: memoizedCreditStatsForUser(
user.id,
state.creditState.map,
).present(localization.active, localization.archived),
)
: Container(),
if (userCompany.canViewOrCreate(EntityType.invoice))
EntitiesListTile(
isFilter: isFilter,
title: localization.invoices,
entityType: EntityType.invoice,
onTap: () =>
viewModel.onEntityPressed(context, EntityType.invoice),
onLongPress: () =>
viewModel.onEntityPressed(context, EntityType.invoice, true),
subtitle:
memoizedInvoiceStatsForUser(user.id, state.invoiceState.map)
.present(localization.active, localization.archived),
),
if (userCompany.canViewOrCreate(EntityType.recurringInvoice))
EntitiesListTile(
isFilter: isFilter,
title: localization.recurringInvoices,
entityType: EntityType.recurringInvoice,
onTap: () => viewModel.onEntityPressed(
context, EntityType.recurringInvoice),
onLongPress: () => viewModel.onEntityPressed(
context, EntityType.recurringInvoice, true),
subtitle: memoizedRecurringInvoiceStatsForUser(
user.id, state.recurringInvoiceState.map)
.present(localization.active, localization.archived),
),
if (userCompany.canViewOrCreate(EntityType.quote))
EntitiesListTile(
isFilter: isFilter,
entityType: EntityType.quote,
title: localization.quotes,
onTap: () => viewModel.onEntityPressed(context, EntityType.quote),
onLongPress: () =>
viewModel.onEntityPressed(context, EntityType.quote, true),
subtitle: memoizedQuoteStatsForUser(
user.id,
state.quoteState.map,
).present(localization.active, localization.archived),
),
if (userCompany.canViewOrCreate(EntityType.credit))
EntitiesListTile(
isFilter: isFilter,
entityType: EntityType.credit,
title: localization.credits,
onTap: () =>
viewModel.onEntityPressed(context, EntityType.credit),
onLongPress: () =>
viewModel.onEntityPressed(context, EntityType.credit, true),
subtitle: memoizedCreditStatsForUser(
user.id,
state.creditState.map,
).present(localization.active, localization.archived),
),
/*
userCompany.canViewOrCreate(EntityType.project)
? EntityListTile(