mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-31 13:25:40 +00:00 
			
		
		
		
	Label print updates (#399)
* Allow download of printed label * Add setting for controlling label printing * Control display of label printing via setting * Refactor label printing functionality - Move to helpers.dart - Will be used for other label types also * Factor out request for label templates * Add label printing support for part * Support label printing for stock location * update release notes
This commit is contained in:
		| @@ -9,6 +9,7 @@ | ||||
|  | ||||
| import "dart:io"; | ||||
| import "package:currency_formatter/currency_formatter.dart"; | ||||
|  | ||||
| import "package:one_context/one_context.dart"; | ||||
| import "package:url_launcher/url_launcher.dart"; | ||||
| import "package:audioplayers/audioplayers.dart"; | ||||
| @@ -132,4 +133,5 @@ String renderCurrency(double? amount, String currency, {int decimals = 2}) { | ||||
|   } | ||||
|  | ||||
|   return value; | ||||
| } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -533,6 +533,12 @@ | ||||
|   "keywords": "Keywords", | ||||
|   "@keywords": {}, | ||||
|  | ||||
|   "labelPrinting": "Label Printing", | ||||
|   "@labelPrinting": {}, | ||||
|  | ||||
|   "labelPrintingDetail": "Enable label printing", | ||||
|   "@labelPrintingDetail": {}, | ||||
|  | ||||
|   "labelTemplate": "Label Template", | ||||
|   "@labelTemplate": {}, | ||||
|  | ||||
|   | ||||
							
								
								
									
										155
									
								
								lib/labels.dart
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										155
									
								
								lib/labels.dart
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,155 @@ | ||||
| import "package:flutter/cupertino.dart"; | ||||
| import "package:font_awesome_flutter/font_awesome_flutter.dart"; | ||||
| import "package:inventree/api.dart"; | ||||
| import "package:inventree/widget/progress.dart"; | ||||
| 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<List<Map<String, dynamic>>> getLabelTemplates( | ||||
|   String labelType, | ||||
|   Map<String, String> data, | ||||
| ) async { | ||||
|  | ||||
|   if (!InvenTreeAPI().isConnected() || !InvenTreeAPI().supportsMixin("labels")) { | ||||
|     return []; | ||||
|   } | ||||
|  | ||||
|   // Filter by active plugins | ||||
|   data["enabled"] = "true"; | ||||
|  | ||||
|   List<Map<String, dynamic>> 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<String, dynamic>) { | ||||
|           labels.add(label); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   }); | ||||
|  | ||||
|   return labels; | ||||
| } | ||||
|  | ||||
|  | ||||
| /* | ||||
|  * Select a particular label, from a provided list of options, | ||||
|  * and print against the selected instances. | ||||
|  */ | ||||
| Future<void> selectAndPrintLabel( | ||||
|     BuildContext context, | ||||
|     List<Map<String, dynamic>> labels, | ||||
|     String labelType, | ||||
|     String labelQuery, | ||||
|     ) async { | ||||
|  | ||||
|   if (!InvenTreeAPI().isConnected()) { | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   // Find a list of available plugins which support label printing | ||||
|   var plugins = InvenTreeAPI().getPlugins(mixin: "labels"); | ||||
|  | ||||
|   dynamic initial_label; | ||||
|   dynamic initial_plugin; | ||||
|  | ||||
|   List<Map<String, dynamic>> label_options = []; | ||||
|   List<Map<String, dynamic>> plugin_options = []; | ||||
|  | ||||
|   // Construct list of available label templates | ||||
|   for (var label in labels) { | ||||
|     String display_name = (label["description"] ?? "").toString(); | ||||
|     int pk = (label["pk"] ?? -1) as int; | ||||
|  | ||||
|     if (display_name.isNotEmpty && pk > 0) { | ||||
|       label_options.add({ | ||||
|         "display_name": display_name, | ||||
|         "value": pk, | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if (label_options.length == 1) { | ||||
|     initial_label = label_options.first["value"]; | ||||
|   } | ||||
|  | ||||
|   // Construct list of available plugins | ||||
|   for (var plugin in plugins) { | ||||
|     plugin_options.add({ | ||||
|       "display_name": plugin.humanName, | ||||
|       "value": plugin.key | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   if (plugin_options.length == 1) { | ||||
|     initial_plugin = plugin_options.first["value"]; | ||||
|   } | ||||
|  | ||||
|   Map<String, dynamic> fields = { | ||||
|     "label": { | ||||
|       "label": L10().labelTemplate, | ||||
|       "type": "choice", | ||||
|       "value": initial_label, | ||||
|       "choices": label_options, | ||||
|       "required": true, | ||||
|     }, | ||||
|     "plugin": { | ||||
|       "label": L10().pluginPrinter, | ||||
|       "type": "choice", | ||||
|       "value": initial_plugin, | ||||
|       "choices": plugin_options, | ||||
|       "required": true, | ||||
|     } | ||||
|   }; | ||||
|  | ||||
|   launchApiForm( | ||||
|     context, | ||||
|     L10().printLabel, | ||||
|     "", | ||||
|     fields, | ||||
|     icon: FontAwesomeIcons.print, | ||||
|     onSuccess: (Map<String, dynamic> data) async { | ||||
|       int labelId = (data["label"] ?? -1) as int; | ||||
|       String pluginKey = (data["plugin"] ?? "") as String; | ||||
|  | ||||
|       if (labelId != -1 && pluginKey.isNotEmpty) { | ||||
|         String url = "/label/${labelType}/${labelId}/print/?${labelQuery}&plugin=${pluginKey}"; | ||||
|  | ||||
|         showLoadingOverlay(context); | ||||
|  | ||||
|         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); | ||||
|             } else { | ||||
|               showSnackIcon( | ||||
|                   L10().printLabelSuccess, | ||||
|                   success: true | ||||
|               ); | ||||
|             } | ||||
|           } else { | ||||
|             showSnackIcon( | ||||
|               L10().printLabelFailure, | ||||
|               success: false, | ||||
|             ); | ||||
|           } | ||||
|         }); | ||||
|       } | ||||
|     }, | ||||
|   ); | ||||
| } | ||||
| @@ -25,6 +25,8 @@ const int SCREEN_ORIENTATION_LANDSCAPE = 2; | ||||
| const String INV_SOUNDS_BARCODE = "barcodeSounds"; | ||||
| const String INV_SOUNDS_SERVER = "serverSounds"; | ||||
|  | ||||
| const String INV_ENABLE_LABEL_PRINTING = "enableLabelPrinting"; | ||||
|  | ||||
| // Part settings | ||||
| const String INV_PART_SHOW_PARAMETERS = "partShowParameters"; | ||||
| const String INV_PART_SHOW_BOM = "partShowBom"; | ||||
|   | ||||
| @@ -1,18 +1,18 @@ | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:one_context/one_context.dart"; | ||||
|  | ||||
| import "package:adaptive_theme/adaptive_theme.dart"; | ||||
| import "package:font_awesome_flutter/font_awesome_flutter.dart"; | ||||
| import "package:flutter_localized_locales/flutter_localized_locales.dart"; | ||||
| import "package:inventree/app_colors.dart"; | ||||
| import "package:inventree/widget/dialogs.dart"; | ||||
| import "package:one_context/one_context.dart"; | ||||
|  | ||||
| import "package:inventree/app_colors.dart"; | ||||
| import "package:inventree/api_form.dart"; | ||||
| import "package:inventree/l10.dart"; | ||||
| import "package:inventree/l10n/supported_locales.dart"; | ||||
| import "package:inventree/main.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
|  | ||||
| import "package:inventree/widget/dialogs.dart"; | ||||
| import "package:inventree/widget/progress.dart"; | ||||
|  | ||||
|  | ||||
| @@ -33,7 +33,7 @@ class _InvenTreeAppSettingsState extends State<InvenTreeAppSettingsWidget> { | ||||
|  | ||||
|   bool reportErrors = true; | ||||
|   bool strictHttps = false; | ||||
|  | ||||
|   bool enableLabelPrinting = true; | ||||
|   bool darkMode = false; | ||||
|  | ||||
|   int screenOrientation = SCREEN_ORIENTATION_SYSTEM; | ||||
| @@ -56,6 +56,7 @@ class _InvenTreeAppSettingsState extends State<InvenTreeAppSettingsWidget> { | ||||
|     reportErrors = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool; | ||||
|     strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; | ||||
|     screenOrientation = await InvenTreeSettingsManager().getValue(INV_SCREEN_ORIENTATION, SCREEN_ORIENTATION_SYSTEM) as int; | ||||
|     enableLabelPrinting = await InvenTreeSettingsManager().getValue(INV_ENABLE_LABEL_PRINTING, true) as bool; | ||||
|  | ||||
|     darkMode = AdaptiveTheme.of(context).mode.isDark; | ||||
|  | ||||
| @@ -218,6 +219,20 @@ class _InvenTreeAppSettingsState extends State<InvenTreeAppSettingsWidget> { | ||||
|                 ); | ||||
|               }, | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text(L10().labelPrinting), | ||||
|               subtitle: Text(L10().labelPrintingDetail), | ||||
|               leading: FaIcon(FontAwesomeIcons.print), | ||||
|               trailing: Switch( | ||||
|                 value: enableLabelPrinting, | ||||
|                 onChanged: (bool value) { | ||||
|                   InvenTreeSettingsManager().setValue(INV_ENABLE_LABEL_PRINTING, value); | ||||
|                   setState(() { | ||||
|                     enableLabelPrinting = value; | ||||
|                   }); | ||||
|                 } | ||||
|               ), | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text(L10().strictHttps), | ||||
|               subtitle: Text(L10().strictHttpsDetails), | ||||
|   | ||||
| @@ -13,8 +13,8 @@ import "package:inventree/settings/login.dart"; | ||||
| import "package:inventree/settings/part_settings.dart"; | ||||
|  | ||||
|  | ||||
| // InvenTree settings view | ||||
| class InvenTreeSettingsWidget extends StatefulWidget { | ||||
|   // InvenTree settings view | ||||
|  | ||||
|   @override | ||||
|   _InvenTreeSettingsState createState() => _InvenTreeSettingsState(); | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import "package:inventree/barcode/barcode.dart"; | ||||
| import "package:inventree/l10.dart"; | ||||
|  | ||||
| import "package:inventree/inventree/stock.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
|  | ||||
| import "package:inventree/widget/location_list.dart"; | ||||
| import "package:inventree/widget/progress.dart"; | ||||
| @@ -15,6 +16,7 @@ import "package:inventree/widget/refreshable_state.dart"; | ||||
| import "package:inventree/widget/snacks.dart"; | ||||
| import "package:inventree/widget/stock_detail.dart"; | ||||
| import "package:inventree/widget/stock_list.dart"; | ||||
| import "package:inventree/labels.dart"; | ||||
|  | ||||
|  | ||||
| /* | ||||
| @@ -38,6 +40,10 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|  | ||||
|   final InvenTreeStockLocation? location; | ||||
|  | ||||
|   bool allowLabelPrinting = true; | ||||
|  | ||||
|   List<Map<String, dynamic>> labels = []; | ||||
|  | ||||
|   @override | ||||
|   String getAppBarTitle() { | ||||
|     return L10().stockLocation; | ||||
| @@ -163,6 +169,23 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     if (widget.location != null && allowLabelPrinting && labels.isNotEmpty) { | ||||
|       actions.add( | ||||
|           SpeedDialChild( | ||||
|               child: FaIcon(FontAwesomeIcons.print), | ||||
|               label: L10().printLabel, | ||||
|               onTap: () async { | ||||
|                 selectAndPrintLabel( | ||||
|                     context, | ||||
|                     labels, | ||||
|                     "location", | ||||
|                     "location=${widget.location!.pk}" | ||||
|                 ); | ||||
|               } | ||||
|           ) | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     return actions; | ||||
|   } | ||||
|  | ||||
| @@ -202,6 +225,19 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> { | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     allowLabelPrinting = await InvenTreeSettingsManager().getBool(INV_ENABLE_LABEL_PRINTING, true); | ||||
|     allowLabelPrinting &= api.getPlugins(mixin: "labels").isNotEmpty; | ||||
|  | ||||
|     if (allowLabelPrinting) { | ||||
|       labels.clear(); | ||||
|  | ||||
|       if (widget.location != null) { | ||||
|         labels = await getLabelTemplates("location", { | ||||
|           "location": widget.location!.pk.toString() | ||||
|         }); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     if (mounted) { | ||||
|       setState(() {}); | ||||
|     } | ||||
|   | ||||
| @@ -11,6 +11,7 @@ import "package:inventree/helpers.dart"; | ||||
| import "package:inventree/inventree/bom.dart"; | ||||
| import "package:inventree/inventree/part.dart"; | ||||
| import "package:inventree/inventree/stock.dart"; | ||||
| import "package:inventree/labels.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
|  | ||||
| import "package:inventree/widget/attachment_widget.dart"; | ||||
| @@ -54,17 +55,16 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> { | ||||
|   int parameterCount = 0; | ||||
|  | ||||
|   bool showParameters = false; | ||||
|  | ||||
|   bool showBom = false; | ||||
|   bool allowLabelPrinting = true; | ||||
|  | ||||
|   int attachmentCount = 0; | ||||
|  | ||||
|   int bomCount = 0; | ||||
|  | ||||
|   int usedInCount = 0; | ||||
|  | ||||
|   int variantCount = 0; | ||||
|  | ||||
|   List<Map<String, dynamic>> labels = []; | ||||
|  | ||||
|   @override | ||||
|   String getAppBarTitle() => L10().partDetails; | ||||
|  | ||||
| @@ -110,12 +110,29 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> { | ||||
|     List<SpeedDialChild> actions = []; | ||||
|  | ||||
|     if (InvenTreeStockItem().canCreate) { | ||||
|       actions.add( | ||||
|           SpeedDialChild( | ||||
|               child: FaIcon(FontAwesomeIcons.box), | ||||
|               label: L10().stockItemCreate, | ||||
|               onTap: () { | ||||
|                 _newStockItem(context); | ||||
|               } | ||||
|           ) | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     if (allowLabelPrinting && labels.isNotEmpty) { | ||||
|       actions.add( | ||||
|         SpeedDialChild( | ||||
|           child: FaIcon(FontAwesomeIcons.box), | ||||
|           label: L10().stockItemCreate, | ||||
|           onTap: () { | ||||
|             _newStockItem(context); | ||||
|           child: FaIcon(FontAwesomeIcons.print), | ||||
|           label: L10().printLabel, | ||||
|           onTap: () async { | ||||
|             selectAndPrintLabel( | ||||
|               context, | ||||
|               labels, | ||||
|               "part", | ||||
|               "part=${widget.part.pk}" | ||||
|             ); | ||||
|           } | ||||
|         ) | ||||
|       ); | ||||
| @@ -226,6 +243,16 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> { | ||||
|         }); | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     allowLabelPrinting = await InvenTreeSettingsManager().getBool(INV_ENABLE_LABEL_PRINTING, true); | ||||
|     allowLabelPrinting &= api.getPlugins(mixin: "labels").isNotEmpty; | ||||
|  | ||||
|     if (allowLabelPrinting) { | ||||
|       labels.clear(); | ||||
|       labels = await getLabelTemplates("part", { | ||||
|         "part": widget.part.pk.toString(), | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   void _editPartDialog(BuildContext context) { | ||||
|   | ||||
| @@ -9,6 +9,7 @@ import "package:inventree/helpers.dart"; | ||||
| import "package:inventree/l10.dart"; | ||||
| import "package:inventree/api.dart"; | ||||
| import "package:inventree/api_form.dart"; | ||||
| import "package:inventree/labels.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
|  | ||||
| import "package:inventree/inventree/company.dart"; | ||||
| @@ -127,13 +128,18 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> { | ||||
|       ); | ||||
|     } | ||||
|  | ||||
|     if (labels.isNotEmpty) { | ||||
|     if (allowLabelPrinting && labels.isNotEmpty) { | ||||
|       actions.add( | ||||
|         SpeedDialChild( | ||||
|           child: FaIcon(FontAwesomeIcons.print), | ||||
|           label: L10().printLabel, | ||||
|           onTap: () { | ||||
|             _printLabel(context); | ||||
|           onTap: () async { | ||||
|             selectAndPrintLabel( | ||||
|                 context, | ||||
|                 labels, | ||||
|                 "stock", | ||||
|                 "item=${widget.item.pk}" | ||||
|             ); | ||||
|           } | ||||
|         ) | ||||
|       ); | ||||
| @@ -198,9 +204,10 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> { | ||||
|  | ||||
|   int attachmentCount = 0; | ||||
|  | ||||
|   bool allowLabelPrinting = true; | ||||
|  | ||||
|   @override | ||||
|   Future<void> onBuild(BuildContext context) async { | ||||
|  | ||||
|     // Load part data if not already loaded | ||||
|     if (part == null) { | ||||
|       refresh(context); | ||||
| @@ -209,9 +216,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> { | ||||
|  | ||||
|   @override | ||||
|   Future<void> request(BuildContext context) async { | ||||
|  | ||||
|     await api.StockStatus.load(); | ||||
|  | ||||
|     stockShowHistory = await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_HISTORY, false) as bool; | ||||
|     stockShowTests = await InvenTreeSettingsManager().getValue(INV_STOCK_SHOW_TESTS, true) as bool; | ||||
|  | ||||
| @@ -254,43 +259,20 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> { | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     // Determine if label printing is supported | ||||
|     allowLabelPrinting = await InvenTreeSettingsManager().getBool(INV_ENABLE_LABEL_PRINTING, true); | ||||
|     allowLabelPrinting &= api.getPlugins(mixin: "labels").isNotEmpty; | ||||
|  | ||||
|     // Request information on labels available for this stock item | ||||
|     if (InvenTreeAPI().pluginsEnabled()) { | ||||
|       _getLabels(); | ||||
|     if (allowLabelPrinting) { | ||||
|       // Clear the existing labels list | ||||
|       labels.clear(); | ||||
|       labels = await getLabelTemplates("stock", { | ||||
|         "item": widget.item.pk.toString() | ||||
|       }); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future <void> _getLabels() async { | ||||
|     // Clear the existing labels list | ||||
|     labels.clear(); | ||||
|  | ||||
|     // If the server does not support label printing, don't bother! | ||||
|     if (!InvenTreeAPI().supportsMixin("labels")) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     InvenTreeAPI().get( | ||||
|         "/label/stock/", | ||||
|         params: { | ||||
|           "enabled": "true", | ||||
|           "item": "${widget.item.pk}", | ||||
|         }, | ||||
|     ).then((APIResponse response) { | ||||
|       if (response.isValid() && response.statusCode == 200) { | ||||
|  | ||||
|         for (var label in response.resultsList()) { | ||||
|           if (label is Map<String, dynamic>) { | ||||
|             labels.add(label); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (mounted) { | ||||
|           setState(() {}); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   /// Delete the stock item from the database | ||||
|   Future<void> _deleteItem(BuildContext context) async { | ||||
|  | ||||
| @@ -314,87 +296,6 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   /// Opens a popup dialog allowing user to select a label for printing | ||||
|   Future <void> _printLabel(BuildContext context) async { | ||||
|  | ||||
|     var plugins = InvenTreeAPI().getPlugins(mixin: "labels"); | ||||
|  | ||||
|     dynamic initial_label; | ||||
|     dynamic initial_plugin; | ||||
|  | ||||
|     List<Map<String, dynamic>> label_options = []; | ||||
|     List<Map<String, dynamic>> plugin_options = []; | ||||
|  | ||||
|     for (var label in labels) { | ||||
|       label_options.add({ | ||||
|         "display_name": label["description"], | ||||
|         "value": label["pk"], | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     for (var plugin in plugins) { | ||||
|       plugin_options.add({ | ||||
|         "display_name": plugin.humanName, | ||||
|         "value": plugin.key, | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     if (labels.length == 1) { | ||||
|       initial_label =  labels.first["pk"]; | ||||
|     } | ||||
|  | ||||
|     if (plugins.length == 1) { | ||||
|       initial_plugin = plugins.first.key; | ||||
|     } | ||||
|  | ||||
|     Map<String, dynamic> fields = { | ||||
|       "label": { | ||||
|         "label": L10().labelTemplate, | ||||
|         "type": "choice", | ||||
|         "value": initial_label, | ||||
|         "choices": label_options, | ||||
|         "required": true, | ||||
|       }, | ||||
|       "plugin": { | ||||
|         "label": L10().pluginPrinter, | ||||
|         "type": "choice", | ||||
|         "value": initial_plugin, | ||||
|         "choices": plugin_options, | ||||
|         "required": true, | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     launchApiForm( | ||||
|       context, | ||||
|       L10().printLabel, | ||||
|       "", | ||||
|       fields, | ||||
|       icon: FontAwesomeIcons.print, | ||||
|       onSuccess: (Map<String, dynamic> data) async { | ||||
|         int labelId = (data["label"] ?? -1) as int; | ||||
|         String pluginKey = (data["plugin"] ?? "") as String; | ||||
|  | ||||
|         if (labelId != -1 && pluginKey.isNotEmpty) { | ||||
|           String url = "/label/stock/${labelId}/print/?item=${widget.item.pk}&plugin=${pluginKey}"; | ||||
|  | ||||
|           InvenTreeAPI().get(url).then((APIResponse response) { | ||||
|             if (response.isValid() && response.statusCode == 200) { | ||||
|               showSnackIcon( | ||||
|                 L10().printLabelSuccess, | ||||
|                 success: true | ||||
|               ); | ||||
|             } else { | ||||
|               showSnackIcon( | ||||
|                 L10().printLabelFailure, | ||||
|                 success: false, | ||||
|               ); | ||||
|             } | ||||
|           }); | ||||
|         } | ||||
|       }, | ||||
|     ); | ||||
|   } | ||||
|  | ||||
|   Future <void> _editStockItem(BuildContext context) async { | ||||
|  | ||||
|     var fields = InvenTreeStockItem().formFields(); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user