diff --git a/assets/release_notes.md b/assets/release_notes.md index 52563aca..58fb5f1e 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -7,6 +7,8 @@ - Fixes bug when removing entire quantity of a stock item - Add line items to purchase orders directly from the app - Add line items to purchase order using barcode scanner +- Add line items to sales orders directly from the app +- Add line items to sales order using barcode scanner ### 0.13.0 - October 2023 diff --git a/lib/barcode/purchase_order.dart b/lib/barcode/purchase_order.dart index 3069da20..8fe67b89 100644 --- a/lib/barcode/purchase_order.dart +++ b/lib/barcode/purchase_order.dart @@ -188,7 +188,6 @@ class POAllocateBarcodeHandler extends BarcodeHandler { context, L10().lineItemAdd, fields: fields, - onSuccess: (data) async {}, ); } diff --git a/lib/barcode/sales_order.dart b/lib/barcode/sales_order.dart new file mode 100644 index 00000000..d485f8fc --- /dev/null +++ b/lib/barcode/sales_order.dart @@ -0,0 +1,81 @@ +import "package:flutter/material.dart"; + +import "package:inventree/inventree/part.dart"; +import "package:inventree/inventree/sales_order.dart"; +import "package:one_context/one_context.dart"; + +import "package:inventree/l10.dart"; + +import "package:inventree/barcode/handler.dart"; +import "package:inventree/barcode/tones.dart"; + +import "package:inventree/widget/snacks.dart"; + + +/* + * Barcode handler class for scanning a new part into a SalesOrder + */ + +class SOAddItemBarcodeHandler extends BarcodeHandler { + + SOAddItemBarcodeHandler({this.salesOrder}); + + InvenTreeSalesOrder? salesOrder; + + @override + String getOverlayText(BuildContext context) => L10().barcodeScanPart; + + @override + Future onBarcodeMatched(Map data) async { + + // Extract the part ID from the returned data + int part_id = -1; + + if (data.containsKey("part")) { + part_id = (data["part"] ?? {} as Map)["pk"] as int; + } + + if (part_id <= 0) { + return onBarcodeUnknown(data); + } + + // Request the part from the server + var part = await InvenTreePart().get(part_id); + + if (part is InvenTreePart) { + + if (part.isSalable) { + // Dispose of the barcode scanner + if (OneContext.hasContext) { + OneContext().pop(); + } + + final context = OneContext().context!; + + var fields = InvenTreeSOLineItem().formFields(); + + fields["order"]?["value"] = salesOrder!.pk; + fields["order"]?["hidden"] = true; + + fields["part"]?["value"] = part.pk; + fields["part"]?["hidden"] = false; + + InvenTreeSOLineItem().createForm( + context, + L10().lineItemAdd, + fields: fields, + ); + + } else { + barcodeFailureTone(); + showSnackIcon(L10().partNotSalable, success: false); + } + + } else { + // Failed to fetch part + return onBarcodeUnknown(data); + } + + } + +} \ No newline at end of file diff --git a/lib/inventree/sales_order.dart b/lib/inventree/sales_order.dart index 1225cb86..4cbc1ab0 100644 --- a/lib/inventree/sales_order.dart +++ b/lib/inventree/sales_order.dart @@ -118,7 +118,11 @@ class InvenTreeSOLineItem extends InvenTreeOrderLine { "order": { "hidden": true, }, - "part": {}, + "part": { + "filters": { + "salable": true, + } + }, "quantity": {}, "reference": {}, "notes": {}, diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e26cfab1..b8afdd5e 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -112,6 +112,9 @@ "barcodeNotAssigned": "Barcode not assigned", "@barcodeNotAssigned": {}, + "barcodeScanPart": "Scan part barcode", + "@barcodeScanPart": {}, + "barcodeReceivePart": "Scan barcode to receive part", "@barcodeReceivePart": {}, @@ -766,6 +769,9 @@ "description": "Part (multiple)" }, + "partNotSalable": "Part not marked as salable", + "@partNotSalable": {}, + "partsNone": "No Parts", "@partsNone": {}, diff --git a/lib/widget/order/sales_order_detail.dart b/lib/widget/order/sales_order_detail.dart index d64070fc..2a489ac6 100644 --- a/lib/widget/order/sales_order_detail.dart +++ b/lib/widget/order/sales_order_detail.dart @@ -2,6 +2,8 @@ import "package:flutter/material.dart"; import "package:flutter_speed_dial/flutter_speed_dial.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; +import "package:inventree/barcode/barcode.dart"; +import "package:inventree/barcode/sales_order.dart"; import "package:inventree/inventree/company.dart"; import "package:inventree/inventree/sales_order.dart"; import "package:inventree/widget/order/so_line_list.dart"; @@ -101,7 +103,20 @@ class _SalesOrderDetailState extends RefreshableState { List barcodeButtons(BuildContext context) { List actions = []; - // TODO + if (widget.order.isOpen && InvenTreeSOLineItem().canCreate) { + actions.add( + SpeedDialChild( + child: Icon(Icons.barcode_reader), + label: L10().lineItemAdd, + onTap: () async { + scanBarcode( + context, + handler: SOAddItemBarcodeHandler(salesOrder: widget.order), + ); + } + ) + ); + } return actions; }