Support adding task/expenses to invoices
This commit is contained in:
parent
501668e44b
commit
b143b869ce
|
|
@ -352,6 +352,9 @@ abstract class ExpenseEntity extends Object
|
||||||
shouldBeInvoiced &&
|
shouldBeInvoiced &&
|
||||||
userCompany.canCreate(EntityType.invoice)) {
|
userCompany.canCreate(EntityType.invoice)) {
|
||||||
actions.add(EntityAction.invoiceExpense);
|
actions.add(EntityAction.invoiceExpense);
|
||||||
|
if ((clientId ?? '').isNotEmpty) {
|
||||||
|
actions.add(EntityAction.addToInvoice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,7 @@ class EntityAction extends EnumClass {
|
||||||
static const EntityAction disconnect = _$disconnect;
|
static const EntityAction disconnect = _$disconnect;
|
||||||
static const EntityAction viewInvoice = _$viewInvoice;
|
static const EntityAction viewInvoice = _$viewInvoice;
|
||||||
static const EntityAction changeStatus = _$changeStatus;
|
static const EntityAction changeStatus = _$changeStatus;
|
||||||
|
static const EntityAction addToInvoice = _$addToInvoice;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ const EntityAction _$resendInvite = const EntityAction._('resendInvite');
|
||||||
const EntityAction _$disconnect = const EntityAction._('disconnect');
|
const EntityAction _$disconnect = const EntityAction._('disconnect');
|
||||||
const EntityAction _$viewInvoice = const EntityAction._('viewInvoice');
|
const EntityAction _$viewInvoice = const EntityAction._('viewInvoice');
|
||||||
const EntityAction _$changeStatus = const EntityAction._('changeStatus');
|
const EntityAction _$changeStatus = const EntityAction._('changeStatus');
|
||||||
|
const EntityAction _$addToInvoice = const EntityAction._('addToInvoice');
|
||||||
|
|
||||||
EntityAction _$valueOf(String name) {
|
EntityAction _$valueOf(String name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
|
|
@ -202,6 +203,8 @@ EntityAction _$valueOf(String name) {
|
||||||
return _$viewInvoice;
|
return _$viewInvoice;
|
||||||
case 'changeStatus':
|
case 'changeStatus':
|
||||||
return _$changeStatus;
|
return _$changeStatus;
|
||||||
|
case 'addToInvoice':
|
||||||
|
return _$addToInvoice;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentError(name);
|
throw new ArgumentError(name);
|
||||||
}
|
}
|
||||||
|
|
@ -271,6 +274,7 @@ final BuiltSet<EntityAction> _$values =
|
||||||
_$disconnect,
|
_$disconnect,
|
||||||
_$viewInvoice,
|
_$viewInvoice,
|
||||||
_$changeStatus,
|
_$changeStatus,
|
||||||
|
_$addToInvoice,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Serializer<EntityAction> _$entityActionSerializer =
|
Serializer<EntityAction> _$entityActionSerializer =
|
||||||
|
|
|
||||||
|
|
@ -608,6 +608,9 @@ abstract class TaskEntity extends Object
|
||||||
if (!isInvoiced && !isRunning) {
|
if (!isInvoiced && !isRunning) {
|
||||||
if (userCompany.canCreate(EntityType.invoice)) {
|
if (userCompany.canCreate(EntityType.invoice)) {
|
||||||
actions.add(EntityAction.invoiceTask);
|
actions.add(EntityAction.invoiceTask);
|
||||||
|
if ((clientId ?? '').isNotEmpty) {
|
||||||
|
actions.add(EntityAction.addToInvoice);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import 'package:invoiceninja_flutter/redux/document/document_actions.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/expense/expense_selectors.dart';
|
import 'package:invoiceninja_flutter/redux/expense/expense_selectors.dart';
|
||||||
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
|
import 'package:invoiceninja_flutter/ui/app/entities/entity_actions_dialog.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/completers.dart';
|
import 'package:invoiceninja_flutter/utils/completers.dart';
|
||||||
|
import 'package:invoiceninja_flutter/utils/dialogs.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/localization.dart';
|
import 'package:invoiceninja_flutter/utils/localization.dart';
|
||||||
|
|
||||||
class ViewExpenseList implements PersistUI {
|
class ViewExpenseList implements PersistUI {
|
||||||
|
|
@ -277,6 +278,7 @@ void handleExpenseAction(
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case EntityAction.invoiceExpense:
|
case EntityAction.invoiceExpense:
|
||||||
|
case EntityAction.addToInvoice:
|
||||||
final availableExpenses = expenses.where((entity) {
|
final availableExpenses = expenses.where((entity) {
|
||||||
final expense = entity as ExpenseEntity;
|
final expense = entity as ExpenseEntity;
|
||||||
return !expense.isDeleted && !expense.isInvoiced;
|
return !expense.isDeleted && !expense.isInvoiced;
|
||||||
|
|
@ -304,6 +306,7 @@ void handleExpenseAction(
|
||||||
))
|
))
|
||||||
.toList();
|
.toList();
|
||||||
if (items.isNotEmpty) {
|
if (items.isNotEmpty) {
|
||||||
|
if (action == EntityAction.invoiceExpense) {
|
||||||
createEntity(
|
createEntity(
|
||||||
context: context,
|
context: context,
|
||||||
entity: InvoiceEntity(state: state, client: client).rebuild(
|
entity: InvoiceEntity(state: state, client: client).rebuild(
|
||||||
|
|
@ -313,6 +316,13 @@ void handleExpenseAction(
|
||||||
..vendorId = vendorId,
|
..vendorId = vendorId,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else {
|
||||||
|
addToInvoiceDialog(
|
||||||
|
context: context,
|
||||||
|
clientId: expense.clientId,
|
||||||
|
items: items,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EntityAction.restore:
|
case EntityAction.restore:
|
||||||
|
|
|
||||||
|
|
@ -382,6 +382,7 @@ void handleTaskAction(
|
||||||
StopTasksRequest(snackBarCompleter<Null>(context, message), taskIds));
|
StopTasksRequest(snackBarCompleter<Null>(context, message), taskIds));
|
||||||
break;
|
break;
|
||||||
case EntityAction.invoiceTask:
|
case EntityAction.invoiceTask:
|
||||||
|
case EntityAction.addToInvoice:
|
||||||
tasks.sort((taskA, taskB) {
|
tasks.sort((taskA, taskB) {
|
||||||
final taskAEntity = taskA as TaskEntity;
|
final taskAEntity = taskA as TaskEntity;
|
||||||
final taskBEntity = taskB as TaskEntity;
|
final taskBEntity = taskB as TaskEntity;
|
||||||
|
|
@ -435,11 +436,20 @@ void handleTaskAction(
|
||||||
});
|
});
|
||||||
|
|
||||||
if (items.isNotEmpty) {
|
if (items.isNotEmpty) {
|
||||||
|
if (action == EntityAction.invoiceTask) {
|
||||||
createEntity(
|
createEntity(
|
||||||
context: context,
|
context: context,
|
||||||
entity: InvoiceEntity(state: state, client: client).rebuild((b) => b
|
entity:
|
||||||
|
InvoiceEntity(state: state, client: client).rebuild((b) => b
|
||||||
..lineItems.addAll(items)
|
..lineItems.addAll(items)
|
||||||
..projectId = projectId));
|
..projectId = projectId));
|
||||||
|
} else {
|
||||||
|
addToInvoiceDialog(
|
||||||
|
context: context,
|
||||||
|
clientId: task.clientId,
|
||||||
|
items: items,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case EntityAction.clone:
|
case EntityAction.clone:
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_redux/flutter_redux.dart';
|
import 'package:flutter_redux/flutter_redux.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/task/task_actions.dart';
|
import 'package:invoiceninja_flutter/redux/task/task_actions.dart';
|
||||||
import 'package:invoiceninja_flutter/redux/task_status/task_status_selectors.dart';
|
import 'package:invoiceninja_flutter/redux/task_status/task_status_selectors.dart';
|
||||||
|
import 'package:invoiceninja_flutter/utils/formatting.dart';
|
||||||
import 'package:invoiceninja_flutter/utils/platforms.dart';
|
import 'package:invoiceninja_flutter/utils/platforms.dart';
|
||||||
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
import 'package:pointer_interceptor/pointer_interceptor.dart';
|
||||||
|
|
||||||
|
|
@ -508,3 +509,53 @@ void changeTaskStatusDialog({
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void addToInvoiceDialog({
|
||||||
|
@required BuildContext context,
|
||||||
|
@required String clientId,
|
||||||
|
@required List<InvoiceItemEntity> items,
|
||||||
|
}) {
|
||||||
|
final localization = AppLocalization.of(context);
|
||||||
|
final store = StoreProvider.of<AppState>(context);
|
||||||
|
final state = store.state;
|
||||||
|
|
||||||
|
final invoices = state.invoiceState.map.values.where((invoice) {
|
||||||
|
if (clientId != invoice.clientId) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return invoice.isActive && !invoice.isPaid;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (invoices.isEmpty) {
|
||||||
|
showMessageDialog(context: context, message: localization.noInvoicesFound);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
showDialog<AlertDialog>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
return SimpleDialog(
|
||||||
|
title: Text(localization.addToInvoice),
|
||||||
|
children: invoices.map((invoice) {
|
||||||
|
return SimpleDialogOption(
|
||||||
|
child: Row(children: [
|
||||||
|
Expanded(child: Text(invoice.number)),
|
||||||
|
Text(
|
||||||
|
formatNumber(invoice.amount, context,
|
||||||
|
clientId: invoice.clientId),
|
||||||
|
),
|
||||||
|
]),
|
||||||
|
onPressed: () {
|
||||||
|
editEntity(
|
||||||
|
context: context,
|
||||||
|
entity: invoice.rebuild(
|
||||||
|
(b) => b..lineItems.addAll(items),
|
||||||
|
));
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,8 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
||||||
static final Map<String, Map<String, String>> _localizedValues = {
|
static final Map<String, Map<String, String>> _localizedValues = {
|
||||||
'en': {
|
'en': {
|
||||||
// STARTER: lang key - do not remove comment
|
// STARTER: lang key - do not remove comment
|
||||||
|
'add_to_invoice': 'Add To Invoice',
|
||||||
|
'no_invoices_found': 'No invoices found',
|
||||||
'week': 'Week',
|
'week': 'Week',
|
||||||
'created_record': 'Successfully created record',
|
'created_record': 'Successfully created record',
|
||||||
'notification_invoice_sent': 'Invoice Sent',
|
'notification_invoice_sent': 'Invoice Sent',
|
||||||
|
|
@ -75046,6 +75048,14 @@ mixin LocalizationsProvider on LocaleCodeAware {
|
||||||
_localizedValues[localeCode]['week'] ??
|
_localizedValues[localeCode]['week'] ??
|
||||||
_localizedValues[localeCode]['week'];
|
_localizedValues[localeCode]['week'];
|
||||||
|
|
||||||
|
String get addToInvoice =>
|
||||||
|
_localizedValues[localeCode]['add_to_invoice'] ??
|
||||||
|
_localizedValues[localeCode]['add_to_invoice'];
|
||||||
|
|
||||||
|
String get noInvoicesFound =>
|
||||||
|
_localizedValues[localeCode]['no_invoices_found'] ??
|
||||||
|
_localizedValues[localeCode]['no_invoices_found'];
|
||||||
|
|
||||||
// STARTER: lang field - do not remove comment
|
// STARTER: lang field - do not remove comment
|
||||||
|
|
||||||
String lookup(String key) {
|
String lookup(String key) {
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,7 @@ IconData getEntityActionIcon(EntityAction entityAction) {
|
||||||
case EntityAction.invoiceTask:
|
case EntityAction.invoiceTask:
|
||||||
case EntityAction.invoiceExpense:
|
case EntityAction.invoiceExpense:
|
||||||
case EntityAction.invoiceProject:
|
case EntityAction.invoiceProject:
|
||||||
|
case EntityAction.addToInvoice:
|
||||||
return Icons.add_circle_outline;
|
return Icons.add_circle_outline;
|
||||||
case EntityAction.resume:
|
case EntityAction.resume:
|
||||||
case EntityAction.start:
|
case EntityAction.start:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue