{% 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 = `
${hintText}
`; return html; } function makeNotesField(options={}) { var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}'; var placeholder = options.placeholder || '{% trans "Enter notes" %}'; return `
${tooltip}
`; } /* * 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 = `
`; html += message; html += "
"; $(modal + ' #barcode-error-message').html(html); } function showInvalidResponseError(modal, response, status) { showBarcodeMessage(modal, `{% trans "Invalid server response" %}
{% 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 += `
{% trans "Scan barcode data below" %}
`; content += `
`; content += `
`; // Optional content before barcode input content += `
`; content += options.headerContent || ''; content += `
`; content += makeBarcodeInput(); if (options.extraFields) { content += options.extraFields; } content += `
`; // Optional content after barcode input content += `'; 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 = `{% trans "Unlink Barcode" %}
`; 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 = ` `; items.forEach(function(item) { html += ` `; }); html += `
{% trans "Part" %} {% trans "Location" %} {% trans "Quantity" %}
${imageHoverIcon(item.part_detail.thumbnail)} ${item.part_detail.name} ${item.location_detail.name} ${item.quantity} ${makeIconButton('fa-times-circle icon-red', 'button-item-remove', item.pk, '{% trans "Remove stock item" %}')}
`; 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 = `
`; // 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 = `
`; 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(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", ); } } } ) }