mirror of
https://github.com/inventree/inventree-app.git
synced 2025-12-03 18:59:50 +00:00
* Basic widget * Redirect label printing action * Refactor label printing code * Construct form elements * Refactor to allow re-use of forms * Basic rendering of label printing form * Remove dead code * Pass custom handler through to form context * Refactoring API forms: - Allow custom pk field name - Add callback when values change * linting * Dynamically rebuild form * Handle nested fields * Handle label printing status * Run dart format * Update release notes * Remove unused var * Enable close icon * Handle initial plugin default value * Store default values: - Selected template (per label type) - Selected printing plugin * Dart format * Fix dart linting * use setter * Just use a public field
281 lines
7.7 KiB
Dart
281 lines
7.7 KiB
Dart
import "package:flutter/cupertino.dart";
|
|
import "package:flutter/material.dart";
|
|
import "package:inventree/api.dart";
|
|
import "package:inventree/preferences.dart";
|
|
import "package:inventree/api_form.dart";
|
|
import "package:inventree/l10.dart";
|
|
import "package:inventree/widget/progress.dart";
|
|
import "package:inventree/widget/snacks.dart";
|
|
|
|
const String PRINT_LABEL_URL = "api/label/print/";
|
|
|
|
/*
|
|
* Custom form handler for label printing.
|
|
* Required to manage dynamic form fields.
|
|
*/
|
|
class LabelFormWidgetState extends APIFormWidgetState {
|
|
LabelFormWidgetState() : super();
|
|
|
|
List<APIFormField> dynamicFields = [];
|
|
|
|
String pluginKey = "";
|
|
String labelType = "";
|
|
|
|
@override
|
|
List<APIFormField> get formFields {
|
|
final baseFields = super.formFields;
|
|
|
|
if (pluginKey.isEmpty) {
|
|
// Handle case where default plugin is provided
|
|
final APIFormField pluginField = baseFields.firstWhere(
|
|
(field) => field.name == "plugin",
|
|
);
|
|
|
|
if (pluginField.initial_data != null) {
|
|
pluginKey = pluginField.value.toString();
|
|
onValueChanged("plugin", pluginKey);
|
|
}
|
|
}
|
|
|
|
return [...baseFields, ...dynamicFields];
|
|
}
|
|
|
|
@override
|
|
void onValueChanged(String field, dynamic value) {
|
|
if (field == "plugin") {
|
|
onPluginChanged(value.toString());
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future<void> handleSuccess(
|
|
Map<String, dynamic> submittedData,
|
|
Map<String, dynamic> responseData,
|
|
) async {
|
|
super.handleSuccess(submittedData, responseData);
|
|
|
|
// Save default values to the database
|
|
final String? plugin = submittedData["plugin"]?.toString();
|
|
final int? template = submittedData["template"] as int?;
|
|
|
|
// Save default printing plugin
|
|
if (plugin != null) {
|
|
InvenTreeSettingsManager().setValue(INV_LABEL_DEFAULT_PLUGIN, plugin);
|
|
}
|
|
|
|
// Save default template for this label type
|
|
if (labelType.isNotEmpty && template != null) {
|
|
final defaultTemplates =
|
|
await InvenTreeSettingsManager().getValue(
|
|
INV_LABEL_DEFAULT_TEMPLATES,
|
|
null,
|
|
)
|
|
as Map<String, dynamic>?;
|
|
|
|
InvenTreeSettingsManager().setValue(INV_LABEL_DEFAULT_TEMPLATES, {
|
|
...?defaultTemplates,
|
|
labelType: template,
|
|
});
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Re-fetch printing options when the plugin changes
|
|
*/
|
|
Future<void> onPluginChanged(String key) async {
|
|
showLoadingOverlay();
|
|
|
|
InvenTreeAPI().options(PRINT_LABEL_URL, params: {"plugin": key}).then((
|
|
APIResponse response,
|
|
) {
|
|
if (response.isValid()) {
|
|
updateFields(response);
|
|
hideLoadingOverlay();
|
|
}
|
|
});
|
|
}
|
|
|
|
/*
|
|
* Callback when the server responds with printing options,
|
|
* based on the selected printing plugin
|
|
*/
|
|
Future<void> updateFields(APIResponse response) async {
|
|
Map<String, dynamic> printingFields = extractFields(response);
|
|
|
|
// Find only the fields which are not in the "base" fields
|
|
List<APIFormField> uniqueFields = [];
|
|
|
|
for (String key in printingFields.keys) {
|
|
if (super.formFields.any((field) => field.name == key)) {
|
|
continue;
|
|
}
|
|
|
|
dynamic data = printingFields[key];
|
|
|
|
Map<String, dynamic> fieldData = {};
|
|
|
|
if (data is Map) {
|
|
fieldData = Map<String, dynamic>.from(data);
|
|
}
|
|
|
|
APIFormField field = APIFormField(key, fieldData);
|
|
field.definition = extractFieldDefinition(
|
|
printingFields,
|
|
field.lookupPath,
|
|
);
|
|
|
|
if (field.type == "dependent field") {
|
|
// Dependent fields must be handled separately
|
|
|
|
// TODO: This should be refactored into api_form.dart
|
|
dynamic child = field.definition["child"];
|
|
|
|
if (child != null && child is Map) {
|
|
Map<String, dynamic> child_map = child as Map<String, dynamic>;
|
|
dynamic nested_children = child_map["children"];
|
|
|
|
if (nested_children != null && nested_children is Map) {
|
|
Map<String, dynamic> nested_child_map =
|
|
nested_children as Map<String, dynamic>;
|
|
|
|
for (var field_key in nested_child_map.keys) {
|
|
field = APIFormField(field_key, nested_child_map);
|
|
field.definition = extractFieldDefinition(
|
|
nested_child_map,
|
|
field_key,
|
|
);
|
|
uniqueFields.add(field);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// This is a "standard" (non-nested) field
|
|
uniqueFields.add(field);
|
|
}
|
|
}
|
|
|
|
if (mounted) {
|
|
setState(() {
|
|
dynamicFields = uniqueFields;
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
Future<void> handlePrintingSuccess(
|
|
BuildContext context,
|
|
Map<String, dynamic> data,
|
|
int repeatCount,
|
|
) async {
|
|
const int MAX_REPEATS = 60;
|
|
|
|
int id = (data["pk"] ?? -1) as int;
|
|
bool complete = (data["complete"] ?? false) as bool;
|
|
bool error = data["errors"] != null;
|
|
String? output = data["output"] as String?;
|
|
|
|
if (complete) {
|
|
if (output != null && output.isNotEmpty) {
|
|
// An output was generated - we can download it!
|
|
showSnackIcon(L10().downloading, success: true);
|
|
InvenTreeAPI().downloadFile(output);
|
|
} else {
|
|
// Label was offloaded, likely to an external printer
|
|
showSnackIcon(L10().printLabelSuccess, success: true);
|
|
}
|
|
} else if (error) {
|
|
showSnackIcon(L10().printLabelFailure, success: false);
|
|
} else if (repeatCount < MAX_REPEATS && id > 0) {
|
|
// Printing is not yet complete, but we have a valid output ID
|
|
Future.delayed(Duration(milliseconds: 1000), () async {
|
|
// Re-query the printing status
|
|
InvenTreeAPI().get("data-output/$id/").then((response) {
|
|
if (response.statusCode == 200) {
|
|
if (response.data is Map<String, dynamic>) {
|
|
final responseData = response.data as Map<String, dynamic>;
|
|
handlePrintingSuccess(context, responseData, repeatCount + 1);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Select a particular label, from a provided list of options,
|
|
* and print against the selected instances.
|
|
*
|
|
*/
|
|
Future<void> selectAndPrintLabel(
|
|
BuildContext context,
|
|
String labelType,
|
|
int instanceId,
|
|
) async {
|
|
if (!InvenTreeAPI().isConnected()) {
|
|
return;
|
|
}
|
|
|
|
if (!InvenTreeAPI().supportsModernLabelPrinting) {
|
|
// Legacy label printing API not supported
|
|
showSnackIcon("Label printing not supported by server", success: false);
|
|
return;
|
|
}
|
|
|
|
// Fetch default values for label printing
|
|
|
|
// Default template
|
|
final defaultTemplates = await InvenTreeSettingsManager().getValue(
|
|
INV_LABEL_DEFAULT_TEMPLATES,
|
|
null,
|
|
);
|
|
int? defaultTemplate;
|
|
|
|
if (defaultTemplates != null && defaultTemplates is Map<String, dynamic>) {
|
|
defaultTemplate = defaultTemplates[labelType] as int?;
|
|
}
|
|
|
|
// Default plugin
|
|
final defaultPlugin = await InvenTreeSettingsManager().getValue(
|
|
INV_LABEL_DEFAULT_PLUGIN,
|
|
null,
|
|
);
|
|
|
|
// Specify a default list of fields for printing
|
|
// The selected plugin may optionally extend this list of fields dynamically
|
|
Map<String, Map<String, dynamic>> baseFields = {
|
|
"template": {
|
|
"default": defaultTemplate,
|
|
"filters": {
|
|
"enabled": true,
|
|
"model_type": labelType,
|
|
"items": instanceId.toString(),
|
|
},
|
|
},
|
|
"plugin": {
|
|
"default": defaultPlugin,
|
|
"pk_field": "key",
|
|
"filters": {"enabled": true, "mixin": "labels"},
|
|
},
|
|
"items": {
|
|
"hidden": true,
|
|
"value": [instanceId],
|
|
},
|
|
};
|
|
|
|
final formHandler = LabelFormWidgetState();
|
|
formHandler.labelType = labelType;
|
|
|
|
launchApiForm(
|
|
context,
|
|
L10().printLabel,
|
|
PRINT_LABEL_URL,
|
|
baseFields,
|
|
method: "POST",
|
|
modelData: {"plugin": defaultPlugin, "template": defaultTemplate},
|
|
formHandler: formHandler,
|
|
onSuccess: (data) async {
|
|
handlePrintingSuccess(context, data, 0);
|
|
},
|
|
);
|
|
}
|