Skip to content

Commit

Permalink
Label printing fix (#489)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
SchrodingersGat authored May 12, 2024
1 parent 91cb24c commit 3c0bca2
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 75 deletions.
6 changes: 6 additions & 0 deletions assets/release_notes.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### 0.15.0 - May 2024
---

- Support modern label printing API
- Updated translations

### 0.14.3 - April 2024
---

Expand Down
3 changes: 3 additions & 0 deletions lib/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<InvenTreePlugin> _plugins = [];

Expand Down
3 changes: 3 additions & 0 deletions lib/inventree/model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 3 additions & 0 deletions lib/inventree/part.dart
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,9 @@ class InvenTreePart extends InvenTreeModel {
@override
String get URL => "part/";

@override
String get MODEL_TYPE => "part";

@override
List<String> get rolesRequired => ["part"];

Expand Down
3 changes: 3 additions & 0 deletions lib/inventree/purchase_order.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class InvenTreePurchaseOrder extends InvenTreeOrder {
@override
String get URL => "order/po/";

@override
String get MODEL_TYPE => "purchaseorder";

@override
List<String> get rolesRequired => ["purchase_order"];

Expand Down
3 changes: 3 additions & 0 deletions lib/inventree/sales_order.dart
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ class InvenTreeSalesOrder extends InvenTreeOrder {
@override
String get URL => "order/so/";

@override
String get MODEL_TYPE => "salesorder";

@override
List<String> get rolesRequired => ["sales_order"];

Expand Down
6 changes: 6 additions & 0 deletions lib/inventree/stock.dart
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,9 @@ class InvenTreeStockItem extends InvenTreeModel {
@override
String get URL => "stock/";

@override
String get MODEL_TYPE => "stockitem";

@override
List<String> get rolesRequired => ["stock"];

Expand Down Expand Up @@ -649,6 +652,9 @@ class InvenTreeStockLocation extends InvenTreeModel {
@override
String get URL => "stock/location/";

@override
String get MODEL_TYPE => "stocklocation";

@override
List<String> get rolesRequired => ["stock_location"];

Expand Down
171 changes: 107 additions & 64 deletions lib/labels.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<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 {
BuildContext context,
List<Map<String, dynamic>> labels,
int instanceId,
String labelType,
String labelQuery,
) async {

if (!InvenTreeAPI().isConnected()) {
return;
Expand Down Expand Up @@ -91,7 +59,7 @@ Future<void> selectAndPrintLabel(
for (var plugin in plugins) {
plugin_options.add({
"display_name": plugin.humanName,
"value": plugin.key
"value": InvenTreeAPI().supportsModenLabelPrinting ? plugin.pk : plugin.key
});
}

Expand Down Expand Up @@ -124,38 +92,113 @@ Future<void> selectAndPrintLabel(
icon: FontAwesomeIcons.print,
onSuccess: (Map<String, dynamic> 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<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";

String url = "/label/template/";

if (InvenTreeAPI().supportsModenLabelPrinting) {
data["model_type"] = labelType;
} else {
// Legacy label printing API endpoint
url = "/label/${labelType}/";
}

List<Map<String, dynamic>> 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<String, dynamic>) {
labels.add(label);
}
}
}
});

return labels;
}
2 changes: 1 addition & 1 deletion lib/settings/about.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
14 changes: 11 additions & 3 deletions lib/widget/part/part_detail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
selectAndPrintLabel(
context,
labels,
widget.part.pk,
"part",
"part=${widget.part.pk}"
);
Expand Down Expand Up @@ -248,9 +249,16 @@ class _PartDisplayState extends RefreshableState<PartDetailWidget> {
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) {
Expand Down
14 changes: 11 additions & 3 deletions lib/widget/stock/location_display.dart
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
selectAndPrintLabel(
context,
labels,
widget.location!.pk,
"location",
"location=${widget.location!.pk}"
);
Expand Down Expand Up @@ -247,9 +248,16 @@ class _LocationDisplayState extends RefreshableState<LocationDisplayWidget> {
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()
}
);
}
}

Expand Down
14 changes: 11 additions & 3 deletions lib/widget/stock/stock_detail.dart
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {
selectAndPrintLabel(
context,
labels,
widget.item.pk,
"stock",
"item=${widget.item.pk}"
);
Expand Down Expand Up @@ -264,10 +265,17 @@ class _StockItemDisplayState extends RefreshableState<StockDetailWidget> {

// 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) {
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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"
Expand Down

0 comments on commit 3c0bca2

Please sign in to comment.