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

[refactor] Scan improvements #577

Merged
merged 21 commits into from
Dec 14, 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
3 changes: 3 additions & 0 deletions assets/release_notes.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
### 0.17.1 - December 2024
---

- Add support for ManufacturerPart model
- Support barcode scanning for ManufacturerPart
- Fixes barcode scanning bug which prevents scanning of DataMatrix codes
- Display "destination" information in PurchaseOrder detail view
- Pre-fill "location" field when receiving items against PurchaseOrder
- Fix display of part name in PurchaseOrderLineItem list
- Adds "assigned to me" filter for Purchase Order list
- Adds "assigned to me" filter for Sales Order list
- Updated translations

### 0.17.0 - December 2024
---
Expand Down
43 changes: 26 additions & 17 deletions lib/api_form.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ import "package:inventree/api.dart";
import "package:inventree/app_colors.dart";
import "package:inventree/barcode/barcode.dart";
import "package:inventree/helpers.dart";
import "package:inventree/inventree/sales_order.dart";
import "package:inventree/l10.dart";

import "package:inventree/inventree/company.dart";
import "package:inventree/inventree/part.dart";
import "package:inventree/inventree/project_code.dart";
import "package:inventree/inventree/sentry.dart";
import "package:inventree/inventree/purchase_order.dart";
import "package:inventree/inventree/sales_order.dart";
import "package:inventree/inventree/stock.dart";

import "package:inventree/inventree/sentry.dart";

