mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Transfer stock items into a stock location using barcode scanning
This commit is contained in:
parent
351a55c9ea
commit
ab4e6548dc
@ -166,14 +166,28 @@ function modalSetContent(modal, content='') {
|
||||
}
|
||||
|
||||
|
||||
function modalSetSubmitText(modal, text) {
|
||||
if (text) {
|
||||
$(modal).find('#modal-form-submit').html(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function modalSetCloseText(modal, text) {
|
||||
if (text) {
|
||||
$(modal).find('#modal-form-close').html(text);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function modalSetButtonText(modal, submit_text, close_text) {
|
||||
/* Set the button text for a modal form
|
||||
*
|
||||
* submit_text - text for the form submit button
|
||||
* close_text - text for the form dismiss button
|
||||
*/
|
||||
$(modal).find("#modal-form-submit").html(submit_text);
|
||||
$(modal).find("#modal-form-close").html(close_text);
|
||||
modalSetSubmitText(modal, submit_text);
|
||||
modalSetCloseText(modal, close_text);
|
||||
}
|
||||
|
||||
|
||||
|
@ -120,6 +120,12 @@
|
||||
inventreeDel('show-part-locs');
|
||||
});
|
||||
|
||||
{% if location %}
|
||||
$("#barcode-check-in").click(function() {
|
||||
barcodeCheckIn({{ location.id }});
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$("#stock-export").click(function() {
|
||||
launchModalForm("{% url 'stock-export-options' %}", {
|
||||
submit_text: "Export",
|
||||
|
@ -1,25 +1,5 @@
|
||||
{% load i18n %}
|
||||
|
||||
/*
|
||||
* Pass barcode data to the server.
|
||||
*/
|
||||
function scanBarcode(barcode, options={}) {
|
||||
|
||||
inventreePut(
|
||||
'/api/barcode/',
|
||||
{
|
||||
'barcode': barcode,
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
success: function(response, status) {
|
||||
console.log(response);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function makeBarcodeInput(placeholderText='') {
|
||||
/*
|
||||
* Generate HTML for a barcode input
|
||||
@ -28,8 +8,6 @@ function makeBarcodeInput(placeholderText='') {
|
||||
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
||||
|
||||
var html = `
|
||||
<div id='barcode-error-message'></div>
|
||||
<form class='js-modal-form' method='post'>
|
||||
<div class='form-group'>
|
||||
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
||||
<div class='controls'>
|
||||
@ -42,14 +20,13 @@ function makeBarcodeInput(placeholderText='') {
|
||||
<div id='hint_barcode_data' class='help-block'>{% trans "Enter barcode data" %}</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
function showBarcodeError(modal, message, style='danger') {
|
||||
function showBarcodeMessage(modal, message, style='danger') {
|
||||
|
||||
var html = `<div class='alert alert-block alert-${style}'>`;
|
||||
|
||||
@ -60,6 +37,12 @@ function showBarcodeError(modal, message, style='danger') {
|
||||
$(modal + ' #barcode-error-message').html(html);
|
||||
}
|
||||
|
||||
|
||||
function showInvalidResponseError(modal, response, status) {
|
||||
showBarcodeMessage(modal, `{% trans "Invalid server response" %}<br>{% trans "Status" %}: '${status}'`);
|
||||
}
|
||||
|
||||
|
||||
function clearBarcodeError(modal, message) {
|
||||
|
||||
$(modal + ' #barcode-error-message').html('');
|
||||
@ -73,6 +56,8 @@ function enableBarcodeInput(modal, enabled=true) {
|
||||
barcode.prop('disabled', !enabled);
|
||||
|
||||
modalEnable(modal, enabled);
|
||||
|
||||
barcode.focus();
|
||||
}
|
||||
|
||||
function getBarcodeData(modal) {
|
||||
@ -86,7 +71,7 @@ function getBarcodeData(modal) {
|
||||
el.val('');
|
||||
el.focus();
|
||||
|
||||
return barcode;
|
||||
return barcode.trim();
|
||||
}
|
||||
|
||||
|
||||
@ -97,54 +82,87 @@ function barcodeDialog(title, options={}) {
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
function sendBarcode() {
|
||||
var barcode = getBarcodeData(modal);
|
||||
|
||||
if (barcode && barcode.length > 0) {
|
||||
|
||||
if (options.onScan) {
|
||||
options.onScan(barcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(modal).on('shown.bs.modal', function() {
|
||||
$(modal + ' .modal-form-content').scrollTop(0);
|
||||
|
||||
var barcode = $(modal + ' #barcode');
|
||||
|
||||
// Handle 'enter' key on barcode
|
||||
barcode.keyup(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.which == 10 || event.which == 13) {
|
||||
sendBarcode();
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure the barcode field has focus
|
||||
$(modal + ' #barcode').focus();
|
||||
barcode.focus();
|
||||
|
||||
var form = $(modal).find('.js-modal-form');
|
||||
|
||||
// Override form submission
|
||||
form.submit(function() {
|
||||
|
||||
var barcode = getBarcodeData(modal);
|
||||
|
||||
if (options.submit) {
|
||||
options.submit(barcode);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// Callback for when the "submit" button is pressed on the modal
|
||||
modalSubmit(modal, function() {
|
||||
|
||||
var barcode = getBarcodeData(modal);
|
||||
|
||||
if (options.submit) {
|
||||
options.submit(barcode);
|
||||
if (options.onSubmit) {
|
||||
options.onSubmit();
|
||||
}
|
||||
});
|
||||
|
||||
if (options.onShow) {
|
||||
options.onShow();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
modalSetTitle(modal, title);
|
||||
|
||||
if (options.onSubmit) {
|
||||
modalShowSubmitButton(modal, true);
|
||||
} else {
|
||||
modalShowSubmitButton(modal, false);
|
||||
}
|
||||
|
||||
var content = '';
|
||||
|
||||
if (options.headerContent) {
|
||||
content += options.headerContent;
|
||||
}
|
||||
|
||||
content += `<div class='alert alert-info alert-block'>{% trans "Scan barcode data below" %}</div>`;
|
||||
|
||||
content += `<div id='barcode-error-message'></div>`;
|
||||
content += `<form class='js-modal-form' method='post'>`;
|
||||
|
||||
// Optional content before barcode input
|
||||
content += `<div class='container' id='barcode-header'>`;
|
||||
content += options.headerContent || '';
|
||||
content += `</div>`;
|
||||
|
||||
content += makeBarcodeInput();
|
||||
|
||||
if (options.footerContent) {
|
||||
content += options.footerContent;
|
||||
if (options.extraFields) {
|
||||
content += options.extraFields;
|
||||
}
|
||||
|
||||
content += `</form>`;
|
||||
|
||||
// Optional content after barcode input
|
||||
content += `<div class='container' id='barcode-footer'>`;
|
||||
content += options.footerContent || '';
|
||||
content += '</div>';
|
||||
|
||||
modalSetContent(modal, content);
|
||||
|
||||
$(modal).modal({
|
||||
@ -152,6 +170,10 @@ function barcodeDialog(title, options={}) {
|
||||
keyboard: false,
|
||||
});
|
||||
|
||||
if (options.preShow) {
|
||||
options.preShow();
|
||||
}
|
||||
|
||||
$(modal).modal('show');
|
||||
}
|
||||
|
||||
@ -167,7 +189,7 @@ function barcodeScanDialog() {
|
||||
barcodeDialog(
|
||||
"Scan Barcode",
|
||||
{
|
||||
submit: function(barcode) {
|
||||
onScan: function(barcode) {
|
||||
enableBarcodeInput(modal, false);
|
||||
inventreePut(
|
||||
'/api/barcode/',
|
||||
@ -190,12 +212,12 @@ function barcodeScanDialog() {
|
||||
}
|
||||
|
||||
} else if ('error' in response) {
|
||||
showBarcodeError(modal, response.error, 'warning');
|
||||
showBarcodeMessage(modal, response.error, 'warning');
|
||||
} else {
|
||||
showBarcodeError(modal, "{% trans 'Unknown response from server' %}", 'warning');
|
||||
showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", 'warning');
|
||||
}
|
||||
} else {
|
||||
showBarcodeError(modal, `{% trans "Invalid server response" %}.<br>Status code: '${status}'`);
|
||||
showInvalidResponseError(modal, response, status);
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -214,9 +236,9 @@ function linkBarcodeDialog(stockitem, options={}) {
|
||||
var modal = '#modal-form';
|
||||
|
||||
barcodeDialog(
|
||||
"Link Barcode",
|
||||
"{% trans 'Link Barcode to Stock Item' %}",
|
||||
{
|
||||
submit: function(barcode) {
|
||||
onScan: function(barcode) {
|
||||
enableBarcodeInput(modal, false);
|
||||
inventreePut(
|
||||
'/api/barcode/link/',
|
||||
@ -228,8 +250,6 @@ function linkBarcodeDialog(stockitem, options={}) {
|
||||
method: 'POST',
|
||||
success: function(response, status) {
|
||||
|
||||
console.log(response);
|
||||
|
||||
enableBarcodeInput(modal, true);
|
||||
|
||||
if (status == 'success') {
|
||||
@ -238,13 +258,13 @@ function linkBarcodeDialog(stockitem, options={}) {
|
||||
$(modal).modal('hide');
|
||||
location.reload();
|
||||
} else if ('error' in response) {
|
||||
showBarcodeError(modal, response.error, 'warning');
|
||||
showBarcodeMessage(modal, response.error, 'warning');
|
||||
} else {
|
||||
showBarcodeError(modal, "{% trans 'Unknown response from server' %}", warning);
|
||||
showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", warning);
|
||||
}
|
||||
|
||||
} else {
|
||||
showBarcodeError(modal, `{% trans "Invalid server response" %}.<br>Status code: '${status}'`);
|
||||
showInvalidResponseError(modal, response, status);
|
||||
}
|
||||
},
|
||||
},
|
||||
@ -287,3 +307,202 @@ function unlinkBarcode(stockitem) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display dialog to check multiple stock items in to a stock location.
|
||||
*/
|
||||
function barcodeCheckIn(location_id, options={}) {
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
// List of items we are going to checkin
|
||||
var items = [];
|
||||
|
||||
|
||||
function reloadTable() {
|
||||
|
||||
modalEnable(modal, false);
|
||||
|
||||
// Remove click listeners
|
||||
$(modal + ' .button-item-remove').off('click');
|
||||
|
||||
var table = $(modal + ' #items-table-div');
|
||||
|
||||
var html = `
|
||||
<table class='table table-condensed table-striped' id='items-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Location" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>`;
|
||||
|
||||
items.forEach(function(item) {
|
||||
html += `
|
||||
<tr pk='${item.pk}'>
|
||||
<td>${imageHoverIcon(item.part_detail.thumbnail)} ${item.part_detail.name}</td>
|
||||
<td>${item.location_detail.name}</td>
|
||||
<td>${item.quantity}</td>
|
||||
<td>${makeIconButton('fa-times-circle icon-red', 'button-item-remove', item.pk, '{% trans "Remove stock item" %}')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
html += `
|
||||
</tbody>
|
||||
</table>`;
|
||||
|
||||
table.html(html);
|
||||
|
||||
modalEnable(modal, items.length > 0);
|
||||
|
||||
$(modal + ' #barcode').focus();
|
||||
|
||||
|
||||
$(modal + ' .button-item-remove').on('click', function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
for (var ii = 0; ii < items.length; ii++) {
|
||||
if (pk.toString() == items[ii].pk.toString()) {
|
||||
//items.splice(ii, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
reloadTable();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var table = `<div class='container' id='items-table-div' style='width: 80%; float: left;'></div>`;
|
||||
|
||||
// Extra form fields
|
||||
var extra = `
|
||||
<div class='form-group'>
|
||||
<label class='control-label' for='notes'>{% trans "Notes" %}</label>
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'>
|
||||
<span class='fas fa-sticky-note'></span>
|
||||
</span>
|
||||
<input id='notes' class='textinput textInput form-control' type='text' name='notes' placeholder='{% trans "Enter notes" %}'>
|
||||
</div>
|
||||
<div id='hint_notes' class='help_block'>{% trans "Enter optional notes for stock transfer" %}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
barcodeDialog(
|
||||
"{% trans "Check Stock Items into Location" %}",
|
||||
{
|
||||
headerContent: table,
|
||||
preShow: function() {
|
||||
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||
modalEnable(modal, false);
|
||||
reloadTable();
|
||||
},
|
||||
onShow: function() {
|
||||
},
|
||||
extraFields: extra,
|
||||
onSubmit: function() {
|
||||
|
||||
|
||||
// Called when the 'check-in' button is pressed
|
||||
|
||||
var data = {location: location_id};
|
||||
|
||||
// Extract 'notes' field
|
||||
data.notes = $(modal + ' #notes').val();
|
||||
|
||||
var entries = [];
|
||||
|
||||
items.forEach(function(item) {
|
||||
entries.push({
|
||||
pk: item.pk,
|
||||
quantity: item.quantity,
|
||||
});
|
||||
});
|
||||
|
||||
data.items = entries;
|
||||
|
||||
inventreePut(
|
||||
'{% url 'api-stock-transfer' %}',
|
||||
data,
|
||||
{
|
||||
method: 'POST',
|
||||
success: function(response, status) {
|
||||
// Hide the modal
|
||||
$(modal).modal('hide');
|
||||
if (status == 'success' && 'success' in response) {
|
||||
|
||||
showAlertOrCache('alert-success', response.success, true);
|
||||
location.reload();
|
||||
} else {
|
||||
showAlertOrCache('alert-success', 'Error transferring stock', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
onScan: function(barcode) {
|
||||
enableBarcodeInput(modal, false);
|
||||
inventreePut(
|
||||
'/api/barcode/',
|
||||
{
|
||||
barcode: barcode,
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
error: function() {
|
||||
enableBarcodeInput(modal, true);
|
||||
showBarcodeMessage(modal, '{% trans "Server error" %}');
|
||||
},
|
||||
success: function(response, status) {
|
||||
|
||||
enableBarcodeInput(modal, true);
|
||||
|
||||
if (status == 'success') {
|
||||
if ('stockitem' in response) {
|
||||
stockitem = response.stockitem;
|
||||
|
||||
var duplicate = false;
|
||||
|
||||
items.forEach(function(item) {
|
||||
if (item.pk == stockitem.pk) {
|
||||
duplicate = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (duplicate) {
|
||||
showBarcodeMessage(modal, "{% trans "Stock Item already scanned" %}", "warning");
|
||||
} else {
|
||||
|
||||
if (stockitem.location == location_id) {
|
||||
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");
|
||||
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Barcode does not match a stock item
|
||||
showBarcodeMessage(modal, "{% trans "Barcode does not match Stock Item" %}", "warning");
|
||||
}
|
||||
} else {
|
||||
showInvalidResponseError(modal, response, status);
|
||||
}
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user