Recurring invoices

This commit is contained in:
Hillel Coren 2020-09-13 10:28:29 +03:00
parent c80f30e84c
commit bb0b1f9792
2 changed files with 196 additions and 53 deletions

View File

@ -38,15 +38,25 @@ class EditRecurringInvoice extends AbstractNavigatorAction
@required NavigatorState navigator, @required NavigatorState navigator,
this.completer, this.completer,
this.cancelCompleter, this.cancelCompleter,
this.itemIndex,
this.force = false}) this.force = false})
: super(navigator: navigator); : super(navigator: navigator);
final InvoiceEntity recurringInvoice; final InvoiceEntity recurringInvoice;
final int itemIndex;
final Completer completer; final Completer completer;
final Completer cancelCompleter; final Completer cancelCompleter;
final bool force; final bool force;
} }
class ShowEmailRecurringInvoice {
ShowEmailRecurringInvoice({this.invoice, this.context, this.completer});
final InvoiceEntity invoice;
final BuildContext context;
final Completer completer;
}
class EditRecurringInvoiceItem implements PersistUI { class EditRecurringInvoiceItem implements PersistUI {
EditRecurringInvoiceItem([this.itemIndex]); EditRecurringInvoiceItem([this.itemIndex]);
@ -65,7 +75,6 @@ class UpdateRecurringInvoiceClient implements PersistUI {
final ClientEntity client; final ClientEntity client;
} }
class LoadRecurringInvoice { class LoadRecurringInvoice {
LoadRecurringInvoice({this.completer, this.recurringInvoiceId}); LoadRecurringInvoice({this.completer, this.recurringInvoiceId});
@ -134,6 +143,20 @@ class LoadRecurringInvoicesSuccess implements StopLoading {
} }
} }
class AddRecurringInvoiceContact implements PersistUI {
AddRecurringInvoiceContact({this.contact, this.invitation});
final ContactEntity contact;
final InvitationEntity invitation;
}
class RemoveRecurringInvoiceContact implements PersistUI {
RemoveRecurringInvoiceContact({this.invitation});
final InvitationEntity invitation;
}
class SaveRecurringInvoiceRequest implements StartSaving { class SaveRecurringInvoiceRequest implements StartSaving {
SaveRecurringInvoiceRequest({this.completer, this.recurringInvoice}); SaveRecurringInvoiceRequest({this.completer, this.recurringInvoice});
@ -266,6 +289,12 @@ class FilterRecurringInvoicesByState implements PersistUI {
final EntityState state; final EntityState state;
} }
class FilterRecurringInvoiceDropdown {
FilterRecurringInvoiceDropdown(this.filter);
final String filter;
}
class FilterRecurringInvoicesByCustom1 implements PersistUI { class FilterRecurringInvoicesByCustom1 implements PersistUI {
FilterRecurringInvoicesByCustom1(this.value); FilterRecurringInvoicesByCustom1(this.value);

View File

@ -9,20 +9,38 @@ import 'package:invoiceninja_flutter/redux/ui/list_ui_state.dart';
import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_state.dart'; import 'package:invoiceninja_flutter/redux/recurring_invoice/recurring_invoice_state.dart';
import 'package:invoiceninja_flutter/data/models/entities.dart'; import 'package:invoiceninja_flutter/data/models/entities.dart';
EntityUIState recurringInvoiceUIReducer( EntityUIState recurringInvoiceUIReducer(RecurringInvoiceUIState state, dynamic action) {
RecurringInvoiceUIState state, dynamic action) {
return state.rebuild((b) => b return state.rebuild((b) => b
..listUIState ..listUIState.replace(recurringInvoiceListReducer(state.listUIState, action))
.replace(recurringInvoiceListReducer(state.listUIState, action))
..editing.replace(editingReducer(state.editing, action)) ..editing.replace(editingReducer(state.editing, action))
..editingItemIndex = editingItemIndexReducer(state.editingItemIndex, action)
..selectedId = selectedIdReducer(state.selectedId, action)); ..selectedId = selectedIdReducer(state.selectedId, action));
} }
final editingItemIndexReducer = combineReducers<int>([
TypedReducer<int, EditRecurringInvoice>(
(index, action) => action.itemIndex),
TypedReducer<int, EditRecurringInvoiceItem>(
(index, action) => action.itemIndex),
]);
Reducer<String> dropdownFilterReducer = combineReducers([
TypedReducer<String, FilterRecurringInvoiceDropdown>(
filterRecurringInvoiceDropdownReducer),
]);
String filterRecurringInvoiceDropdownReducer(
String dropdownFilter, FilterRecurringInvoiceDropdown action) {
return action.filter;
}
Reducer<String> selectedIdReducer = combineReducers([ Reducer<String> selectedIdReducer = combineReducers([
TypedReducer<String, ViewRecurringInvoice>( TypedReducer<String, ViewRecurringInvoice>(
(String selectedId, dynamic action) => action.recurringInvoiceId), (selectedId, action) => action.recurringInvoiceId),
TypedReducer<String, AddRecurringInvoiceSuccess>( TypedReducer<String, AddRecurringInvoiceSuccess>(
(String selectedId, dynamic action) => action.recurringInvoice.id), (selectedId, action) => action.recurringInvoice.id),
TypedReducer<String, ShowEmailRecurringInvoice>(
(selectedId, action) => action.invoice.id),
TypedReducer<String, SelectCompany>( TypedReducer<String, SelectCompany>(
(selectedId, action) => action.clearSelection ? '' : selectedId), (selectedId, action) => action.clearSelection ? '' : selectedId),
TypedReducer<String, DeleteRecurringInvoicesSuccess>( TypedReducer<String, DeleteRecurringInvoicesSuccess>(
@ -30,17 +48,40 @@ Reducer<String> selectedIdReducer = combineReducers([
TypedReducer<String, ArchiveRecurringInvoicesSuccess>( TypedReducer<String, ArchiveRecurringInvoicesSuccess>(
(selectedId, action) => ''), (selectedId, action) => ''),
TypedReducer<String, ClearEntityFilter>((selectedId, action) => ''), TypedReducer<String, ClearEntityFilter>((selectedId, action) => ''),
TypedReducer<String, FilterByEntity>((selectedId, action) => TypedReducer<String, ClearEntitySelection>((selectedId, action) =>
action.clearSelection action.entityType == EntityType.recurringInvoice ? '' : selectedId),
? '' TypedReducer<String, FilterByEntity>((selectedId, action) => action
: action.entityType == EntityType.recurringInvoice .clearSelection
? action.entityId ? ''
: selectedId), : action.entityType == EntityType.recurringInvoice ? action.entityId : selectedId),
]); ]);
final editingReducer = combineReducers<InvoiceEntity>([ final editingReducer = combineReducers<InvoiceEntity>([
TypedReducer<InvoiceEntity, SaveRecurringInvoiceSuccess>(_updateEditing), TypedReducer<InvoiceEntity, SaveRecurringInvoiceSuccess>(_updateEditing),
TypedReducer<InvoiceEntity, AddRecurringInvoiceSuccess>(_updateEditing), TypedReducer<InvoiceEntity, AddRecurringInvoiceSuccess>(_updateEditing),
TypedReducer<InvoiceEntity, EditRecurringInvoice>(_updateEditing),
TypedReducer<InvoiceEntity, UpdateRecurringInvoice>((recurringInvoice, action) {
return action.recurringInvoice.rebuild((b) => b..isChanged = true);
}),
TypedReducer<InvoiceEntity, AddRecurringInvoiceItem>((recurringInvoice, action) {
return recurringInvoice.rebuild((b) => b..isChanged = true);
}),
TypedReducer<InvoiceEntity, DeleteRecurringInvoiceItem>((recurringInvoice, action) {
return recurringInvoice.rebuild((b) => b..isChanged = true);
}),
TypedReducer<InvoiceEntity, UpdateRecurringInvoiceItem>((recurringInvoice, action) {
return recurringInvoice.rebuild((b) => b..isChanged = true);
}),
TypedReducer<InvoiceEntity, UpdateRecurringInvoiceClient>((recurringInvoice, action) {
final client = action.client;
return recurringInvoice.rebuild((b) => b
..isChanged = true
..clientId = client?.id ?? ''
..invitations.replace((client?.contacts ?? <ContactEntity>[])
.where((contact) => contact.sendEmail)
.map((contact) => InvitationEntity(contactId: contact.id))
.toList()));
}),
TypedReducer<InvoiceEntity, RestoreRecurringInvoicesSuccess>( TypedReducer<InvoiceEntity, RestoreRecurringInvoicesSuccess>(
(recurringInvoices, action) { (recurringInvoices, action) {
return action.recurringInvoices[0]; return action.recurringInvoices[0];
@ -53,12 +94,23 @@ final editingReducer = combineReducers<InvoiceEntity>([
(recurringInvoices, action) { (recurringInvoices, action) {
return action.recurringInvoices[0]; return action.recurringInvoices[0];
}), }),
TypedReducer<InvoiceEntity, EditRecurringInvoice>(_updateEditing), TypedReducer<InvoiceEntity, AddRecurringInvoiceItem>(
TypedReducer<InvoiceEntity, UpdateRecurringInvoice>( _addRecurringInvoiceItem),
(recurringInvoice, action) { TypedReducer<InvoiceEntity, AddRecurringInvoiceItems>(
return action.recurringInvoice.rebuild((b) => b..isChanged = true); _addRecurringInvoiceItems),
}), TypedReducer<InvoiceEntity, DeleteRecurringInvoiceItem>(
_removeRecurringInvoiceItem),
TypedReducer<InvoiceEntity, UpdateRecurringInvoiceItem>(
_updateRecurringInvoiceItem),
TypedReducer<InvoiceEntity, DiscardChanges>(_clearEditing), TypedReducer<InvoiceEntity, DiscardChanges>(_clearEditing),
TypedReducer<InvoiceEntity, AddRecurringInvoiceContact>((recurringInvoice, action) {
return recurringInvoice.rebuild((b) => b
..invitations.add(
action.invitation ?? InvitationEntity(contactId: action.contact.id)));
}),
TypedReducer<InvoiceEntity, RemoveRecurringInvoiceContact>((recurringInvoice, action) {
return recurringInvoice.rebuild((b) => b..invitations.remove(action.invitation));
}),
]); ]);
InvoiceEntity _clearEditing(InvoiceEntity recurringInvoice, dynamic action) { InvoiceEntity _clearEditing(InvoiceEntity recurringInvoice, dynamic action) {
@ -69,15 +121,55 @@ InvoiceEntity _updateEditing(InvoiceEntity recurringInvoice, dynamic action) {
return action.recurringInvoice; return action.recurringInvoice;
} }
InvoiceEntity _addRecurringInvoiceItem(
InvoiceEntity recurringInvoice, AddRecurringInvoiceItem action) {
final item = action.recurringInvoiceItem ?? RecurringInvoiceItemEntity();
return recurringInvoice.rebuild((b) => b
..hasTasks = b.hasTasks || item.isTask
..hasExpenses = b.hasExpenses || item.isExpense
..lineItems.add(item));
}
InvoiceEntity _addRecurringInvoiceItems(
InvoiceEntity recurringInvoice, AddRecurringInvoiceItems action) {
return recurringInvoice.rebuild((b) => b
..hasTasks = action.lineItems.where((item) => item.isTask).isNotEmpty
..hasExpenses = action.lineItems.where((item) => item.isExpense).isNotEmpty
..lineItems.addAll(action.lineItems));
}
InvoiceEntity _removeRecurringInvoiceItem(
InvoiceEntity recurringInvoice, DeleteRecurringInvoiceItem action) {
if (recurringInvoice.lineItems.length <= action.index) {
return recurringInvoice;
}
return recurringInvoice.rebuild((b) => b..lineItems.removeAt(action.index));
}
InvoiceEntity _updateRecurringInvoiceItem(
InvoiceEntity recurringInvoice, UpdateRecurringInvoiceItem action) {
if (recurringInvoice.lineItems.length <= action.index) {
return recurringInvoice;
}
return recurringInvoice
.rebuild((b) => b..lineItems[action.index] = action.recurringInvoiceItem);
}
final recurringInvoiceListReducer = combineReducers<ListUIState>([ final recurringInvoiceListReducer = combineReducers<ListUIState>([
TypedReducer<ListUIState, SortRecurringInvoices>(_sortRecurringInvoices), TypedReducer<ListUIState, SortRecurringInvoices>(_sortRecurringInvoices),
TypedReducer<ListUIState, FilterRecurringInvoicesByState>( TypedReducer<ListUIState, FilterRecurringInvoicesByState>(
_filterRecurringInvoicesByState), _filterRecurringInvoicesByState),
TypedReducer<ListUIState, FilterRecurringInvoicesByStatus>(
_filterRecurringInvoicesByStatus),
TypedReducer<ListUIState, FilterRecurringInvoices>(_filterRecurringInvoices), TypedReducer<ListUIState, FilterRecurringInvoices>(_filterRecurringInvoices),
TypedReducer<ListUIState, FilterRecurringInvoicesByCustom1>( TypedReducer<ListUIState, FilterRecurringInvoicesByCustom1>(
_filterRecurringInvoicesByCustom1), _filterRecurringInvoicesByCustom1),
TypedReducer<ListUIState, FilterRecurringInvoicesByCustom2>( TypedReducer<ListUIState, FilterRecurringInvoicesByCustom2>(
_filterRecurringInvoicesByCustom2), _filterRecurringInvoicesByCustom2),
TypedReducer<ListUIState, FilterRecurringInvoicesByCustom3>(
_filterRecurringInvoicesByCustom3),
TypedReducer<ListUIState, FilterRecurringInvoicesByCustom4>(
_filterRecurringInvoicesByCustom4),
TypedReducer<ListUIState, StartRecurringInvoiceMultiselect>( TypedReducer<ListUIState, StartRecurringInvoiceMultiselect>(
_startListMultiselect), _startListMultiselect),
TypedReducer<ListUIState, AddToRecurringInvoiceMultiselect>( TypedReducer<ListUIState, AddToRecurringInvoiceMultiselect>(
@ -89,38 +181,62 @@ final recurringInvoiceListReducer = combineReducers<ListUIState>([
]); ]);
ListUIState _filterRecurringInvoicesByCustom1( ListUIState _filterRecurringInvoicesByCustom1(
ListUIState recurringInvoiceListState, ListUIState recurringInvoiceListState, FilterRecurringInvoicesByCustom1 action) {
FilterRecurringInvoicesByCustom1 action) {
if (recurringInvoiceListState.custom1Filters.contains(action.value)) { if (recurringInvoiceListState.custom1Filters.contains(action.value)) {
return recurringInvoiceListState return recurringInvoiceListState
.rebuild((b) => b..custom1Filters.remove(action.value)); .rebuild((b) => b..custom1Filters.remove(action.value));
} else { } else {
return recurringInvoiceListState return recurringInvoiceListState.rebuild((b) => b..custom1Filters.add(action.value));
.rebuild((b) => b..custom1Filters.add(action.value));
} }
} }
ListUIState _filterRecurringInvoicesByCustom2( ListUIState _filterRecurringInvoicesByCustom2(
ListUIState recurringInvoiceListState, ListUIState recurringInvoiceListState, FilterRecurringInvoicesByCustom2 action) {
FilterRecurringInvoicesByCustom2 action) {
if (recurringInvoiceListState.custom2Filters.contains(action.value)) { if (recurringInvoiceListState.custom2Filters.contains(action.value)) {
return recurringInvoiceListState return recurringInvoiceListState
.rebuild((b) => b..custom2Filters.remove(action.value)); .rebuild((b) => b..custom2Filters.remove(action.value));
} else { } else {
return recurringInvoiceListState.rebuild((b) => b..custom2Filters.add(action.value));
}
}
ListUIState _filterRecurringInvoicesByCustom3(
ListUIState recurringInvoiceListState, FilterRecurringInvoicesByCustom3 action) {
if (recurringInvoiceListState.custom3Filters.contains(action.value)) {
return recurringInvoiceListState return recurringInvoiceListState
.rebuild((b) => b..custom2Filters.add(action.value)); .rebuild((b) => b..custom3Filters.remove(action.value));
} else {
return recurringInvoiceListState.rebuild((b) => b..custom3Filters.add(action.value));
}
}
ListUIState _filterRecurringInvoicesByCustom4(
ListUIState recurringInvoiceListState, FilterRecurringInvoicesByCustom4 action) {
if (recurringInvoiceListState.custom4Filters.contains(action.value)) {
return recurringInvoiceListState
.rebuild((b) => b..custom4Filters.remove(action.value));
} else {
return recurringInvoiceListState.rebuild((b) => b..custom4Filters.add(action.value));
} }
} }
ListUIState _filterRecurringInvoicesByState( ListUIState _filterRecurringInvoicesByState(
ListUIState recurringInvoiceListState, ListUIState recurringInvoiceListState, FilterRecurringInvoicesByState action) {
FilterRecurringInvoicesByState action) {
if (recurringInvoiceListState.stateFilters.contains(action.state)) { if (recurringInvoiceListState.stateFilters.contains(action.state)) {
return recurringInvoiceListState return recurringInvoiceListState
.rebuild((b) => b..stateFilters.remove(action.state)); .rebuild((b) => b..stateFilters.remove(action.state));
} else { } else {
return recurringInvoiceListState.rebuild((b) => b..stateFilters.add(action.state));
}
}
ListUIState _filterRecurringInvoicesByStatus(
ListUIState recurringInvoiceListState, FilterRecurringInvoicesByStatus action) {
if (recurringInvoiceListState.statusFilters.contains(action.status)) {
return recurringInvoiceListState return recurringInvoiceListState
.rebuild((b) => b..stateFilters.add(action.state)); .rebuild((b) => b..statusFilters.remove(action.status));
} else {
return recurringInvoiceListState.rebuild((b) => b..statusFilters.add(action.status));
} }
} }
@ -141,24 +257,24 @@ ListUIState _sortRecurringInvoices(
} }
ListUIState _startListMultiselect( ListUIState _startListMultiselect(
ListUIState productListState, StartRecurringInvoiceMultiselect action) { ListUIState recurringInvoiceListState, StartRecurringInvoiceMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = ListBuilder()); return recurringInvoiceListState.rebuild((b) => b..selectedIds = ListBuilder());
} }
ListUIState _addToListMultiselect( ListUIState _addToListMultiselect(
ListUIState productListState, AddToRecurringInvoiceMultiselect action) { ListUIState recurringInvoiceListState, AddToRecurringInvoiceMultiselect action) {
return productListState.rebuild((b) => b..selectedIds.add(action.entity.id)); return recurringInvoiceListState.rebuild((b) => b..selectedIds.add(action.entity.id));
} }
ListUIState _removeFromListMultiselect(ListUIState productListState, ListUIState _removeFromListMultiselect(ListUIState recurringInvoiceListState,
RemoveFromRecurringInvoiceMultiselect action) { RemoveFromRecurringInvoiceMultiselect action) {
return productListState return recurringInvoiceListState
.rebuild((b) => b..selectedIds.remove(action.entity.id)); .rebuild((b) => b..selectedIds.remove(action.entity.id));
} }
ListUIState _clearListMultiselect( ListUIState _clearListMultiselect(
ListUIState productListState, ClearRecurringInvoiceMultiselect action) { ListUIState recurringInvoiceListState, ClearRecurringInvoiceMultiselect action) {
return productListState.rebuild((b) => b..selectedIds = null); return recurringInvoiceListState.rebuild((b) => b..selectedIds = null);
} }
final recurringInvoicesReducer = combineReducers<RecurringInvoiceState>([ final recurringInvoicesReducer = combineReducers<RecurringInvoiceState>([
@ -168,9 +284,9 @@ final recurringInvoicesReducer = combineReducers<RecurringInvoiceState>([
_addRecurringInvoice), _addRecurringInvoice),
TypedReducer<RecurringInvoiceState, LoadRecurringInvoicesSuccess>( TypedReducer<RecurringInvoiceState, LoadRecurringInvoicesSuccess>(
_setLoadedRecurringInvoices), _setLoadedRecurringInvoices),
TypedReducer<RecurringInvoiceState, LoadRecurringInvoiceSuccess>(
_setLoadedRecurringInvoice),
TypedReducer<RecurringInvoiceState, LoadCompanySuccess>(_setLoadedCompany), TypedReducer<RecurringInvoiceState, LoadCompanySuccess>(_setLoadedCompany),
TypedReducer<RecurringInvoiceState, LoadRecurringInvoiceSuccess>(
_updateRecurringInvoice),
TypedReducer<RecurringInvoiceState, ArchiveRecurringInvoicesSuccess>( TypedReducer<RecurringInvoiceState, ArchiveRecurringInvoicesSuccess>(
_archiveRecurringInvoiceSuccess), _archiveRecurringInvoiceSuccess),
TypedReducer<RecurringInvoiceState, DeleteRecurringInvoicesSuccess>( TypedReducer<RecurringInvoiceState, DeleteRecurringInvoicesSuccess>(
@ -190,8 +306,7 @@ RecurringInvoiceState _archiveRecurringInvoiceSuccess(
} }
RecurringInvoiceState _deleteRecurringInvoiceSuccess( RecurringInvoiceState _deleteRecurringInvoiceSuccess(
RecurringInvoiceState recurringInvoiceState, RecurringInvoiceState recurringInvoiceState, DeleteRecurringInvoicesSuccess action) {
DeleteRecurringInvoicesSuccess action) {
return recurringInvoiceState.rebuild((b) { return recurringInvoiceState.rebuild((b) {
for (final recurringInvoice in action.recurringInvoices) { for (final recurringInvoice in action.recurringInvoices) {
b.map[recurringInvoice.id] = recurringInvoice; b.map[recurringInvoice.id] = recurringInvoice;
@ -199,6 +314,12 @@ RecurringInvoiceState _deleteRecurringInvoiceSuccess(
}); });
} }
RecurringInvoiceState _emailRecurringInvoiceSuccess(
RecurringInvoiceState recurringInvoiceState, EmailRecurringInvoiceSuccess action) {
return recurringInvoiceState
.rebuild((b) => b..map[action.recurringInvoice.id] = action.recurringInvoice);
}
RecurringInvoiceState _restoreRecurringInvoiceSuccess( RecurringInvoiceState _restoreRecurringInvoiceSuccess(
RecurringInvoiceState recurringInvoiceState, RecurringInvoiceState recurringInvoiceState,
RestoreRecurringInvoicesSuccess action) { RestoreRecurringInvoicesSuccess action) {
@ -210,25 +331,18 @@ RecurringInvoiceState _restoreRecurringInvoiceSuccess(
} }
RecurringInvoiceState _addRecurringInvoice( RecurringInvoiceState _addRecurringInvoice(
RecurringInvoiceState recurringInvoiceState, RecurringInvoiceState recurringInvoiceState, AddRecurringInvoiceSuccess action) {
AddRecurringInvoiceSuccess action) {
return recurringInvoiceState.rebuild((b) => b return recurringInvoiceState.rebuild((b) => b
..map[action.recurringInvoice.id] = action.recurringInvoice ..map[action.recurringInvoice.id] = action.recurringInvoice
..list.add(action.recurringInvoice.id)); ..list.add(action.recurringInvoice.id));
} }
RecurringInvoiceState _updateRecurringInvoice( RecurringInvoiceState _updateRecurringInvoice(
RecurringInvoiceState recurringInvoiceState, RecurringInvoiceState recurringInvoiceState, dynamic action) {
SaveRecurringInvoiceSuccess action) { final InvoiceEntity recurringInvoice = action.recurringInvoice;
return recurringInvoiceState.rebuild( return recurringInvoiceState.rebuild((b) => b
(b) => b..map[action.recurringInvoice.id] = action.recurringInvoice); ..map[action.recurringInvoice.id] = recurringInvoice
} .rebuild((b) => b..loadedAt = DateTime.now().millisecondsSinceEpoch));
RecurringInvoiceState _setLoadedRecurringInvoice(
RecurringInvoiceState recurringInvoiceState,
LoadRecurringInvoiceSuccess action) {
return recurringInvoiceState.rebuild(
(b) => b..map[action.recurringInvoice.id] = action.recurringInvoice);
} }
RecurringInvoiceState _setLoadedRecurringInvoices( RecurringInvoiceState _setLoadedRecurringInvoices(