From 02a9bbe3c65d8d8ae840d36eb71ba0732c3796a3 Mon Sep 17 00:00:00 2001
From: Oliver Walters <oliver.henry.walters@gmail.com>
Date: Fri, 25 Mar 2022 23:36:56 +1100
Subject: [PATCH] Enhance existing api_forms functionality

- Allow form to be created without an actual API endpoint
- Useful for creating client-side forms without any server interaction
---
 lib/api.dart                 |  8 +++--
 lib/api_form.dart            | 59 ++++++++++++++++++++++--------------
 lib/widget/stock_detail.dart |  5 +++
 3 files changed, 47 insertions(+), 25 deletions(-)

diff --git a/lib/api.dart b/lib/api.dart
index 4c326879..34e4a1c4 100644
--- a/lib/api.dart
+++ b/lib/api.dart
@@ -258,6 +258,9 @@ class InvenTreeAPI {
     return plugins;
   }
 
+  // Test if the provided plugin mixin is supported by any active plugins
+  bool supportsMixin(String mixin) => getPlugins(mixin: mixin).isNotEmpty;
+
   // Getter for server version information
   String get version => _version;
 
@@ -511,6 +514,8 @@ class InvenTreeAPI {
       return;
     }
 
+    print("Requesting plugin information");
+
     // Request a list of plugins from the server
     final List<InvenTreeModel> results = await InvenTreePlugin().list();
 
@@ -522,11 +527,8 @@ class InvenTreeAPI {
         }
       }
     }
-
-    print("Discovered ${_plugins.length} active plugins!");
   }
 
-
   bool checkPermission(String role, String permission) {
     /*
      * Check if the user has the given role.permission assigned
diff --git a/lib/api_form.dart b/lib/api_form.dart
index 75a6a089..5b28f5e4 100644
--- a/lib/api_form.dart
+++ b/lib/api_form.dart
@@ -778,8 +778,6 @@ Map<String, dynamic> extractFieldDefinition(Map<String, dynamic> data, String lo
   String el = path.last;
 
   if (!_data.containsKey(el)) {
-    print("Could not find field definition for ${lookup}");
-    print("- Final field path ${el} missing from data");
     return {};
   } else {
 
@@ -824,24 +822,28 @@ Future<void> launchApiForm(
       IconData icon = FontAwesomeIcons.save,
     }) async {
 
-  var options = await InvenTreeAPI().options(url);
-
-  // Invalid response from server
-  if (!options.isValid()) {
-    return;
-  }
-
   // List of fields defined by the server
-  Map<String, dynamic> serverFields = extractFields(options);
+  Map<String, dynamic> serverFields = {};
 
-  if (serverFields.isEmpty) {
-    // User does not have permission to perform this action
-    showSnackIcon(
-      L10().response403,
-      icon: FontAwesomeIcons.userTimes,
-    );
+  if (url.isNotEmpty) {
+    var options = await InvenTreeAPI().options(url);
 
-    return;
+    // Invalid response from server
+    if (!options.isValid()) {
+      return;
+    }
+
+    serverFields = extractFields(options);
+
+    if (serverFields.isEmpty) {
+      // User does not have permission to perform this action
+      showSnackIcon(
+        L10().response403,
+        icon: FontAwesomeIcons.userTimes,
+      );
+
+      return;
+    }
   }
 
   // Construct a list of APIFormField objects
@@ -868,8 +870,7 @@ Future<void> launchApiForm(
 
     // Skip fields with empty definitions
     if (field.definition.isEmpty) {
-      print("ERROR: Empty field definition for field '${fieldName}'");
-      continue;
+      print("Warning: Empty field definition for field '${fieldName}'");
     }
 
     // Add instance value to the field
@@ -1170,6 +1171,23 @@ class _APIFormWidgetState extends State<APIFormWidget> {
       }
     }
 
+    // Run custom onSuccess function
+    var successFunc = onSuccess;
+
+    // An "empty" URL means we don't want to submit the form anywhere
+    // Perhaps we just want to process the data?
+    if (url.isEmpty) {
+      // Hide the form
+      Navigator.pop(context);
+
+      if (successFunc != null) {
+        // Return the raw "submitted" data, rather than the server response
+        successFunc(data);
+      }
+
+      return;
+    }
+
     final response = await _submit(data);
 
     if (!response.isValid()) {
@@ -1187,9 +1205,6 @@ class _APIFormWidgetState extends State<APIFormWidget> {
 
         // TODO: Display a snackBar
 
-        // Run custom onSuccess function
-        var successFunc = onSuccess;
-
         if (successFunc != null) {
 
           // Ensure the response is a valid JSON structure
diff --git a/lib/widget/stock_detail.dart b/lib/widget/stock_detail.dart
index 9b5c35aa..128a090e 100644
--- a/lib/widget/stock_detail.dart
+++ b/lib/widget/stock_detail.dart
@@ -124,6 +124,11 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
     // 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: {