mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 13:15:43 +00:00 
			
		
		
		
	Transfer stock items into a stock location using barcode scanning
This commit is contained in:
		| @@ -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); | ||||
|     modalShowSubmitButton(modal, true); | ||||
|  | ||||
|     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); | ||||
|                             } | ||||
|                         }, | ||||
|                     }, | ||||
|                 ); | ||||
|             }, | ||||
|         } | ||||
|     ); | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user