481 lines
12 KiB
Dart
481 lines
12 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/constants.dart';
|
|
|
|
// Project imports:
|
|
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/product/product_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 '../document/document_actions.dart';
|
|
|
|
class ViewProductList implements PersistUI {
|
|
ViewProductList({
|
|
this.force = false,
|
|
this.page = 0,
|
|
});
|
|
|
|
final bool force;
|
|
final int page;
|
|
}
|
|
|
|
class ViewProduct implements PersistUI, PersistPrefs {
|
|
ViewProduct({@required this.productId, this.force = false});
|
|
|
|
final String productId;
|
|
final bool force;
|
|
}
|
|
|
|
class EditProduct implements PersistUI, PersistPrefs {
|
|
EditProduct({@required this.product, this.completer, this.force = false});
|
|
|
|
final ProductEntity product;
|
|
final Completer completer;
|
|
final bool force;
|
|
}
|
|
|
|
class UpdateProduct implements PersistUI {
|
|
UpdateProduct(this.product);
|
|
|
|
final ProductEntity product;
|
|
}
|
|
|
|
class LoadProductRequest implements StartLoading {}
|
|
|
|
class LoadProduct {
|
|
LoadProduct({this.completer, this.productId});
|
|
|
|
final Completer completer;
|
|
final String productId;
|
|
}
|
|
|
|
class LoadProductSuccess implements StopLoading, PersistData {
|
|
LoadProductSuccess(this.product);
|
|
|
|
final ProductEntity product;
|
|
|
|
@override
|
|
String toString() {
|
|
return 'LoadProductSuccess{product: $product}';
|
|
}
|
|
}
|
|
|
|
class LoadProductFailure implements StopLoading {
|
|
LoadProductFailure(this.error);
|
|
|
|
final dynamic error;
|
|
|
|
@override
|
|
String toString() {
|
|
return 'LoadProductFailure{error: $error}';
|
|
}
|
|
}
|
|
|
|
class LoadProducts {
|
|
LoadProducts({this.completer, this.page = 1});
|
|
|
|
final Completer completer;
|
|
final int page;
|
|
}
|
|
|
|
class LoadProductsRequest implements StartLoading {}
|
|
|
|
class LoadProductsFailure implements StopLoading {
|
|
LoadProductsFailure(this.error);
|
|
|
|
final dynamic error;
|
|
|
|
@override
|
|
String toString() {
|
|
return 'LoadProductsFailure{error: $error}';
|
|
}
|
|
}
|
|
|
|
class LoadProductsSuccess implements StopLoading {
|
|
LoadProductsSuccess(this.products);
|
|
|
|
final BuiltList<ProductEntity> products;
|
|
|
|
@override
|
|
String toString() {
|
|
return 'LoadProductsSuccess{products: $products}';
|
|
}
|
|
}
|
|
|
|
class SaveProductRequest implements StartSaving {
|
|
SaveProductRequest({this.product, this.completer});
|
|
|
|
final Completer completer;
|
|
final ProductEntity product;
|
|
}
|
|
|
|
class SaveProductSuccess implements StopSaving, PersistData, PersistUI {
|
|
SaveProductSuccess(this.product);
|
|
|
|
final ProductEntity product;
|
|
}
|
|
|
|
class AddProductSuccess implements StopSaving, PersistData, PersistUI {
|
|
AddProductSuccess(this.product);
|
|
|
|
final ProductEntity product;
|
|
}
|
|
|
|
class SaveProductFailure implements StopSaving {
|
|
SaveProductFailure(this.error);
|
|
|
|
final Object error;
|
|
}
|
|
|
|
class ArchiveProductsRequest implements StartSaving {
|
|
ArchiveProductsRequest(this.completer, this.productIds);
|
|
|
|
final Completer completer;
|
|
final List<String> productIds;
|
|
}
|
|
|
|
class ArchiveProductsSuccess implements StopSaving, PersistData {
|
|
ArchiveProductsSuccess(this.products);
|
|
|
|
final List<ProductEntity> products;
|
|
}
|
|
|
|
class ArchiveProductsFailure implements StopSaving {
|
|
ArchiveProductsFailure(this.products);
|
|
|
|
final List<ProductEntity> products;
|
|
}
|
|
|
|
class DeleteProductsRequest implements StartSaving {
|
|
DeleteProductsRequest(this.completer, this.productIds);
|
|
|
|
final Completer completer;
|
|
final List<String> productIds;
|
|
}
|
|
|
|
class DeleteProductsSuccess implements StopSaving, PersistData {
|
|
DeleteProductsSuccess(this.products);
|
|
|
|
final List<ProductEntity> products;
|
|
}
|
|
|
|
class DeleteProductsFailure implements StopSaving {
|
|
DeleteProductsFailure(this.products);
|
|
|
|
final List<ProductEntity> products;
|
|
}
|
|
|
|
class RestoreProductsRequest implements StartSaving {
|
|
RestoreProductsRequest(this.completer, this.productIds);
|
|
|
|
final Completer completer;
|
|
final List<String> productIds;
|
|
}
|
|
|
|
class RestoreProductsSuccess implements StopSaving, PersistData {
|
|
RestoreProductsSuccess(this.products);
|
|
|
|
final List<ProductEntity> products;
|
|
}
|
|
|
|
class RestoreProductsFailure implements StopSaving {
|
|
RestoreProductsFailure(this.products);
|
|
|
|
final List<ProductEntity> products;
|
|
}
|
|
|
|
class SetTaxCategoryProductsRequest implements StartSaving {
|
|
SetTaxCategoryProductsRequest(
|
|
{this.completer, this.productIds, this.taxCategoryId});
|
|
|
|
final Completer completer;
|
|
final List<String> productIds;
|
|
final String taxCategoryId;
|
|
}
|
|
|
|
class SetTaxCategoryProductsSuccess implements StopSaving, PersistData {
|
|
SetTaxCategoryProductsSuccess(this.products);
|
|
|
|
final List<ProductEntity> products;
|
|
}
|
|
|
|
class SetTaxCategoryProductsFailure implements StopSaving {
|
|
SetTaxCategoryProductsFailure(this.error);
|
|
|
|
final dynamic error;
|
|
}
|
|
|
|
class FilterProducts implements PersistUI {
|
|
FilterProducts(this.filter);
|
|
|
|
final String filter;
|
|
}
|
|
|
|
class SortProducts implements PersistUI, PersistPrefs {
|
|
SortProducts(this.field);
|
|
|
|
final String field;
|
|
}
|
|
|
|
class FilterProductsByState implements PersistUI {
|
|
FilterProductsByState(this.state);
|
|
|
|
final EntityState state;
|
|
}
|
|
|
|
class FilterProductsByCustom1 implements PersistUI {
|
|
FilterProductsByCustom1(this.value);
|
|
|
|
final String value;
|
|
}
|
|
|
|
class FilterProductsByCustom2 implements PersistUI {
|
|
FilterProductsByCustom2(this.value);
|
|
|
|
final String value;
|
|
}
|
|
|
|
class FilterProductsByCustom3 implements PersistUI {
|
|
FilterProductsByCustom3(this.value);
|
|
|
|
final String value;
|
|
}
|
|
|
|
class FilterProductsByCustom4 implements PersistUI {
|
|
FilterProductsByCustom4(this.value);
|
|
|
|
final String value;
|
|
}
|
|
|
|
class FilterProductDropdown {
|
|
FilterProductDropdown(this.filter);
|
|
|
|
final String filter;
|
|
}
|
|
|
|
void handleProductAction(
|
|
BuildContext context, List<BaseEntity> products, EntityAction action) {
|
|
if (products.isEmpty) {
|
|
return;
|
|
}
|
|
|
|
final store = StoreProvider.of<AppState>(context);
|
|
final state = store.state;
|
|
final localization = AppLocalization.of(context);
|
|
final productIds = products.map((product) => product.id).toList();
|
|
final product = products.first;
|
|
|
|
switch (action) {
|
|
case EntityAction.newInvoice:
|
|
final invoice = InvoiceEntity(state: state);
|
|
createEntity(
|
|
context: context,
|
|
entity: invoice.rebuild(
|
|
(b) => b
|
|
..lineItems.addAll(
|
|
productIds.map(
|
|
(productId) => convertProductToInvoiceItem(
|
|
company: state.company,
|
|
invoice: invoice,
|
|
product: state.productState.map[productId],
|
|
currencyMap: state.staticState.currencyMap,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
break;
|
|
case EntityAction.newPurchaseOrder:
|
|
final invoice =
|
|
InvoiceEntity(state: state, entityType: EntityType.purchaseOrder);
|
|
createEntity(
|
|
context: context,
|
|
entity: invoice.rebuild(
|
|
(b) => b
|
|
..lineItems.addAll(
|
|
productIds.map(
|
|
(productId) => convertProductToInvoiceItem(
|
|
company: state.company,
|
|
invoice: invoice,
|
|
product: state.productState.map[productId],
|
|
currencyMap: state.staticState.currencyMap,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
break;
|
|
case EntityAction.edit:
|
|
editEntity(entity: product);
|
|
break;
|
|
case EntityAction.clone:
|
|
createEntity(context: context, entity: (product as ProductEntity).clone);
|
|
break;
|
|
case EntityAction.restore:
|
|
final message = productIds.length > 1
|
|
? localization.restoredProducts
|
|
.replaceFirst(':value', ':count')
|
|
.replaceFirst(':count', productIds.length.toString())
|
|
: localization.restoredProduct;
|
|
store.dispatch(RestoreProductsRequest(
|
|
snackBarCompleter<Null>(context, message), productIds));
|
|
break;
|
|
case EntityAction.archive:
|
|
final message = productIds.length > 1
|
|
? localization.archivedProducts
|
|
.replaceFirst(':value', ':count')
|
|
.replaceFirst(':count', productIds.length.toString())
|
|
: localization.archivedProduct;
|
|
store.dispatch(ArchiveProductsRequest(
|
|
snackBarCompleter<Null>(context, message), productIds));
|
|
break;
|
|
case EntityAction.delete:
|
|
final message = productIds.length > 1
|
|
? localization.deletedProducts
|
|
.replaceFirst(':value', ':count')
|
|
.replaceFirst(':count', productIds.length.toString())
|
|
: localization.deletedProduct;
|
|
store.dispatch(DeleteProductsRequest(
|
|
snackBarCompleter<Null>(context, message), productIds));
|
|
break;
|
|
case EntityAction.toggleMultiselect:
|
|
if (!store.state.productListState.isInMultiselect()) {
|
|
store.dispatch(StartProductMultiselect());
|
|
}
|
|
|
|
if (products.isEmpty) {
|
|
break;
|
|
}
|
|
|
|
for (final product in products) {
|
|
if (!store.state.productListState.isSelected(product.id)) {
|
|
store.dispatch(AddToProductMultiselect(entity: product));
|
|
} else {
|
|
store.dispatch(RemoveFromProductMultiselect(entity: product));
|
|
}
|
|
}
|
|
break;
|
|
case EntityAction.more:
|
|
showEntityActionsDialog(
|
|
entities: [product],
|
|
);
|
|
break;
|
|
case EntityAction.documents:
|
|
final documentIds = <String>[];
|
|
for (var product in products) {
|
|
for (var document in (product as ProductEntity).documents) {
|
|
documentIds.add(document.id);
|
|
}
|
|
}
|
|
if (documentIds.isEmpty) {
|
|
showMessageDialog(
|
|
context: context, message: localization.noDocumentsToDownload);
|
|
} else {
|
|
store.dispatch(
|
|
DownloadDocumentsRequest(
|
|
documentIds: documentIds,
|
|
completer: snackBarCompleter<Null>(
|
|
context,
|
|
localization.exportedData,
|
|
),
|
|
),
|
|
);
|
|
}
|
|
break;
|
|
case EntityAction.setTaxCategory:
|
|
showDialog<void>(
|
|
context: context,
|
|
builder: (context) {
|
|
return SimpleDialog(
|
|
title: Text(localization.setTaxCategory),
|
|
children: kTaxCategories.keys.map((taxCategoryId) {
|
|
final taxCategory = kTaxCategories[taxCategoryId];
|
|
return SimpleDialogOption(
|
|
child: Text(localization.lookup(taxCategory)),
|
|
onPressed: () {
|
|
Navigator.of(context).pop();
|
|
store.dispatch(SetTaxCategoryProductsRequest(
|
|
productIds: productIds,
|
|
taxCategoryId: taxCategoryId,
|
|
completer: snackBarCompleter<Null>(
|
|
context,
|
|
productIds.length == 1
|
|
? localization.updatedTaxCategory
|
|
: localization.updatedTaxCategories)));
|
|
},
|
|
);
|
|
}).toList(),
|
|
);
|
|
});
|
|
break;
|
|
default:
|
|
print('## ERROR: unhandled action $action in product_actions');
|
|
break;
|
|
}
|
|
}
|
|
|
|
class StartProductMultiselect {}
|
|
|
|
class AddToProductMultiselect {
|
|
AddToProductMultiselect({@required this.entity});
|
|
|
|
final BaseEntity entity;
|
|
}
|
|
|
|
class RemoveFromProductMultiselect {
|
|
RemoveFromProductMultiselect({@required this.entity});
|
|
|
|
final BaseEntity entity;
|
|
}
|
|
|
|
class ClearProductMultiselect {}
|
|
|
|
class SaveProductDocumentRequest implements StartSaving {
|
|
SaveProductDocumentRequest({
|
|
@required this.completer,
|
|
@required this.multipartFile,
|
|
@required this.product,
|
|
});
|
|
|
|
final Completer completer;
|
|
final MultipartFile multipartFile;
|
|
final ProductEntity product;
|
|
}
|
|
|
|
class SaveProductDocumentSuccess implements StopSaving, PersistData, PersistUI {
|
|
SaveProductDocumentSuccess(this.document);
|
|
|
|
final DocumentEntity document;
|
|
}
|
|
|
|
class SaveProductDocumentFailure implements StopSaving {
|
|
SaveProductDocumentFailure(this.error);
|
|
|
|
final Object error;
|
|
}
|
|
|
|
class UpdateProductTab implements PersistUI {
|
|
UpdateProductTab({this.tabIndex});
|
|
|
|
final int tabIndex;
|
|
}
|
|
|
|
class UpdateClientTab implements PersistUI {
|
|
UpdateClientTab({this.tabIndex});
|
|
|
|
final int tabIndex;
|
|
}
|