mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Location barcode actions (#3887)
* Reorder action buttons for stock location * Tweak position of "New Location" button * Tweak loaction of "New Category" button for part category page * Working on skeleton for new barcode dialog * Scan location into location * Add configurable input delay for processing barcode scan data
This commit is contained in:
parent
9beefd09f7
commit
db45b6f9dc
@ -933,6 +933,17 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
},
|
||||
|
||||
'BARCODE_INPUT_DELAY': {
|
||||
'name': _('Barcode Input Delay'),
|
||||
'description': _('Barcode input processing delay time'),
|
||||
'default': 50,
|
||||
'validator': [
|
||||
int,
|
||||
MinValueValidator(1),
|
||||
],
|
||||
'units': 'ms',
|
||||
},
|
||||
|
||||
'BARCODE_WEBCAM_SUPPORT': {
|
||||
'name': _('Barcode Webcam Support'),
|
||||
'description': _('Allow barcode scanning via webcam in browser'),
|
||||
|
@ -63,11 +63,6 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if roles.part_category.add %}
|
||||
<button class='btn btn-success' id='cat-create' title='{% trans "Create new part category" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Category" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block details_left %}
|
||||
@ -225,7 +220,17 @@
|
||||
|
||||
<div class='panel panel-hidden' id='panel-subcategories'>
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Subcategories" %}</h4>
|
||||
<div class='d-flex flex-wrap'>
|
||||
<h4>{% trans "Subcategories" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if roles.part_category.add %}
|
||||
<button class='btn btn-success' id='cat-create' title='{% trans "Create new part category" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Category" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='subcategory-button-toolbar'>
|
||||
|
@ -53,12 +53,21 @@
|
||||
{% else %}
|
||||
<li><a class='dropdown-item' href='#' id='barcode-link'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li>
|
||||
{% endif %}
|
||||
{% if labels_enabled %}
|
||||
<li><a class='dropdown-item' href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a class='dropdown-item' href='#' id='barcode-check-in'><span class='fas fa-arrow-right'></span> {% trans "Check-in Items" %}</a></li>
|
||||
<li><a class='dropdown-item' href='#' id='barcode-scan-in-items' title='{% trans "Scan stock items into this location" %}'><span class='fas fa-boxes'></span> {% trans "Scan In Stock Items" %}</a></li>
|
||||
<li><a class='dropdown-item' href='#' id='barcode-scan-in-containers' title='{% trans "Scan stock container into this location" %}'><span class='fas fa-sitemap'></span> {% trans "Scan In Container" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Printing action -->
|
||||
{% if labels_enabled %}
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='printing-options' title='{% trans "Printing actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-print'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu'>
|
||||
<li><a class='dropdown-item' href='#' id='print-label'><span class='fas fa-print'></span> {% trans "Print Label" %}</a>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- Check permissions and owner -->
|
||||
{% if user_owns_location %}
|
||||
{% if roles.stock.change %}
|
||||
@ -96,11 +105,6 @@
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if user_owns_location and roles.stock_location.add %}
|
||||
<button class='btn btn-success' id='location-create' type='button' title='{% trans "Create new stock location" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Location" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
{% block details_left %}
|
||||
@ -203,7 +207,17 @@
|
||||
|
||||
<div class='panel panel-hidden' id='panel-sublocations'>
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Sublocations" %}</h4>
|
||||
<div class='d-flex flex-wrap'>
|
||||
<h4>{% trans "Sublocations" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if user_owns_location and roles.stock_location.add %}
|
||||
<button class='btn btn-success' id='location-create' type='button' title='{% trans "Create new stock location" %}'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Location" %}
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='panel-content'>
|
||||
<div id='sublocation-button-toolbar'>
|
||||
@ -284,13 +298,29 @@
|
||||
{% endif %}
|
||||
|
||||
{% if location %}
|
||||
$("#barcode-check-in").click(function() {
|
||||
barcodeCheckIn({{ location.id }});
|
||||
$("#barcode-scan-in-items").click(function() {
|
||||
barcodeCheckInStockItems({{ location.id }});
|
||||
});
|
||||
|
||||
$('#barcode-scan-in-containers').click(function() {
|
||||
barcodeCheckInStockLocations({{ location.id }},
|
||||
{
|
||||
onSuccess: function() {
|
||||
showMessage(
|
||||
'{% trans "Scanned stock container into this location" %}',
|
||||
{
|
||||
style: 'success',
|
||||
}
|
||||
);
|
||||
|
||||
$('#sublocation-table').bootstrapTable('refresh');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$('#location-create').click(function () {
|
||||
|
||||
createStockLocation({
|
||||
{% if location %}
|
||||
parent: {{ location.pk }},
|
||||
|
@ -13,6 +13,7 @@
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_ENABLE" icon="fa-qrcode" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_INPUT_DELAY" icon="fa-hourglass-half" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="BARCODE_WEBCAM_SUPPORT" icon="fa-video" %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -14,7 +14,8 @@
|
||||
*/
|
||||
|
||||
/* exported
|
||||
barcodeCheckIn,
|
||||
barcodeCheckInStockItems,
|
||||
barcodeCheckInStockLocations,
|
||||
barcodeScanDialog,
|
||||
linkBarcodeDialog,
|
||||
scanItemsIntoLocation,
|
||||
@ -22,12 +23,14 @@
|
||||
onBarcodeScanClicked,
|
||||
*/
|
||||
|
||||
function makeBarcodeInput(placeholderText='', hintText='') {
|
||||
/*
|
||||
* Generate HTML for a barcode input
|
||||
*/
|
||||
var barcodeInputTimer = null;
|
||||
|
||||
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
||||
/*
|
||||
* Generate HTML for a barcode scan input
|
||||
*/
|
||||
function makeBarcodeInput(placeholderText='', hintText='') {
|
||||
|
||||
placeholderText = placeholderText || '{% trans "Scan barcode data here using barcode scanner" %}';
|
||||
|
||||
hintText = hintText || '{% trans "Enter barcode data" %}';
|
||||
|
||||
@ -43,7 +46,7 @@ function makeBarcodeInput(placeholderText='', hintText='') {
|
||||
<span class='fas fa-qrcode'></span>
|
||||
</span>
|
||||
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
||||
<button id='barcode_scan_btn' type='button' class='btn btn-secondary' onclick='onBarcodeScanClicked()' style='display: none;'>
|
||||
<button title='{% trans "Scan barcode using connected webcam" %}' id='barcode_scan_btn' type='button' class='btn btn-secondary' onclick='onBarcodeScanClicked()' style='display: none;'>
|
||||
<span class='fas fa-camera'></span>
|
||||
</button>
|
||||
</div>
|
||||
@ -92,6 +95,9 @@ function onBarcodeScanCompleted(result, options) {
|
||||
postBarcodeData(result.data, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Construct a generic "notes" field for barcode scanning operations
|
||||
*/
|
||||
function makeNotesField(options={}) {
|
||||
|
||||
var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}';
|
||||
@ -199,6 +205,9 @@ function showBarcodeMessage(modal, message, style='danger') {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display an error message when the server indicates an error
|
||||
*/
|
||||
function showInvalidResponseError(modal, response, status) {
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
@ -207,6 +216,9 @@ function showInvalidResponseError(modal, response, status) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Enable (or disable) the barcode scanning input
|
||||
*/
|
||||
function enableBarcodeInput(modal, enabled=true) {
|
||||
|
||||
var barcode = $(modal + ' #barcode');
|
||||
@ -218,6 +230,10 @@ function enableBarcodeInput(modal, enabled=true) {
|
||||
barcode.focus();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Extract scanned data from the barcode input
|
||||
*/
|
||||
function getBarcodeData(modal) {
|
||||
|
||||
modal = modal || '#modal-form';
|
||||
@ -233,10 +249,10 @@ function getBarcodeData(modal) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Handle a barcode display dialog.
|
||||
*/
|
||||
function barcodeDialog(title, options={}) {
|
||||
/*
|
||||
* Handle a barcode display dialog.
|
||||
*/
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
@ -244,7 +260,6 @@ function barcodeDialog(title, options={}) {
|
||||
var barcode = getBarcodeData(modal);
|
||||
|
||||
if (barcode && barcode.length > 0) {
|
||||
|
||||
postBarcodeData(barcode, options);
|
||||
}
|
||||
}
|
||||
@ -264,7 +279,15 @@ function barcodeDialog(title, options={}) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.which == 10 || event.which == 13) {
|
||||
clearTimeout(barcodeInputTimer);
|
||||
sendBarcode();
|
||||
} else {
|
||||
// Start a timer to automatically send barcode after input is complete
|
||||
clearTimeout(barcodeInputTimer);
|
||||
|
||||
barcodeInputTimer = setTimeout(function() {
|
||||
sendBarcode();
|
||||
}, global_settings.BARCODE_INPUT_DELAY);
|
||||
}
|
||||
});
|
||||
|
||||
@ -305,9 +328,11 @@ function barcodeDialog(title, options={}) {
|
||||
modalShowSubmitButton(modal, false);
|
||||
}
|
||||
|
||||
var details = options.details || '{% trans "Scan barcode data" %}';
|
||||
|
||||
var content = '';
|
||||
|
||||
content += `<div class='alert alert-info alert-block'>{% trans "Scan barcode data below" %}</div>`;
|
||||
content += `<div class='alert alert-info alert-block'>${details}</div>`;
|
||||
|
||||
content += `<div id='barcode-error-message'></div>`;
|
||||
content += `<form class='js-modal-form' method='post'>`;
|
||||
@ -431,7 +456,7 @@ function unlinkBarcode(data, options={}) {
|
||||
/*
|
||||
* Display dialog to check multiple stock items in to a stock location.
|
||||
*/
|
||||
function barcodeCheckIn(location_id, options={}) {
|
||||
function barcodeCheckInStockItems(location_id, options={}) {
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
@ -486,6 +511,7 @@ function barcodeCheckIn(location_id, options={}) {
|
||||
|
||||
$(modal + ' #barcode').focus();
|
||||
|
||||
// Callback to remove the scanned item from the table
|
||||
$(modal + ' .button-item-remove').unbind('click').on('mouseup', function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
@ -514,8 +540,9 @@ function barcodeCheckIn(location_id, options={}) {
|
||||
var extra = makeNotesField();
|
||||
|
||||
barcodeDialog(
|
||||
'{% trans "Check Stock Items into Location" %}',
|
||||
'{% trans "Scan Stock Items Into Location" %}',
|
||||
{
|
||||
details: '{% trans "Scan stock item barcode to check in to this location" %}',
|
||||
headerContent: table,
|
||||
preShow: function() {
|
||||
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||
@ -609,7 +636,7 @@ function barcodeCheckIn(location_id, options={}) {
|
||||
);
|
||||
} 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 valid stock item" %}', 'warning');
|
||||
}
|
||||
},
|
||||
}
|
||||
@ -617,6 +644,59 @@ function barcodeCheckIn(location_id, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display dialog to scan stock locations into the current location
|
||||
*/
|
||||
function barcodeCheckInStockLocations(location_id, options={}) {
|
||||
|
||||
var modal = '#modal-form';
|
||||
var header = '';
|
||||
|
||||
barcodeDialog(
|
||||
'{% trans "Scan Stock Container Into Location" %}',
|
||||
{
|
||||
details: '{% trans "Scan stock container barcode to check in to this location" %}',
|
||||
headerContent: header,
|
||||
preShow: function() {
|
||||
modalEnable(modal, false);
|
||||
},
|
||||
onShow: function() {
|
||||
// TODO
|
||||
},
|
||||
onScan: function(response) {
|
||||
if ('stocklocation' in response) {
|
||||
var pk = response.stocklocation.pk;
|
||||
|
||||
var url = `/api/stock/location/${pk}/`;
|
||||
|
||||
// Move the scanned location into *this* location
|
||||
inventreePut(
|
||||
url,
|
||||
{
|
||||
parent: location_id,
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
success: function(response) {
|
||||
$(modal).modal('hide');
|
||||
handleFormSuccess(response, options);
|
||||
},
|
||||
error: function(xhr) {
|
||||
$(modal).modal('hide');
|
||||
showApiError(xhr, url);
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Barcode does not match a valid stock location
|
||||
showBarcodeMessage(modal, '{% trans "Barcode does not match valid stock location" %}', 'warning');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display dialog to check a single stock item into a stock location
|
||||
*/
|
||||
|
Loading…
Reference in New Issue
Block a user