From d61ae8532a82109cafa642df7bf0ffdcd0d0c4cb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 28 Jan 2021 21:36:57 +1100 Subject: [PATCH] Dialog for checking multiple items into a stock location --- InvenTree/barcode/api.py | 2 +- InvenTree/stock/api.py | 3 +- .../stock/templates/stock/item_base.html | 2 +- InvenTree/templates/js/barcode.js | 180 ++++++++++++++++-- 4 files changed, 163 insertions(+), 24 deletions(-) diff --git a/InvenTree/barcode/api.py b/InvenTree/barcode/api.py index cecdf0b349..f8a2c37329 100644 --- a/InvenTree/barcode/api.py +++ b/InvenTree/barcode/api.py @@ -90,7 +90,7 @@ class BarcodeScan(APIView): if loc is not None: response['stocklocation'] = plugin.renderStockLocation(loc) - response['url'] = reverse('location-detail', kwargs={'pk': loc.id}) + response['url'] = reverse('stock-location-detail', kwargs={'pk': loc.id}) match_found = True # Try to associate with a part diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index c8211aaeca..3e874e91e5 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -160,7 +160,8 @@ class StockAdjust(APIView): try: quantity = Decimal(str(entry.get('quantity', None))) except (ValueError, TypeError, InvalidOperation): - raise ValidationError({'quantity': 'Each entry must contain a valid quantity field'}) + # Default to the quantity of the item + quantity = item.quantity if quantity < 0: raise ValidationError({'quantity': 'Quantity field must not be less than zero'}) diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index e73ce8c7f9..d284a74e98 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -460,7 +460,7 @@ $("#barcode-unlink").click(function() { }); $("#barcode-scan-into-location").click(function() { - scanItemIntoLocation({{ item.id }}); + scanItemsIntoLocation([{{ item.id }}]); }); {% if item.in_stock %} diff --git a/InvenTree/templates/js/barcode.js b/InvenTree/templates/js/barcode.js index 9c9a8c1b08..0cc58be914 100644 --- a/InvenTree/templates/js/barcode.js +++ b/InvenTree/templates/js/barcode.js @@ -25,6 +25,25 @@ function makeBarcodeInput(placeholderText='') { return html; } +function makeNotesField(options={}) { + + var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}'; + + return ` +
+ +
+
+ + + + +
+
${tooltip}
+
+
`; +} + function showBarcodeMessage(modal, message, style='danger') { @@ -386,22 +405,10 @@ function barcodeCheckIn(location_id, options={}) { var table = `
`; // Extra form fields - var extra = ` -
- -
-
- - - - -
-
{% trans "Enter optional notes for stock transfer" %}
-
-
`; + var extra = makeNotesField(); barcodeDialog( - "{% trans "Check Stock Items into Location" %}", + '{% trans "Check Stock Items into Location" %}', { headerContent: table, preShow: function() { @@ -414,7 +421,6 @@ function barcodeCheckIn(location_id, options={}) { extraFields: extra, onSubmit: function() { - // Called when the 'check-in' button is pressed var data = {location: location_id}; @@ -434,7 +440,7 @@ function barcodeCheckIn(location_id, options={}) { data.items = entries; inventreePut( - '{% url 'api-stock-transfer' %}', + "{% url 'api-stock-transfer' %}", data, { method: 'POST', @@ -446,7 +452,7 @@ function barcodeCheckIn(location_id, options={}) { showAlertOrCache('alert-success', response.success, true); location.reload(); } else { - showAlertOrCache('alert-success', 'Error transferring stock', false); + showAlertOrCache('alert-success', '{% trans "Error transferring stock" %}', false); } } } @@ -482,25 +488,25 @@ function barcodeCheckIn(location_id, options={}) { }); if (duplicate) { - showBarcodeMessage(modal, "{% trans "Stock Item already scanned" %}", "warning"); + showBarcodeMessage(modal, '{% trans "Stock Item already scanned" %}', "warning"); } else { if (stockitem.location == location_id) { - showBarcodeMessage(modal, "{% trans "Stock Item already in this location" %}"); + showBarcodeMessage(modal, '{% trans "Stock Item already in this location" %}'); return; } // Add this stock item to the list items.push(stockitem); - showBarcodeMessage(modal, "{% trans "Added stock item" %}", "success"); + showBarcodeMessage(modal, '{% trans "Added stock item" %}', "success"); reloadTable(); } } else { // Barcode does not match a stock item - showBarcodeMessage(modal, "{% trans "Barcode does not match Stock Item" %}", "warning"); + showBarcodeMessage(modal, '{% trans "Barcode does not match Stock Item" %}', "warning"); } } else { showInvalidResponseError(modal, response, status); @@ -512,3 +518,135 @@ function barcodeCheckIn(location_id, options={}) { } ); } + + +/* + * Display dialog to check a single stock item into a stock location + */ +function scanItemsIntoLocation(item_id_list, options={}) { + + var modal = options.modal || '#modal-form'; + + var stock_location = null; + + // Extra form fields + var extra = makeNotesField(); + + // Header contentfor + var header = ` +
+
+ `; + + function updateLocationInfo(location) { + var div = $(modal + ' #header-div'); + + if (stock_location && stock_location.pk) { + div.html(` +
+ {% trans "Location" %}
+ ${stock_location.name}
+ ${stock_location.description} +
+ `); + } else { + div.html(''); + } + } + + barcodeDialog( + '{% trans "Check Into Location" %}', + { + headerContent: header, + extraFields: extra, + preShow: function() { + modalSetSubmitText(modal, '{% trans "Check In" %}'); + modalEnable(modal, false); + }, + onShow: function() { + }, + onSubmit: function() { + // Called when the 'check-in' button is pressed + if (!stock_location) { + return; + } + + var items = []; + + item_id_list.forEach(function(pk) { + items.push({ + pk: pk, + }); + }) + + var data = { + location: stock_location.pk, + notes: $(modal + ' #notes').val(), + items: items, + }; + + // Send API request + inventreePut( + '{% url "api-stock-transfer" %}', + data, + { + method: 'POST', + success: function(response, status) { + // First hide the modal + $(modal).modal('hide'); + + if (status == 'success' && 'success' in response) { + showAlertOrCache('alert-success', response.success, true); + location.reload(); + } else { + showAlertOrCache('alert-danger', '{% trans "Error transferring stock" %}', false); + } + } + } + ) + }, + onScan: function(barcode) { + updateLocationInfo(null); + enableBarcodeInput(modal, false); + inventreePut( + '/api/barcode/', + { + barcode: barcode, + }, + { + method: 'POST', + error: function() { + enableBarcodeInput(modal, true); + showBarcodeMessage(modal, '{% trans "Server error" %}'); + }, + success: function(response, status) { + modalEnable(modal, false); + enableBarcodeInput(modal, true); + + if (status == 'success') { + if ('stocklocation' in response) { + // Barcode corresponds to a StockLocation + stock_location = response.stocklocation; + + updateLocationInfo(stock_location); + modalEnable(modal, true); + + } else { + // Barcode does *NOT* correspond to a StockLocation + showBarcodeMessage( + modal, + '{% trans "Barcode does not match a valid location" %}', + "warning", + ); + } + } else { + // Invalid response returned from server + showInvalidResponseError(modal, response, status); + } + } + } + ) + } + } + ) +} \ No newline at end of file