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) {
|
function modalSetButtonText(modal, submit_text, close_text) {
|
||||||
/* Set the button text for a modal form
|
/* Set the button text for a modal form
|
||||||
*
|
*
|
||||||
* submit_text - text for the form submit button
|
* submit_text - text for the form submit button
|
||||||
* close_text - text for the form dismiss button
|
* close_text - text for the form dismiss button
|
||||||
*/
|
*/
|
||||||
$(modal).find("#modal-form-submit").html(submit_text);
|
modalSetSubmitText(modal, submit_text);
|
||||||
$(modal).find("#modal-form-close").html(close_text);
|
modalSetCloseText(modal, close_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -120,6 +120,12 @@
|
|||||||
inventreeDel('show-part-locs');
|
inventreeDel('show-part-locs');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{% if location %}
|
||||||
|
$("#barcode-check-in").click(function() {
|
||||||
|
barcodeCheckIn({{ location.id }});
|
||||||
|
});
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
$("#stock-export").click(function() {
|
$("#stock-export").click(function() {
|
||||||
launchModalForm("{% url 'stock-export-options' %}", {
|
launchModalForm("{% url 'stock-export-options' %}", {
|
||||||
submit_text: "Export",
|
submit_text: "Export",
|
||||||
|
@ -1,25 +1,5 @@
|
|||||||
{% load i18n %}
|
{% 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='') {
|
function makeBarcodeInput(placeholderText='') {
|
||||||
/*
|
/*
|
||||||
* Generate HTML for a barcode input
|
* Generate HTML for a barcode input
|
||||||
@ -28,8 +8,6 @@ function makeBarcodeInput(placeholderText='') {
|
|||||||
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
||||||
|
|
||||||
var html = `
|
var html = `
|
||||||
<div id='barcode-error-message'></div>
|
|
||||||
<form class='js-modal-form' method='post'>
|
|
||||||
<div class='form-group'>
|
<div class='form-group'>
|
||||||
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
||||||
<div class='controls'>
|
<div class='controls'>
|
||||||
@ -42,14 +20,13 @@ function makeBarcodeInput(placeholderText='') {
|
|||||||
<div id='hint_barcode_data' class='help-block'>{% trans "Enter barcode data" %}</div>
|
<div id='hint_barcode_data' class='help-block'>{% trans "Enter barcode data" %}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function showBarcodeError(modal, message, style='danger') {
|
function showBarcodeMessage(modal, message, style='danger') {
|
||||||
|
|
||||||
var html = `<div class='alert alert-block alert-${style}'>`;
|
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);
|
$(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) {
|
function clearBarcodeError(modal, message) {
|
||||||
|
|
||||||
$(modal + ' #barcode-error-message').html('');
|
$(modal + ' #barcode-error-message').html('');
|
||||||
@ -73,6 +56,8 @@ function enableBarcodeInput(modal, enabled=true) {
|
|||||||
barcode.prop('disabled', !enabled);
|
barcode.prop('disabled', !enabled);
|
||||||
|
|
||||||
modalEnable(modal, enabled);
|
modalEnable(modal, enabled);
|
||||||
|
|
||||||
|
barcode.focus();
|
||||||
}
|
}
|
||||||
|
|
||||||
function getBarcodeData(modal) {
|
function getBarcodeData(modal) {
|
||||||
@ -86,7 +71,7 @@ function getBarcodeData(modal) {
|
|||||||
el.val('');
|
el.val('');
|
||||||
el.focus();
|
el.focus();
|
||||||
|
|
||||||
return barcode;
|
return barcode.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -97,54 +82,87 @@ function barcodeDialog(title, options={}) {
|
|||||||
|
|
||||||
var modal = '#modal-form';
|
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).on('shown.bs.modal', function() {
|
||||||
$(modal + ' .modal-form-content').scrollTop(0);
|
$(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
|
// Ensure the barcode field has focus
|
||||||
$(modal + ' #barcode').focus();
|
barcode.focus();
|
||||||
|
|
||||||
var form = $(modal).find('.js-modal-form');
|
var form = $(modal).find('.js-modal-form');
|
||||||
|
|
||||||
// Override form submission
|
// Override form submission
|
||||||
form.submit(function() {
|
form.submit(function() {
|
||||||
|
|
||||||
var barcode = getBarcodeData(modal);
|
|
||||||
|
|
||||||
if (options.submit) {
|
|
||||||
options.submit(barcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Callback for when the "submit" button is pressed on the modal
|
||||||
modalSubmit(modal, function() {
|
modalSubmit(modal, function() {
|
||||||
|
if (options.onSubmit) {
|
||||||
var barcode = getBarcodeData(modal);
|
options.onSubmit();
|
||||||
|
|
||||||
if (options.submit) {
|
|
||||||
options.submit(barcode);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (options.onShow) {
|
||||||
|
options.onShow();
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
modalSetTitle(modal, title);
|
modalSetTitle(modal, title);
|
||||||
modalShowSubmitButton(modal, true);
|
|
||||||
|
if (options.onSubmit) {
|
||||||
|
modalShowSubmitButton(modal, true);
|
||||||
|
} else {
|
||||||
|
modalShowSubmitButton(modal, false);
|
||||||
|
}
|
||||||
|
|
||||||
var content = '';
|
var content = '';
|
||||||
|
|
||||||
if (options.headerContent) {
|
|
||||||
content += options.headerContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
content += `<div class='alert alert-info alert-block'>{% trans "Scan barcode data below" %}</div>`;
|
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();
|
content += makeBarcodeInput();
|
||||||
|
|
||||||
if (options.footerContent) {
|
if (options.extraFields) {
|
||||||
content += options.footerContent;
|
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);
|
modalSetContent(modal, content);
|
||||||
|
|
||||||
$(modal).modal({
|
$(modal).modal({
|
||||||
@ -152,6 +170,10 @@ function barcodeDialog(title, options={}) {
|
|||||||
keyboard: false,
|
keyboard: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (options.preShow) {
|
||||||
|
options.preShow();
|
||||||
|
}
|
||||||
|
|
||||||
$(modal).modal('show');
|
$(modal).modal('show');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,7 +189,7 @@ function barcodeScanDialog() {
|
|||||||
barcodeDialog(
|
barcodeDialog(
|
||||||
"Scan Barcode",
|
"Scan Barcode",
|
||||||
{
|
{
|
||||||
submit: function(barcode) {
|
onScan: function(barcode) {
|
||||||
enableBarcodeInput(modal, false);
|
enableBarcodeInput(modal, false);
|
||||||
inventreePut(
|
inventreePut(
|
||||||
'/api/barcode/',
|
'/api/barcode/',
|
||||||
@ -190,12 +212,12 @@ function barcodeScanDialog() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if ('error' in response) {
|
} else if ('error' in response) {
|
||||||
showBarcodeError(modal, response.error, 'warning');
|
showBarcodeMessage(modal, response.error, 'warning');
|
||||||
} else {
|
} else {
|
||||||
showBarcodeError(modal, "{% trans 'Unknown response from server' %}", 'warning');
|
showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", 'warning');
|
||||||
}
|
}
|
||||||
} else {
|
} 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';
|
var modal = '#modal-form';
|
||||||
|
|
||||||
barcodeDialog(
|
barcodeDialog(
|
||||||
"Link Barcode",
|
"{% trans 'Link Barcode to Stock Item' %}",
|
||||||
{
|
{
|
||||||
submit: function(barcode) {
|
onScan: function(barcode) {
|
||||||
enableBarcodeInput(modal, false);
|
enableBarcodeInput(modal, false);
|
||||||
inventreePut(
|
inventreePut(
|
||||||
'/api/barcode/link/',
|
'/api/barcode/link/',
|
||||||
@ -228,8 +250,6 @@ function linkBarcodeDialog(stockitem, options={}) {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
success: function(response, status) {
|
success: function(response, status) {
|
||||||
|
|
||||||
console.log(response);
|
|
||||||
|
|
||||||
enableBarcodeInput(modal, true);
|
enableBarcodeInput(modal, true);
|
||||||
|
|
||||||
if (status == 'success') {
|
if (status == 'success') {
|
||||||
@ -238,13 +258,13 @@ function linkBarcodeDialog(stockitem, options={}) {
|
|||||||
$(modal).modal('hide');
|
$(modal).modal('hide');
|
||||||
location.reload();
|
location.reload();
|
||||||
} else if ('error' in response) {
|
} else if ('error' in response) {
|
||||||
showBarcodeError(modal, response.error, 'warning');
|
showBarcodeMessage(modal, response.error, 'warning');
|
||||||
} else {
|
} else {
|
||||||
showBarcodeError(modal, "{% trans 'Unknown response from server' %}", warning);
|
showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", warning);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} 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