From 943104f20cd2808196108fe1d07e0b954a856cf8 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 11 Apr 2023 22:52:27 +1000 Subject: [PATCH] Add actions to issue or cancel purchase orders (#313) --- lib/inventree/purchase_order.dart | 21 +++++++++ lib/l10n/app_en.arb | 9 ++++ lib/widget/dialogs.dart | 2 +- lib/widget/purchase_order_detail.dart | 68 +++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) diff --git a/lib/inventree/purchase_order.dart b/lib/inventree/purchase_order.dart index 2ee14890..abfd0ba3 100644 --- a/lib/inventree/purchase_order.dart +++ b/lib/inventree/purchase_order.dart @@ -93,6 +93,8 @@ class InvenTreePurchaseOrder extends InvenTreeModel { bool get isOpen => status == PO_STATUS_PENDING || status == PO_STATUS_PLACED; + bool get isPending => status == PO_STATUS_PENDING; + bool get isPlaced => status == PO_STATUS_PLACED; bool get isFailed => status == PO_STATUS_CANCELLED || status == PO_STATUS_LOST || status == PO_STATUS_RETURNED; @@ -132,6 +134,25 @@ class InvenTreePurchaseOrder extends InvenTreeModel { InvenTreeModel createFromJson(Map json) { return InvenTreePurchaseOrder.fromJson(json); } + + /// Mark this order as "placed" / "issued" + Future issueOrder() async { + // Order can only be placed when the order is 'pending' + if (!isPending) { + return; + } + + await api.post("${url}issue/", expectedStatusCode: 201); + } + + /// Mark this order as "cancelled" + Future cancelOrder() async { + if (!isOpen) { + return; + } + + await api.post("${url}cancel/", expectedStatusCode: 201); + } } class InvenTreePOLineItem extends InvenTreeModel { diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index bd306028..e9d81e97 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -156,6 +156,9 @@ "description": "Cancel" }, + "cancelOrder": "Cancel Order", + "@cancelOrder": {}, + "category": "Category", "@category": {}, @@ -479,9 +482,15 @@ "invalidUsernamePassword": "Invalid username / password combination", "@invalidUsernamePassword": {}, + "issue": "Issue", + "@issue": {}, + "issueDate": "Issue Date", "@issueDate": {}, + "issueOrder": "Issue Order", + "@issueOrder": {}, + "itemInLocation": "Item already in location", "@itemInLocation": {}, diff --git a/lib/widget/dialogs.dart b/lib/widget/dialogs.dart index a14a13fb..398dea65 100644 --- a/lib/widget/dialogs.dart +++ b/lib/widget/dialogs.dart @@ -26,7 +26,7 @@ Future confirmationDialog(String title, String text, {Color? color, IconDa title: Text(title, style: TextStyle(color: color)), leading: FaIcon(icon, color: color), ), - content: Text(text), + content: text.isEmpty ? Text(text) : null, actions: [ TextButton( child: Text(_reject), diff --git a/lib/widget/purchase_order_detail.dart b/lib/widget/purchase_order_detail.dart index 220596d4..3b7d1eee 100644 --- a/lib/widget/purchase_order_detail.dart +++ b/lib/widget/purchase_order_detail.dart @@ -1,5 +1,7 @@ import "package:flutter/material.dart"; +import "package:flutter_speed_dial/flutter_speed_dial.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; +import "package:inventree/widget/dialogs.dart"; import "package:one_context/one_context.dart"; import "package:inventree/api.dart"; @@ -62,6 +64,72 @@ class _PurchaseOrderDetailState extends RefreshableState actionButtons(BuildContext context) { + List actions = []; + + if (api.checkPermission("purchase_order", "add")) { + if (order.isPending) { + actions.add( + SpeedDialChild( + child: FaIcon(FontAwesomeIcons.paperPlane, color: Colors.blue), + label: L10().issueOrder, + onTap: () async { + _issueOrder(context); + } + ) + ); + } + + if (order.isOpen) { + actions.add( + SpeedDialChild( + child: FaIcon(FontAwesomeIcons.circleXmark, color: Colors.red), + label: L10().cancelOrder, + onTap: () async { + _cancelOrder(context); + } + ) + ); + } + } + + return actions; + } + + /// Issue this order + Future _issueOrder(BuildContext context) async { + + confirmationDialog( + L10().issueOrder, "", + icon: FontAwesomeIcons.paperPlane, + color: Colors.blue, + acceptText: L10().issue, + onAccept: () async { + await order.issueOrder().then((dynamic) { + refresh(context); + }); + } + ); + } + + /// Cancel this order + Future _cancelOrder(BuildContext context) async { + + confirmationDialog( + L10().cancelOrder, "", + icon: FontAwesomeIcons.circleXmark, + color: Colors.red, + acceptText: L10().cancel, + onAccept: () async { + await order.cancelOrder().then((dynamic) { + print("callback"); + refresh(context); + }); + } + ); + } + @override Future request(BuildContext context) async { await order.reload();