import "package:inventree/widget/dialogs.dart";
import "package:inventree/widget/fields.dart";
import "package:inventree/widget/progress.dart";
Expand Down Expand Up @@ -562,11 +564,17 @@ class APIFormField {
Map<String, dynamic> data = item as Map<String, dynamic>;

switch (model) {
case "part":
case InvenTreePart.MODEL_TYPE:
return InvenTreePart.fromJson(data).fullname;
case "partcategory":
case InvenTreeCompany.MODEL_TYPE:
return InvenTreeCompany.fromJson(data).name;
case InvenTreePurchaseOrder.MODEL_TYPE:
return InvenTreePurchaseOrder.fromJson(data).reference;
case InvenTreeSalesOrder.MODEL_TYPE:
return InvenTreeSalesOrder.fromJson(data).reference;
case InvenTreePartCategory.MODEL_TYPE:
return InvenTreePartCategory.fromJson(data).pathstring;
case "stocklocation":
case InvenTreeStockLocation.MODEL_TYPE:
return InvenTreeStockLocation.fromJson(data).pathstring;
default:
return "itemAsString not implemented for '${model}'";
Expand Down Expand Up @@ -606,10 +614,12 @@ class APIFormField {
Map<String, String> _relatedFieldFilters() {

switch (model) {
case "supplierpart":
case InvenTreeSupplierPart.MODEL_TYPE:
return InvenTreeSupplierPart().defaultListFilters();
case "stockitem":
case InvenTreeStockItem.MODEL_TYPE:
return InvenTreeStockItem().defaultListFilters();
default:
break;
}

return {};
Expand Down Expand Up @@ -643,7 +653,7 @@ class APIFormField {
}

switch (model) {
case "part":
case InvenTreePart.MODEL_TYPE:
var part = InvenTreePart.fromJson(data);

return ListTile(
Expand All @@ -657,14 +667,14 @@ class APIFormField {
) : null,
leading: extended ? InvenTreeAPI().getThumbnail(part.thumbnail) : null,
);
case "parttesttemplate":
case InvenTreePartTestTemplate.MODEL_TYPE:
var template = InvenTreePartTestTemplate.fromJson(data);

return ListTile(
title: Text(template.testName),
subtitle: Text(template.description),
);
case "supplierpart":
case InvenTreeSupplierPart.MODEL_TYPE:
var part = InvenTreeSupplierPart.fromJson(data);

return ListTile(
Expand All @@ -673,7 +683,7 @@ class APIFormField {
leading: extended ? InvenTreeAPI().getThumbnail(part.partImage) : null,
trailing: extended && part.supplierImage.isNotEmpty ? InvenTreeAPI().getThumbnail(part.supplierImage) : null,
);
case "partcategory":
case InvenTreePartCategory.MODEL_TYPE:

var cat = InvenTreePartCategory.fromJson(data);

Expand All @@ -687,7 +697,7 @@ class APIFormField {
style: TextStyle(fontWeight: selected ? FontWeight.bold : FontWeight.normal),
) : null,
);
case "stockitem":
case InvenTreeStockItem.MODEL_TYPE:
var item = InvenTreeStockItem.fromJson(data);

return ListTile(
Expand All @@ -697,8 +707,7 @@ class APIFormField {
leading: InvenTreeAPI().getThumbnail(item.partThumbnail),
trailing: Text(item.quantityString()),
);
case "stocklocation":

case InvenTreeStockLocation.MODEL_TYPE:
var loc = InvenTreeStockLocation.fromJson(data);

return ListTile(
Expand All @@ -711,7 +720,7 @@ class APIFormField {
style: TextStyle(fontWeight: selected ? FontWeight.bold : FontWeight.normal),
) : null,
);
case "salesordershipment":
case InvenTreeSalesOrderShipment.MODEL_TYPE:
var shipment = InvenTreeSalesOrderShipment.fromJson(data);

return ListTile(
Expand All @@ -733,14 +742,14 @@ class APIFormField {
title: Text(name),
subtitle: Text(role),
);
case "company":
case InvenTreeCompany.MODEL_TYPE:
var company = InvenTreeCompany.fromJson(data);
return ListTile(
title: Text(company.name),
subtitle: extended ? Text(company.description) : null,
leading: InvenTreeAPI().getThumbnail(company.thumbnail)
);
case "projectcode":
case InvenTreeProjectCode.MODEL_TYPE:
var project_code = InvenTreeProjectCode.fromJson(data);
return ListTile(
title: Text(project_code.code),
Expand Down
107 changes: 67 additions & 40 deletions lib/barcode/barcode.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import "package:flutter_speed_dial/flutter_speed_dial.dart";
import "package:flutter_tabler_icons/flutter_tabler_icons.dart";
import "package:inventree/helpers.dart";
import "package:inventree/inventree/sales_order.dart";
import "package:inventree/inventree/sentry.dart";
import "package:inventree/preferences.dart";
import "package:inventree/widget/company/manufacturer_part_detail.dart";
import "package:inventree/widget/order/sales_order_detail.dart";
import "package:one_context/one_context.dart";

Expand All @@ -30,6 +32,7 @@ import "package:inventree/widget/order/purchase_order_detail.dart";
import "package:inventree/widget/refreshable_state.dart";
import "package:inventree/widget/snacks.dart";
import "package:inventree/widget/stock/stock_detail.dart";
import "package:inventree/widget/company/company_detail.dart";
import "package:inventree/widget/company/supplier_part_detail.dart";


Expand Down Expand Up @@ -176,15 +179,37 @@ class BarcodeScanHandler extends BarcodeHandler {
*/
Future<void> handleSupplierPart(int pk) async {

var supplierpart = await InvenTreeSupplierPart().get(pk);
var supplierPart = await InvenTreeSupplierPart().get(pk);

if (supplierpart is InvenTreeSupplierPart) {
if (supplierPart is InvenTreeSupplierPart) {
OneContext().pop();
OneContext().push(MaterialPageRoute(
builder: (context) => SupplierPartDetailWidget(supplierpart)));
builder: (context) => SupplierPartDetailWidget(supplierPart)));
}
}

/*
* Response when a "ManufacturerPart" instance is scanned
*/
Future<void> handleManufacturerPart(int pk) async {
var manufacturerPart = await InvenTreeManufacturerPart().get(pk);

if (manufacturerPart is InvenTreeManufacturerPart) {
OneContext().pop();
OneContext().push(MaterialPageRoute(
builder: (context) => ManufacturerPartDetailWidget(manufacturerPart)));
}
}

Future<void> handleCompany(int pk) async {
var company = await InvenTreeCompany().get(pk);

if (company is InvenTreeCompany) {
OneContext().pop();
OneContext().push(MaterialPageRoute(
builder: (context) => CompanyDetailWidget(company)));
}
}

/*
* Response when a "PurchaseOrder" instance is scanned
Expand Down Expand Up @@ -218,26 +243,32 @@ class BarcodeScanHandler extends BarcodeHandler {

// The following model types can be matched with barcodes
List<String> validModels = [
"part",
"stockitem",
"stocklocation",
"supplierpart",
InvenTreePart.MODEL_TYPE,
InvenTreeCompany.MODEL_TYPE,
InvenTreeStockItem.MODEL_TYPE,
InvenTreeStockLocation.MODEL_TYPE,
InvenTreeSupplierPart.MODEL_TYPE,
InvenTreeManufacturerPart.MODEL_TYPE,
];


if (InvenTreeAPI().supportsOrderBarcodes) {
validModels.add("purchaseorder");
validModels.add("salesorder");
validModels.add(InvenTreePurchaseOrder.MODEL_TYPE);
validModels.add(InvenTreeSalesOrder.MODEL_TYPE);
}

for (var key in validModels) {
if (data.containsKey(key)) {
pk = (data[key]?["pk"] ?? -1) as int;
try {
pk = (data[key]?["pk"] ?? -1) as int;

// Break on the first valid match found
if (pk > 0) {
model = key;
break;
// Break on the first valid match found
if (pk > 0) {
model = key;
break;
}
} catch (error, stackTrace) {
sentryReportError("onBarcodeMatched", error, stackTrace);
}
}
}
Expand All @@ -248,25 +279,30 @@ class BarcodeScanHandler extends BarcodeHandler {
barcodeSuccessTone();

switch (model) {
case "part":
await handlePart(pk);
return;
case "stockitem":
case InvenTreeStockItem.MODEL_TYPE:
await handleStockItem(pk);
return;
case "stocklocation":
case InvenTreePurchaseOrder.MODEL_TYPE:
await handlePurchaseOrder(pk);
return;
case InvenTreeSalesOrder.MODEL_TYPE:
await handleSalesOrder(pk);
return;
case InvenTreeStockLocation.MODEL_TYPE:
await handleStockLocation(pk);
return;
case "supplierpart":
case InvenTreeSupplierPart.MODEL_TYPE:
await handleSupplierPart(pk);
return;
case "purchaseorder":
await handlePurchaseOrder(pk);
case InvenTreeManufacturerPart.MODEL_TYPE:
await handleManufacturerPart(pk);
return;
case "salesorder":
await handleSalesOrder(pk);
case InvenTreePart.MODEL_TYPE:
await handlePart(pk);
return;
case InvenTreeCompany.MODEL_TYPE:
await handleCompany(pk);
return;
// TODO: Handle manufacturer part
default:
// Fall through to failure state
break;
Expand Down Expand Up @@ -324,21 +360,6 @@ class UniqueBarcodeHandler extends BarcodeHandler {

@override
Future<void> onBarcodeMatched(Map<String, dynamic> data) async {

barcodeFailureTone();

// If the barcode is known, we can"t assign it to the stock item!
showSnackIcon(
L10().barcodeInUse,
icon: Icons.qr_code,
success: false
);
}

@override
Future<void> onBarcodeUnknown(Map<String, dynamic> data) async {
// If the barcode is unknown, we *can* assign it to the stock item!

if (!data.containsKey("hash") && !data.containsKey("barcode_hash")) {
showServerError(
"barcode/",
Expand Down Expand Up @@ -370,6 +391,12 @@ class UniqueBarcodeHandler extends BarcodeHandler {
}
}
}

@override
Future<void> onBarcodeUnknown(Map<String, dynamic> data) async {
await onBarcodeMatched(data);
}

}


Expand Down
14 changes: 6 additions & 8 deletions lib/barcode/camera_controller.dart
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
/*
* Callback function when a barcode is scanned
*/
void _onScanSuccess(Code? code) {
Future<void> onScanSuccess(Code? code) async {

if (scanning_paused) {
return;
Expand Down Expand Up @@ -122,18 +122,16 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
if (mounted) {
setState(() {
scanned_code = barcode;
scanning_paused = barcode.isNotEmpty;
});
}

if (barcode.isNotEmpty) {

handleBarcodeData(barcode).then((_) {
pauseScan();

await handleBarcodeData(barcode).then((_) {
if (!single_scanning && mounted) {
// Resume next scan
setState(() {
scanning_paused = false;
});
resumeScan();
}
});
}
Expand Down Expand Up @@ -186,7 +184,7 @@ class _CameraBarcodeControllerState extends InvenTreeBarcodeControllerState {
Widget BarcodeReader(BuildContext context) {

return ReaderWidget(
onScan: _onScanSuccess,
onScan: onScanSuccess,
isMultiScan: false,
tryHarder: true,
tryInverted: true,
Expand Down
Loading
Loading