Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Label printing fix #489

Merged
merged 9 commits into from
May 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading