invoice/lib/redux/credit/credit_actions.dart

692 lines
18 KiB
Dart

// Dart imports:
import 'dart:async';
// Flutter imports:
import 'package:flutter/material.dart';
// Package imports:
import 'package:built_collection/built_collection.dart';
import 'package:flutter_redux/flutter_redux.dart';
import 'package:http/http.dart';
import 'package:invoiceninja_flutter/redux/document/document_actions.dart';
import 'package:url_launcher/url_launcher.dart';
// Project imports:
import 'package:invoiceninja_flutter/constants.dart';
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/design/design_selectors.dart';
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
import 'package:invoiceninja_flutter/utils/completers.dart';
import 'package:invoiceninja_flutter/utils/dialogs.dart';
import 'package:invoiceninja_flutter/utils/localization.dart';
import 'package:http/http.dart' as http;
import 'package:invoiceninja_flutter/data/web_client.dart';
import 'package:printing/printing.dart';
class ViewCreditList implements PersistUI {
ViewCreditList({this.force = false});
final bool force;
}
class ViewCredit implements PersistUI, PersistPrefs {
ViewCredit({
this.creditId,
this.force = false,
});
final String creditId;
final bool force;
}
class EditCredit implements PersistUI, PersistPrefs {
EditCredit(
{this.credit, this.creditItemIndex, this.completer, this.force = false});
final InvoiceEntity credit;
final int creditItemIndex;
final Completer completer;
final bool force;
}
class ShowEmailCredit {
ShowEmailCredit({this.credit, this.context, this.completer});
final InvoiceEntity credit;
final BuildContext context;
final Completer completer;
}
class ShowPdfCredit {
ShowPdfCredit({this.credit, this.context, this.activityId});
final InvoiceEntity credit;
final BuildContext context;
final String activityId;
}
class EditCreditItem implements PersistUI {
EditCreditItem([this.creditItemIndex]);
final int creditItemIndex;
}
class UpdateCredit implements PersistUI {
UpdateCredit(this.credit);
final InvoiceEntity credit;
}
class UpdateCreditClient implements PersistUI {
UpdateCreditClient({this.client});
final ClientEntity client;
}
class LoadCredit {
LoadCredit({this.completer, this.creditId});
final Completer completer;
final String creditId;
}
class LoadCredits {
LoadCredits({this.completer});
final Completer completer;
}
class LoadCreditRequest implements StartLoading {}
class LoadCreditFailure implements StopLoading {
LoadCreditFailure(this.error);
final dynamic error;
@override
String toString() {
return 'LoadCreditFailure{error: $error}';
}
}
class LoadCreditSuccess implements StopLoading, PersistData {
LoadCreditSuccess(this.credit);
final InvoiceEntity credit;
@override
String toString() {
return 'LoadCreditSuccess{credit: $credit}';
}
}
class LoadCreditsRequest implements StartLoading {}
class LoadCreditsFailure implements StopLoading {
LoadCreditsFailure(this.error);
final dynamic error;
@override
String toString() {
return 'LoadCreditsFailure{error: $error}';
}
}
class LoadCreditsSuccess implements StopLoading {
LoadCreditsSuccess(this.credits);
final BuiltList<InvoiceEntity> credits;
@override
String toString() {
return 'LoadCreditsSuccess{credits: $credits}';
}
}
class AddCreditContact implements PersistUI {
AddCreditContact({this.contact, this.invitation});
final ContactEntity contact;
final InvitationEntity invitation;
}
class RemoveCreditContact implements PersistUI {
RemoveCreditContact({this.invitation});
final InvitationEntity invitation;
}
class AddCreditItem implements PersistUI {
AddCreditItem({this.creditItem});
final InvoiceItemEntity creditItem;
}
class MoveCreditItem implements PersistUI {
MoveCreditItem({
this.oldIndex,
this.newIndex,
});
final int oldIndex;
final int newIndex;
}
class AddCreditItems implements PersistUI {
AddCreditItems(this.creditItems);
final List<InvoiceItemEntity> creditItems;
}
class UpdateCreditItem implements PersistUI {
UpdateCreditItem({this.index, this.creditItem});
final int index;
final InvoiceItemEntity creditItem;
}
class DeleteCreditItem implements PersistUI {
DeleteCreditItem(this.index);
final int index;
}
class SaveCreditRequest implements StartSaving {
SaveCreditRequest({
@required this.completer,
@required this.credit,
@required this.action,
});
final Completer completer;
final InvoiceEntity credit;
final EntityAction action;
}
class SaveCreditSuccess implements StopSaving, PersistData, PersistUI {
SaveCreditSuccess(this.credit);
final InvoiceEntity credit;
}
class AddCreditSuccess implements StopSaving, PersistData, PersistUI {
AddCreditSuccess(this.credit);
final InvoiceEntity credit;
}
class SaveCreditFailure implements StopSaving {
SaveCreditFailure(this.error);
final Object error;
}
class EmailCreditRequest implements StartSaving {
EmailCreditRequest(
{this.completer, this.creditId, this.template, this.subject, this.body});
final Completer completer;
final String creditId;
final EmailTemplate template;
final String subject;
final String body;
}
class EmailCreditSuccess implements StopSaving, PersistData {}
class EmailCreditFailure implements StopSaving {
EmailCreditFailure(this.error);
final dynamic error;
}
class MarkSentCreditRequest implements StartSaving {
MarkSentCreditRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class MarkSentCreditSuccess implements StopSaving, PersistData {
MarkSentCreditSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class MarkSentCreditFailure implements StopSaving {
MarkSentCreditFailure(this.error);
final Object error;
}
class BulkEmailCreditsRequest implements StartSaving {
BulkEmailCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class BulkEmailCreditsSuccess implements StopSaving, PersistData {
BulkEmailCreditsSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class BulkEmailCreditsFailure implements StopSaving {
BulkEmailCreditsFailure(this.error);
final dynamic error;
}
class MarkCreditsPaidRequest implements StartSaving {
MarkCreditsPaidRequest(this.completer, this.invoiceIds);
final Completer completer;
final List<String> invoiceIds;
}
class MarkCreditsPaidSuccess implements StopSaving {
MarkCreditsPaidSuccess(this.invoices);
final List<InvoiceEntity> invoices;
}
class MarkCreditsPaidFailure implements StopSaving {
MarkCreditsPaidFailure(this.error);
final dynamic error;
}
class ArchiveCreditsRequest implements StartSaving {
ArchiveCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class ArchiveCreditsSuccess implements StopSaving, PersistData {
ArchiveCreditsSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class ArchiveCreditsFailure implements StopSaving {
ArchiveCreditsFailure(this.credits);
final List<InvoiceEntity> credits;
}
class DeleteCreditsRequest implements StartSaving {
DeleteCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class DeleteCreditsSuccess implements StopSaving, PersistData {
DeleteCreditsSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class DeleteCreditsFailure implements StopSaving {
DeleteCreditsFailure(this.credits);
final List<InvoiceEntity> credits;
}
class DownloadCreditsRequest implements StartSaving {
DownloadCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class DownloadCreditsSuccess implements StopSaving {}
class DownloadCreditsFailure implements StopSaving {
DownloadCreditsFailure(this.error);
final Object error;
}
class RestoreCreditsRequest implements StartSaving {
RestoreCreditsRequest(this.completer, this.creditIds);
final Completer completer;
final List<String> creditIds;
}
class RestoreCreditsSuccess implements StopSaving, PersistData {
RestoreCreditsSuccess(this.credits);
final List<InvoiceEntity> credits;
}
class RestoreCreditsFailure implements StopSaving {
RestoreCreditsFailure(this.credits);
final List<InvoiceEntity> credits;
}
class FilterCredits implements PersistUI {
FilterCredits(this.filter);
final String filter;
}
class SortCredits implements PersistUI, PersistPrefs {
SortCredits(this.field);
final String field;
}
class FilterCreditsByState implements PersistUI {
FilterCreditsByState(this.state);
final EntityState state;
}
class FilterCreditsByStatus implements PersistUI {
FilterCreditsByStatus(this.status);
final EntityStatus status;
}
class FilterCreditDropdown {
FilterCreditDropdown(this.filter);
final String filter;
}
class FilterCreditsByCustom1 implements PersistUI {
FilterCreditsByCustom1(this.value);
final String value;
}
class FilterCreditsByCustom2 implements PersistUI {
FilterCreditsByCustom2(this.value);
final String value;
}
class FilterCreditsByCustom3 implements PersistUI {
FilterCreditsByCustom3(this.value);
final String value;
}
class FilterCreditsByCustom4 implements PersistUI {
FilterCreditsByCustom4(this.value);
final String value;
}
class SaveCreditDocumentRequest implements StartSaving {
SaveCreditDocumentRequest({
@required this.completer,
@required this.multipartFile,
@required this.credit,
});
final Completer completer;
final MultipartFile multipartFile;
final InvoiceEntity credit;
}
class SaveCreditDocumentSuccess implements StopSaving, PersistData, PersistUI {
SaveCreditDocumentSuccess(this.document);
final DocumentEntity document;
}
class SaveCreditDocumentFailure implements StopSaving {
SaveCreditDocumentFailure(this.error);
final Object error;
}
Future handleCreditAction(
BuildContext context, List<BaseEntity> credits, EntityAction action) async {
final store = StoreProvider.of<AppState>(context);
final state = store.state;
final localization = AppLocalization.of(context);
final credit = credits.first as InvoiceEntity;
final creditIds = credits.map((credit) => credit.id).toList();
switch (action) {
case EntityAction.edit:
editEntity(context: context, entity: credit);
break;
case EntityAction.viewPdf:
store.dispatch(ShowPdfCredit(credit: credit, context: context));
break;
case EntityAction.clientPortal:
if (await canLaunch(credit.invitationSilentLink)) {
await launch(credit.invitationSilentLink,
forceSafariVC: false, forceWebView: false);
}
break;
case EntityAction.markSent:
store.dispatch(MarkSentCreditRequest(
snackBarCompleter<Null>(context, localization.markedCreditAsSent),
creditIds));
break;
case EntityAction.emailCredit:
case EntityAction.bulkEmailCredit:
bool emailValid = true;
creditIds.forEach((element) {
final client = state.clientState.get(credit.clientId);
if (!client.hasEmailAddress) {
emailValid = false;
}
});
if (!emailValid) {
showMessageDialog(
context: context,
message: localization.clientEmailNotSet,
secondaryActions: [
TextButton(
onPressed: () {
Navigator.of(context).pop();
editEntity(
context: context,
entity: state.clientState.get(credit.clientId));
},
child: Text(localization.editClient.toUpperCase()))
]);
return;
}
if (action == EntityAction.emailCredit) {
store.dispatch(ShowEmailCredit(
completer:
snackBarCompleter<Null>(context, localization.emailedCredit),
credit: credit,
context: context));
} else {
confirmCallback(
context: context,
message: localization.bulkEmailCredit,
callback: (_) {
store.dispatch(BulkEmailCreditsRequest(
snackBarCompleter<Null>(
context,
creditIds.length == 1
? localization.emailedCredit
: localization.emailedCredits),
creditIds));
});
}
break;
case EntityAction.cloneToOther:
cloneToDialog(context: context, invoice: credit);
break;
case EntityAction.cloneToInvoice:
final designId = getDesignIdForClientByEntity(
state: state,
clientId: credit.clientId,
entityType: EntityType.invoice);
createEntity(
context: context,
entity: credit.clone.rebuild((b) => b
..entityType = EntityType.invoice
..designId = designId));
break;
case EntityAction.cloneToQuote:
final designId = getDesignIdForClientByEntity(
state: state,
clientId: credit.clientId,
entityType: EntityType.quote);
createEntity(
context: context,
entity: credit.clone.rebuild((b) => b
..entityType = EntityType.quote
..designId = designId));
break;
case EntityAction.clone:
case EntityAction.cloneToCredit:
createEntity(context: context, entity: credit.clone);
break;
case EntityAction.cloneToRecurring:
final designId = getDesignIdForClientByEntity(
state: state,
clientId: credit.clientId,
entityType: EntityType.invoice);
createEntity(
context: context,
entity: credit.clone.rebuild((b) => b
..entityType = EntityType.recurringInvoice
..designId = designId));
break;
case EntityAction.markPaid:
store.dispatch(MarkCreditsPaidRequest(
snackBarCompleter<Null>(
context,
credits.length == 1
? localization.markedCreditAsPaid
: localization.markedCreditsAsPaid),
creditIds));
break;
case EntityAction.applyCredit:
createEntity(
context: context,
entity: PaymentEntity(state: state).rebuild((b) => b
..typeId = kPaymentTypeCredit
..clientId = credit.clientId
..credits.addAll(credits
.map((credit) => PaymentableEntity.fromCredit(credit))
.toList())),
filterEntity: state.clientState.map[credit.clientId],
);
break;
case EntityAction.download:
launch(credit.invitationDownloadLink);
break;
case EntityAction.bulkDownload:
store.dispatch(DownloadCreditsRequest(
snackBarCompleter<Null>(context, localization.exportedData),
creditIds));
break;
case EntityAction.restore:
final message = creditIds.length > 1
? localization.restoredCredits
.replaceFirst(':value', creditIds.length.toString())
: localization.restoredCredit;
store.dispatch(RestoreCreditsRequest(
snackBarCompleter<Null>(context, message), creditIds));
break;
case EntityAction.archive:
final message = creditIds.length > 1
? localization.archivedCredits
.replaceFirst(':value', creditIds.length.toString())
: localization.archivedCredit;
store.dispatch(ArchiveCreditsRequest(
snackBarCompleter<Null>(context, message), creditIds));
break;
case EntityAction.delete:
final message = creditIds.length > 1
? localization.deletedCredits
.replaceFirst(':value', creditIds.length.toString())
: localization.deletedCredit;
store.dispatch(DeleteCreditsRequest(
snackBarCompleter<Null>(context, message), creditIds));
break;
case EntityAction.toggleMultiselect:
if (!store.state.creditListState.isInMultiselect()) {
store.dispatch(StartCreditMultiselect());
}
for (final credit in credits) {
if (!store.state.creditListState.isSelected(credit.id)) {
store.dispatch(AddToCreditMultiselect(entity: credit));
} else {
store.dispatch(RemoveFromCreditMultiselect(entity: credit));
}
}
break;
case EntityAction.printPdf:
final invitation = credit.invitations.first;
final url = invitation.downloadLink;
store.dispatch(StartLoading());
final http.Response response =
await WebClient().get(url, '', rawResponse: true);
store.dispatch(StopLoading());
await Printing.layoutPdf(onLayout: (_) => response.bodyBytes);
break;
case EntityAction.more:
showEntityActionsDialog(
entities: [credit],
);
break;
case EntityAction.documents:
final documentIds = <String>[];
for (var credit in credits) {
for (var document in (credit as InvoiceEntity).documents) {
documentIds.add(document.id);
}
}
store.dispatch(
DownloadDocumentsRequest(
documentIds: documentIds,
completer: snackBarCompleter<Null>(
context,
localization.exportedData,
),
),
);
break;
default:
print('## ERROR: unhandled action $action in credit_actions');
break;
}
}
class StartCreditMultiselect {}
class AddToCreditMultiselect {
AddToCreditMultiselect({@required this.entity});
final BaseEntity entity;
}
class RemoveFromCreditMultiselect {
RemoveFromCreditMultiselect({@required this.entity});
final BaseEntity entity;
}
class ClearCreditMultiselect {}
class UpdateCreditTab implements PersistUI {
UpdateCreditTab({this.tabIndex});
final int tabIndex;
}