InvenTree/InvenTree/templates/js/barcode.js
2021-05-06 14:35:23 +02:00

612 lines
17 KiB
JavaScript

{% load i18n %}
function makeBarcodeInput(placeholderText='', hintText='') {
/*
* Generate HTML for a barcode input
*/
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
hintText = hintText || '{% trans "Enter barcode data" %}';
var html = `
<div class='form-group'>
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
<div class='controls'>
<div class='input-group'>
<span class='input-group-addon'>
<span class='fas fa-qrcode'></span>
</span>
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
</div>
<div id='hint_barcode_data' class='help-block'>${hintText}</div>
</div>
</div>
`;
return html;
}
function makeNotesField(options={}) {
var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}';
var placeholder = options.placeholder || '{% trans "Enter notes" %}';
return `
<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='${placeholder}'>
</div>
<div id='hint_notes' class='help_block'>${tooltip}</div>
</div>
</div>`;
}
/*
* POST data to the server, and handle standard responses.
*/
function postBarcodeData(barcode_data, options={}) {
var modal = options.modal || '#modal-form';
var url = options.url || '/api/barcode/';
var data = options.data || {};
data.barcode = barcode_data;
inventreePut(
url,
data,
{
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 ('success' in response) {
if (options.onScan) {
options.onScan(response);
}
} else if ('error' in response) {
showBarcodeMessage(
modal,
response.error,
'warning'
);
} else {
showBarcodeMessage(
modal,
'{% trans "Unknown response from server" %}',
'warning'
);
}
} else {
// Invalid response returned from server
showInvalidResponseError(modal, response, status);
}
}
}
)
}
function showBarcodeMessage(modal, message, style='danger') {
var html = `<div class='alert alert-block alert-${style}'>`;
html += message;
html += "</div>";
$(modal + ' #barcode-error-message').html(html);
}
function showInvalidResponseError(modal, response, status) {
showBarcodeMessage(modal, `{% trans "Invalid server response" %}<br>{% trans "Status" %}: '${status}'`);
}
function enableBarcodeInput(modal, enabled=true) {
var barcode = $(modal + ' #barcode');
barcode.prop('disabled', !enabled);
modalEnable(modal, enabled);
barcode.focus();
}
function getBarcodeData(modal) {
modal = modal || '#modal-form';
var el = $(modal + ' #barcode');
var barcode = el.val();
el.val('');
el.focus();
return barcode.trim();
}
function barcodeDialog(title, options={}) {
/*
* Handle a barcode display dialog.
*/
var modal = '#modal-form';
function sendBarcode() {
var barcode = getBarcodeData(modal);
if (barcode && barcode.length > 0) {
postBarcodeData(barcode, options);
}
}
$(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
barcode.focus();
var form = $(modal).find('.js-modal-form');
// Override form submission
form.submit(function() {
return false;
});
// Callback for when the "submit" button is pressed on the modal
modalSubmit(modal, function() {
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 = '';
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.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({
backdrop: 'static',
keyboard: false,
});
if (options.preShow) {
options.preShow();
}
$(modal).modal('show');
}
function barcodeScanDialog() {
/*
* Perform a barcode scan,
* and (potentially) redirect the browser
*/
var modal = '#modal-form';
barcodeDialog(
"Scan Barcode",
{
onScan: function(response) {
if ('url' in response) {
$(modal).modal('hide');
// Redirect to the URL!
window.location.href = response.url;
} else {
showBarcodeMessage(
modal,
'{% trans "No URL in response" %}',
'warning'
);
}
}
},
);
}
/*
* Dialog for linking a particular barcode to a stock item.
*/
function linkBarcodeDialog(stockitem, options={}) {
var modal = '#modal-form';
barcodeDialog(
"{% trans 'Link Barcode to Stock Item' %}",
{
url: '/api/barcode/link/',
data: {
stockitem: stockitem,
},
onScan: function(response) {
$(modal).modal('hide');
location.reload();
}
}
);
}
/*
* Remove barcode association from a device.
*/
function unlinkBarcode(stockitem) {
var html = `<b>{% trans "Unlink Barcode" %}</b><br>`;
html += "{% trans 'This will remove the association between this stock item and the barcode' %}";
showQuestionDialog(
"{% trans 'Unlink Barcode' %}",
html,
{
accept_text: "{% trans 'Unlink' %}",
accept: function() {
inventreePut(
`/api/stock/${stockitem}/`,
{
// Clear the UID field
uid: '',
},
{
method: 'PATCH',
success: function(response, status) {
location.reload();
},
},
);
},
}
);
}
/*
* 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').unbind('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').unbind('click').on('mouseup', function() {
var pk = $(this).attr('pk');
var match = false;
for (var ii = 0; ii < items.length; ii++) {
if (pk.toString() == items[ii].pk.toString()) {
items.splice(ii, 1);
match = true;
break;
}
}
if (match) {
reloadTable();
}
return false;
});
}
var table = `<div class='container' id='items-table-div' style='width: 80%; float: left;'></div>`;
// Extra form fields
var extra = makeNotesField();
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', '{% trans "Error transferring stock" %}', false);
}
}
}
);
},
onScan: function(response) {
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");
}
},
}
);
}
/*
* 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 = `
<div id='header-div'>
</div>
`;
function updateLocationInfo(location) {
var div = $(modal + ' #header-div');
if (stock_location && stock_location.pk) {
div.html(`
<div class='alert alert-block alert-info'>
<b>{% trans "Location" %}</b></br>
${stock_location.name}<br>
<i>${stock_location.description}</i>
</div>
`);
} 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(response) {
updateLocationInfo(null);
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",
);
}
}
}
)
}