From 03c6de8255aed5eb5d01aa7d01b244b1565176c6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 10 May 2022 00:15:20 +1000 Subject: [PATCH 1/5] Stock location can now be identified via the app --- assets/release_notes.md | 1 + lib/l10n/app_en.arb | 15 +++++ lib/widget/location_display.dart | 97 +++++++++++++++++++++++++++++--- lib/widget/stock_detail.dart | 4 +- 4 files changed, 107 insertions(+), 10 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index c7dfde30..38487ba7 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -7,6 +7,7 @@ - Refactor home screen display - Display unread notifications on home screen - Fixes duplicated display of units when showing stock quantity +- Adds ability to locate / identify stock items or locations (requires server plugin) - Improve rendering of home screen when server is not connected - Adds ability to load global and user settings from the server diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 56371b44..7640b154 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -416,6 +416,9 @@ "keywords": "Keywords", "@keywords": {}, + "labelTemplate": "Label Template", + "@labelTemplate": {}, + "lastStocktake": "Last Stocktake", "@lastStocktake": {}, @@ -428,6 +431,12 @@ "lineItems": "Line Items", "@lineItems": {}, + "locateItem": "Locate stock item", + "@locateItem": {}, + + "locateLocation": "Locate stock location", + "@locateLocation": {}, + "locationCreate": "New Location", "@locationCreate": {}, @@ -575,6 +584,12 @@ "printLabel": "Print Label", "@printLabel": {}, + "plugin": "Plugin", + "@plugin": {}, + + "pluginPrinter": "Printer", + "@pluginPrinter": {}, + "printLabelFailure": "Label printing failed", "@printLabelFailure": {}, diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index cdf1856e..17ca0103 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -3,6 +3,7 @@ import "package:flutter/material.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:inventree/api.dart"; +import 'package:inventree/api_form.dart'; import "package:inventree/app_colors.dart"; import "package:inventree/barcode.dart"; import "package:inventree/inventree/stock.dart"; @@ -61,19 +62,99 @@ class _LocationDisplayState extends RefreshableState { ); */ - if ((location != null) && (InvenTreeAPI().checkPermission("stock_location", "change"))) { - actions.add( - IconButton( - icon: FaIcon(FontAwesomeIcons.edit), - tooltip: L10().edit, - onPressed: () { _editLocationDialog(context); }, - ) - ); + if (location != null) { + + // Add "locate" button + if (InvenTreeAPI().supportsMixin("locate")) { + actions.add( + IconButton( + icon: FaIcon(FontAwesomeIcons.searchLocation), + tooltip: L10().locateLocation, + onPressed: () async { + _locateStockLocation(context); + }, + ) + ); + } + + // Add "edit" button + if (InvenTreeAPI().checkPermission("stock_location", "change")) { + actions.add( + IconButton( + icon: FaIcon(FontAwesomeIcons.edit), + tooltip: L10().edit, + onPressed: () { _editLocationDialog(context); }, + ) + ); + } } return actions; } + Future _locateStockLocation(BuildContext context) async { + + final _loc = location; + + if (_loc != null) { + + final plugins = InvenTreeAPI().getPlugins(mixin: "locate"); + + if (plugins.isEmpty) { + // TODO: Error message here + return; + } + + String plugin_name = ""; + + if (plugins.length == 1) { + plugin_name = plugins.first.key; + } else { + // User selects which plugin to use + List> plugin_options = []; + + for (var plugin in plugins) { + plugin_options.add({ + "display_name": plugin.humanName, + "value": plugin.key, + }); + } + + Map fields = { + "plugin": { + "label": L10().plugin, + "type": "choice", + "value": plugins.first.key, + "choices": plugin_options, + "required": true, + } + }; + + await launchApiForm( + context, + L10().locateLocation, + "", + fields, + icon: FontAwesomeIcons.searchLocation, + onSuccess: (Map data) async { + plugin_name = (data["plugin"] ?? "") as String; + } + ); + } + + print("plugin: ${plugin_name}"); + + InvenTreeAPI().post( + "/api/locate/", + body: { + "plugin": plugin_name, + "location": "${_loc.pk}", + }, + expectedStatusCode: 200, + ); + } + } + void _editLocationDialog(BuildContext context) { final _loc = location; diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 3b400b26..3e2f4709 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -217,14 +217,14 @@ class _StockItemDisplayState extends RefreshableState { Map fields = { "label": { - "label": "Label Template", + "label": L10().labelTemplate, "type": "choice", "value": initial_label, "choices": label_options, "required": true, }, "plugin": { - "label": "Printer", + "label": L10().pluginPrinter, "type": "choice", "value": initial_plugin, "choices": plugin_options, From 5ba887d59b430ab13463dc17f2f4954faa4fed69 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 10 May 2022 00:28:49 +1000 Subject: [PATCH 2/5] Refactor locate function --- lib/api.dart | 78 ++++++++++++++++++++++++++++++++ lib/l10n/app_en.arb | 3 ++ lib/widget/location_display.dart | 62 +++---------------------- 3 files changed, 88 insertions(+), 55 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index fc900c1d..a799235b 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -22,6 +22,8 @@ import "package:inventree/user_profile.dart"; import "package:inventree/widget/snacks.dart"; import "package:path_provider/path_provider.dart"; +import 'api_form.dart'; + /* * Class representing an API response from the server @@ -1200,4 +1202,80 @@ class InvenTreeAPI { } } + /** + * Send a request to the server to locate / identify either a StockItem or StockLocation + */ + Future locateItemOrLocation(BuildContext context, {int? item, int? location}) async { + + var plugins = getPlugins(mixin: "locate"); + + print("locateItemOrLocation"); + + if (plugins.isEmpty) { + // TODO: Error message + return; + } + + String plugin_name = ""; + + if (plugins.length == 1) { + plugin_name = plugins.first.key; + } else { + // User selects which plugin to use + List> plugin_options = []; + + for (var plugin in plugins) { + plugin_options.add({ + "display_name": plugin.humanName, + "value": plugin.key, + }); + } + + Map fields = { + "plugin": { + "label": L10().plugin, + "type": "choice", + "value": plugins.first.key, + "choices": plugin_options, + "required": true, + } + }; + + await launchApiForm( + context, + L10().locateLocation, + "", + fields, + icon: FontAwesomeIcons.searchLocation, + onSuccess: (Map data) async { + plugin_name = (data["plugin"] ?? "") as String; + } + ); + } + + Map body = { + "plugin": plugin_name, + }; + + if (item != null) { + body["item"] = item.toString(); + } + + if (location != null) { + body["location"] = location.toString(); + } + + post( + "/api/locate/", + body: body, + expectedStatusCode: 200, + ).then((APIResponse response) { + if (response.successful()) { + showSnackIcon( + L10().requestSuccessful, + success: true, + ); + } + }); + } } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7640b154..02cb93b5 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -707,6 +707,9 @@ "request": "Request", "@request": {}, + "requestSuccessful": "Request successful", + "@requestSuccessful": {}, + "requestingData": "Requesting Data", "@requestingData": {}, diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index 17ca0103..4b38bf4a 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -92,69 +92,21 @@ class _LocationDisplayState extends RefreshableState { return actions; } + /* + * Request identification of this location + */ Future _locateStockLocation(BuildContext context) async { final _loc = location; if (_loc != null) { - - final plugins = InvenTreeAPI().getPlugins(mixin: "locate"); - - if (plugins.isEmpty) { - // TODO: Error message here - return; - } - - String plugin_name = ""; - - if (plugins.length == 1) { - plugin_name = plugins.first.key; - } else { - // User selects which plugin to use - List> plugin_options = []; - - for (var plugin in plugins) { - plugin_options.add({ - "display_name": plugin.humanName, - "value": plugin.key, - }); - } - - Map fields = { - "plugin": { - "label": L10().plugin, - "type": "choice", - "value": plugins.first.key, - "choices": plugin_options, - "required": true, - } - }; - - await launchApiForm( - context, - L10().locateLocation, - "", - fields, - icon: FontAwesomeIcons.searchLocation, - onSuccess: (Map data) async { - plugin_name = (data["plugin"] ?? "") as String; - } - ); - } - - print("plugin: ${plugin_name}"); - - InvenTreeAPI().post( - "/api/locate/", - body: { - "plugin": plugin_name, - "location": "${_loc.pk}", - }, - expectedStatusCode: 200, - ); + InvenTreeAPI().locateItemOrLocation(context, location: _loc.pk); } } + /* + * Launch a dialog form to edit this stock location + */ void _editLocationDialog(BuildContext context) { final _loc = location; From 366c732668344b5a039b57b2e75a3cf2d5285d68 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 10 May 2022 00:30:28 +1000 Subject: [PATCH 3/5] Add support for locating a stock item --- lib/widget/stock_detail.dart | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart index 3e2f4709..b0b3cdab 100644 --- a/lib/widget/stock_detail.dart +++ b/lib/widget/stock_detail.dart @@ -67,6 +67,18 @@ class _StockItemDisplayState extends RefreshableState { ); } + if (InvenTreeAPI().supportsMixin("locate")) { + actions.add( + IconButton( + icon: FaIcon(FontAwesomeIcons.searchLocation), + tooltip: L10().locateItem, + onPressed: () async { + InvenTreeAPI().locateItemOrLocation(context, item: item.pk); + }, + ) + ); + } + if (InvenTreeAPI().checkPermission("stock", "change")) { actions.add( IconButton( From 9f6269375feabe8376f27d02e41a1b569278c1af Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 10 May 2022 01:19:38 +1000 Subject: [PATCH 4/5] linting --- lib/api.dart | 4 ++-- lib/widget/location_display.dart | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/api.dart b/lib/api.dart index a799235b..51a414c3 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -22,7 +22,7 @@ import "package:inventree/user_profile.dart"; import "package:inventree/widget/snacks.dart"; import "package:path_provider/path_provider.dart"; -import 'api_form.dart'; +import "package:inventree/api_form.dart"; /* @@ -1202,7 +1202,7 @@ class InvenTreeAPI { } } - /** + /* * Send a request to the server to locate / identify either a StockItem or StockLocation */ Future locateItemOrLocation(BuildContext context, {int? item, int? location}) async { diff --git a/lib/widget/location_display.dart b/lib/widget/location_display.dart index 4b38bf4a..c1647eeb 100644 --- a/lib/widget/location_display.dart +++ b/lib/widget/location_display.dart @@ -3,7 +3,6 @@ import "package:flutter/material.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:inventree/api.dart"; -import 'package:inventree/api_form.dart'; import "package:inventree/app_colors.dart"; import "package:inventree/barcode.dart"; import "package:inventree/inventree/stock.dart"; From bc53dafaba4653f609b8091bb4bc9b026159d027 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 10 May 2022 08:12:53 +1000 Subject: [PATCH 5/5] Add plugin support status to server information screen --- lib/l10n/app_en.arb | 6 ++++++ lib/settings/about.dart | 12 ++++++++++++ 2 files changed, 18 insertions(+) diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 02cb93b5..3b13d9cf 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -590,6 +590,12 @@ "pluginPrinter": "Printer", "@pluginPrinter": {}, + "pluginSupport": "Plugin Support Enabled", + "@pluginSupport": {}, + + "pluginSupportDetail": "The server supports custom plugins", + "@pluginSupportDetail": {}, + "printLabelFailure": "Label printing failed", "@printLabelFailure": {}, diff --git a/lib/settings/about.dart b/lib/settings/about.dart index f62179cd..7ba8c5dc 100644 --- a/lib/settings/about.dart +++ b/lib/settings/about.dart @@ -102,6 +102,18 @@ class InvenTreeAboutWidget extends StatelessWidget { leading: FaIcon(FontAwesomeIcons.server), ) ); + + // Display extra tile if the server supports plugins + if (InvenTreeAPI().pluginsEnabled()) { + tiles.add( + ListTile( + title: Text(L10().pluginSupport), + subtitle: Text(L10().pluginSupportDetail), + leading: FaIcon(FontAwesomeIcons.plug), + ) + ); + } + } else { tiles.add( ListTile(