From 3c0bca276d02eec18fd7976438da8d5fbdad3cc2 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 12 May 2024 20:41:02 +1000 Subject: [PATCH] Label printing fix (#489) * Add check for modern label printing interface * Update getLabelTemplates * Fix typo * Refactor / simplify * Revert parameter type * Update version number and release notes * Refactor label printing function - Will require some cleanup in the future - Still needs testing * Fix for modern printing * Typo fix --- assets/release_notes.md | 6 + lib/api.dart | 3 + lib/inventree/model.dart | 3 + lib/inventree/part.dart | 3 + lib/inventree/purchase_order.dart | 3 + lib/inventree/sales_order.dart | 3 + lib/inventree/stock.dart | 6 + lib/labels.dart | 171 ++++++++++++++++--------- lib/settings/about.dart | 2 +- lib/widget/part/part_detail.dart | 14 +- lib/widget/stock/location_display.dart | 14 +- lib/widget/stock/stock_detail.dart | 14 +- pubspec.yaml | 2 +- 13 files changed, 169 insertions(+), 75 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index d41beb30..7404407c 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -1,3 +1,9 @@ +### 0.15.0 - May 2024 +--- + +- Support modern label printing API +- Updated translations + ### 0.14.3 - April 2024 --- diff --git a/lib/api.dart b/lib/api.dart index cf33bd16..67b076f8 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -345,6 +345,9 @@ class InvenTreeAPI { // Does the server support "active" status on Company and SupplierPart API endpoints? bool get supportsCompanyActiveStatus => isConnected() && apiVersion >= 189; + // Does the server support the "modern" (consolidated) label printing API? + bool get supportsModenLabelPrinting => isConnected() && apiVersion >= 197; + // Cached list of plugins (refreshed when we connect to the server) List _plugins = []; diff --git a/lib/inventree/model.dart b/lib/inventree/model.dart index a28e5d6a..747dc26f 100644 --- a/lib/inventree/model.dart +++ b/lib/inventree/model.dart @@ -64,6 +64,9 @@ class InvenTreeModel { // Note: If the WEB_URL is the same (except for /api/) as URL then just leave blank String get WEB_URL => ""; + // Return the "model type" of this model + String get MODEL_TYPE => ""; + // Helper function to set a value in the JSON data void setValue(String key, dynamic value) { jsondata[key] = value; diff --git a/lib/inventree/part.dart b/lib/inventree/part.dart index e44494e1..61e1c274 100644 --- a/lib/inventree/part.dart +++ b/lib/inventree/part.dart @@ -196,6 +196,9 @@ class InvenTreePart extends InvenTreeModel { @override String get URL => "part/"; + @override + String get MODEL_TYPE => "part"; + @override List get rolesRequired => ["part"]; diff --git a/lib/inventree/purchase_order.dart b/lib/inventree/purchase_order.dart index 80708948..fab36729 100644 --- a/lib/inventree/purchase_order.dart +++ b/lib/inventree/purchase_order.dart @@ -20,6 +20,9 @@ class InvenTreePurchaseOrder extends InvenTreeOrder { @override String get URL => "order/po/"; + @override + String get MODEL_TYPE => "purchaseorder"; + @override List get rolesRequired => ["purchase_order"]; diff --git a/lib/inventree/sales_order.dart b/lib/inventree/sales_order.dart index b65ad611..b23d8ebf 100644 --- a/lib/inventree/sales_order.dart +++ b/lib/inventree/sales_order.dart @@ -23,6 +23,9 @@ class InvenTreeSalesOrder extends InvenTreeOrder { @override String get URL => "order/so/"; + @override + String get MODEL_TYPE => "salesorder"; + @override List get rolesRequired => ["sales_order"]; diff --git a/lib/inventree/stock.dart b/lib/inventree/stock.dart index ba4e8717..b9c40369 100644 --- a/lib/inventree/stock.dart +++ b/lib/inventree/stock.dart @@ -142,6 +142,9 @@ class InvenTreeStockItem extends InvenTreeModel { @override String get URL => "stock/"; + @override + String get MODEL_TYPE => "stockitem"; + @override List get rolesRequired => ["stock"]; @@ -649,6 +652,9 @@ class InvenTreeStockLocation extends InvenTreeModel { @override String get URL => "stock/location/"; + @override + String get MODEL_TYPE => "stocklocation"; + @override List get rolesRequired => ["stock_location"]; diff --git a/lib/labels.dart b/lib/labels.dart index c43ad30d..c8fadd09 100644 --- a/lib/labels.dart +++ b/lib/labels.dart @@ -6,50 +6,18 @@ import "package:inventree/api_form.dart"; import "package:inventree/l10.dart"; import "package:inventree/widget/snacks.dart"; -/* - * Discover which label templates are available for a given item - */ -Future>> getLabelTemplates( - String labelType, - Map data, -) async { - - if (!InvenTreeAPI().isConnected() || !InvenTreeAPI().supportsMixin("labels")) { - return []; - } - - // Filter by active plugins - data["enabled"] = "true"; - - List> labels = []; - - await InvenTreeAPI().get( - "/label/${labelType}/", - params: data, - ).then((APIResponse response) { - if (response.isValid() && response.statusCode == 200) { - for (var label in response.resultsList()) { - if (label is Map) { - labels.add(label); - } - } - } - }); - - return labels; -} - - /* * Select a particular label, from a provided list of options, * and print against the selected instances. + * */ Future selectAndPrintLabel( - BuildContext context, - List> labels, - String labelType, - String labelQuery, - ) async { + BuildContext context, + List> labels, + int instanceId, + String labelType, + String labelQuery, +) async { if (!InvenTreeAPI().isConnected()) { return; @@ -91,7 +59,7 @@ Future selectAndPrintLabel( for (var plugin in plugins) { plugin_options.add({ "display_name": plugin.humanName, - "value": plugin.key + "value": InvenTreeAPI().supportsModenLabelPrinting ? plugin.pk : plugin.key }); } @@ -124,38 +92,113 @@ Future selectAndPrintLabel( icon: FontAwesomeIcons.print, onSuccess: (Map data) async { int labelId = (data["label"] ?? -1) as int; - String pluginKey = (data["plugin"] ?? "") as String; + var pluginKey = data["plugin"]; - if (labelId != -1 && pluginKey.isNotEmpty) { - String url = "/label/${labelType}/${labelId}/print/?${labelQuery}&plugin=${pluginKey}"; + bool result = false; + + if (labelId != -1 && pluginKey != null) { showLoadingOverlay(context); - InvenTreeAPI().get(url).then((APIResponse response) { - hideLoadingOverlay(); - if (response.isValid() && response.statusCode == 200) { + if (InvenTreeAPI().supportsModenLabelPrinting) { + + // Modern label printing API uses a POST request to a single API endpoint. + await InvenTreeAPI().post( + "/label/print/", + body: { + "plugin": pluginKey, + "template": labelId, + "items": [instanceId] + } + ).then((APIResponse response) { + hideLoadingOverlay(); - var data = response.asMap(); + if (response.isValid() && response.statusCode >= 200 && + response.statusCode <= 201) { + var data = response.asMap(); - if (data.containsKey("file")) { - var label_file = (data["file"] ?? "") as String; + if (data.containsKey("output")) { + var label_file = (data["output"] ?? "") as String; - // Attempt to open remote file - InvenTreeAPI().downloadFile(label_file); - } else { - showSnackIcon( - L10().printLabelSuccess, - success: true - ); + // Attempt to open generated file + InvenTreeAPI().downloadFile(label_file); + result = true; + } } - } else { - showSnackIcon( - L10().printLabelFailure, - success: false, - ); - } }); + } else { + // Legacy label printing API + // Uses a GET request to a specially formed URL which depends on the parameters + String url = "/label/${labelType}/${labelId}/print/?${labelQuery}&plugin=${pluginKey}"; + await InvenTreeAPI().get(url).then((APIResponse response) { + hideLoadingOverlay(); + if (response.isValid() && response.statusCode == 200) { + var data = response.asMap(); + if (data.containsKey("file")) { + var label_file = (data["file"] ?? "") as String; + + // Attempt to open remote file + InvenTreeAPI().downloadFile(label_file); + result = true; + } + } + }); } - }, - ); + + if (result) { + showSnackIcon( + L10().printLabelSuccess, + success: true + ); + } else { + showSnackIcon( + L10().printLabelFailure, + success: false, + ); + } + } + }); +} + + +/* + * Discover which label templates are available for a given item + */ +Future>> getLabelTemplates( + String labelType, + Map data, +) async { + + if (!InvenTreeAPI().isConnected() || !InvenTreeAPI().supportsMixin("labels")) { + return []; + } + + // Filter by active plugins + data["enabled"] = "true"; + + String url = "/label/template/"; + + if (InvenTreeAPI().supportsModenLabelPrinting) { + data["model_type"] = labelType; + } else { + // Legacy label printing API endpoint + url = "/label/${labelType}/"; + } + + List> labels = []; + + await InvenTreeAPI().get( + url, + params: data, + ).then((APIResponse response) { + if (response.isValid() && response.statusCode == 200) { + for (var label in response.resultsList()) { + if (label is Map) { + labels.add(label); + } + } + } + }); + + return labels; } \ No newline at end of file diff --git a/lib/settings/about.dart b/lib/settings/about.dart index 3f02f6fc..1bec3763 100644 --- a/lib/settings/about.dart +++ b/lib/settings/about.dart @@ -194,7 +194,7 @@ class InvenTreeAboutWidget extends StatelessWidget { tiles.add( ListTile( title: Text(L10().documentation), - subtitle: Text("https://docs.inventree.org"), + subtitle: Text("https://docs.inventree.org/app"), leading: FaIcon(FontAwesomeIcons.book, color: COLOR_ACTION), onTap: () { _openDocs(); diff --git a/lib/widget/part/part_detail.dart b/lib/widget/part/part_detail.dart index 8009ee68..f9693a1f 100644 --- a/lib/widget/part/part_detail.dart +++ b/lib/widget/part/part_detail.dart @@ -129,6 +129,7 @@ class _PartDisplayState extends RefreshableState { selectAndPrintLabel( context, labels, + widget.part.pk, "part", "part=${widget.part.pk}" ); @@ -248,9 +249,16 @@ class _PartDisplayState extends RefreshableState { allowLabelPrinting &= api.supportsMixin("labels"); if (allowLabelPrinting) { - _labels = await getLabelTemplates("part", { - "part": widget.part.pk.toString(), - }); + + String model_type = api.supportsModenLabelPrinting ? InvenTreePart().MODEL_TYPE : "part"; + String item_key = api.supportsModenLabelPrinting ? "items" : "part"; + + _labels = await getLabelTemplates( + model_type, + { + item_key: widget.part.pk.toString() + } + ); } if (mounted) { diff --git a/lib/widget/stock/location_display.dart b/lib/widget/stock/location_display.dart index ac73c0d2..dcb13720 100644 --- a/lib/widget/stock/location_display.dart +++ b/lib/widget/stock/location_display.dart @@ -193,6 +193,7 @@ class _LocationDisplayState extends RefreshableState { selectAndPrintLabel( context, labels, + widget.location!.pk, "location", "location=${widget.location!.pk}" ); @@ -247,9 +248,16 @@ class _LocationDisplayState extends RefreshableState { if (allowLabelPrinting) { if (widget.location != null) { - _labels = await getLabelTemplates("location", { - "location": widget.location!.pk.toString() - }); + + String model_type = api.supportsModenLabelPrinting ? InvenTreeStockLocation().MODEL_TYPE : "location"; + String item_key = api.supportsModenLabelPrinting ? "items" : "location"; + + _labels = await getLabelTemplates( + model_type, + { + item_key: widget.location!.pk.toString() + } + ); } } diff --git a/lib/widget/stock/stock_detail.dart b/lib/widget/stock/stock_detail.dart index ee21b444..f44f0dd0 100644 --- a/lib/widget/stock/stock_detail.dart +++ b/lib/widget/stock/stock_detail.dart @@ -138,6 +138,7 @@ class _StockItemDisplayState extends RefreshableState { selectAndPrintLabel( context, labels, + widget.item.pk, "stock", "item=${widget.item.pk}" ); @@ -264,10 +265,17 @@ class _StockItemDisplayState extends RefreshableState { // Request information on labels available for this stock item if (allowLabelPrinting) { + + String model_type = api.supportsModenLabelPrinting ? InvenTreeStockLocation().MODEL_TYPE : "stock"; + String item_key = api.supportsModenLabelPrinting ? "items" : "item"; + // Clear the existing labels list - _labels = await getLabelTemplates("stock", { - "item": widget.item.pk.toString() - }); + _labels = await getLabelTemplates( + model_type, + { + item_key: widget.item.pk.toString() + } + ); } if (mounted) { diff --git a/pubspec.yaml b/pubspec.yaml index 49e1f7cb..0589baab 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: inventree description: InvenTree stock management -version: 0.14.3+81 +version: 0.15.0+82 environment: sdk: ">=2.19.5 <3.13.0"