mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-31 05:05:42 +00:00 
			
		
		
		
	Merge pull request #1260 from SchrodingersGat/barcode-scanner
Barcode scanner
This commit is contained in:
		| @@ -90,7 +90,7 @@ class BarcodeScan(APIView): | ||||
|  | ||||
|             if loc is not None: | ||||
|                 response['stocklocation'] = plugin.renderStockLocation(loc) | ||||
|                 response['url'] = reverse('location-detail', kwargs={'pk': loc.id}) | ||||
|                 response['url'] = reverse('stock-location-detail', kwargs={'pk': loc.id}) | ||||
|                 match_found = True | ||||
|  | ||||
|             # Try to associate with a part | ||||
|   | ||||
| @@ -71,6 +71,13 @@ class InvenTreeSetting(models.Model): | ||||
|             'choices': djmoney.settings.CURRENCY_CHOICES, | ||||
|         }, | ||||
|  | ||||
|         'BARCODE_ENABLE': { | ||||
|             'name': _('Barcode Support'), | ||||
|             'description': _('Enable barcode scanner support'), | ||||
|             'default': True, | ||||
|             'validator': bool, | ||||
|         }, | ||||
|  | ||||
|         'PART_IPN_REGEX': { | ||||
|             'name': _('IPN Regex'), | ||||
|             'description': _('Regular expression pattern for matching Part IPN') | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -44,6 +44,8 @@ | ||||
|                 <span id='part-star-icon' class='fas fa-star {% if starred %}icon-yellow{% endif %}'/> | ||||
|             </button> | ||||
|  | ||||
|             {% settings_value 'BARCODE_ENABLE' as barcodes %} | ||||
|             {% if barcodes %} | ||||
|             <!-- Barcode actions menu --> | ||||
|             <div class='btn-group'> | ||||
|                 <button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button> | ||||
| @@ -52,6 +54,7 @@ | ||||
|                     <li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li> | ||||
|                 </ul> | ||||
|             </div> | ||||
|             {% endif %} | ||||
|             {% if part.active %} | ||||
|             <button type='button' class='btn btn-default' id='price-button' title='{% trans "Show pricing information" %}'> | ||||
|                 <span id='part-price-icon' class='fas fa-dollar-sign'/> | ||||
|   | ||||
| @@ -121,12 +121,17 @@ class StockAdjust(APIView): | ||||
|     - StockAdd: add stock items | ||||
|     - StockRemove: remove stock items | ||||
|     - StockTransfer: transfer stock items | ||||
|  | ||||
|     # TODO - This needs serious refactoring!!! | ||||
|  | ||||
|     """ | ||||
|  | ||||
|     permission_classes = [ | ||||
|         permissions.IsAuthenticated, | ||||
|     ] | ||||
|  | ||||
|     allow_missing_quantity = False | ||||
|  | ||||
|     def get_items(self, request): | ||||
|         """ | ||||
|         Return a list of items posted to the endpoint. | ||||
| @@ -157,10 +162,13 @@ class StockAdjust(APIView): | ||||
|             except (ValueError, StockItem.DoesNotExist): | ||||
|                 raise ValidationError({'pk': 'Each entry must contain a valid pk field'}) | ||||
|  | ||||
|             if self.allow_missing_quantity and 'quantity' not in entry: | ||||
|                 entry['quantity'] = item.quantity | ||||
|  | ||||
|             try: | ||||
|                 quantity = Decimal(str(entry.get('quantity', None))) | ||||
|             except (ValueError, TypeError, InvalidOperation): | ||||
|                 raise ValidationError({'quantity': 'Each entry must contain a valid quantity field'}) | ||||
|                 raise ValidationError({'quantity': "Each entry must contain a valid quantity value"}) | ||||
|  | ||||
|             if quantity < 0: | ||||
|                 raise ValidationError({'quantity': 'Quantity field must not be less than zero'}) | ||||
| @@ -234,6 +242,8 @@ class StockTransfer(StockAdjust): | ||||
|     API endpoint for performing stock movements | ||||
|     """ | ||||
|  | ||||
|     allow_missing_quantity = True | ||||
|  | ||||
|     def post(self, request, *args, **kwargs): | ||||
|  | ||||
|         self.get_items(request) | ||||
|   | ||||
| @@ -120,6 +120,8 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} | ||||
| </div> | ||||
|  | ||||
| <div class='btn-group action-buttons' role='group'> | ||||
|     {% settings_value 'BARCODE_ENABLE' as barcodes %} | ||||
|     {% if barcodes %} | ||||
|     <!-- Barcode actions menu --> | ||||
|     <div class='btn-group'> | ||||
|         <button id='barcode-options' title='{% trans "Barcode actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-qrcode'></span> <span class='caret'></span></button> | ||||
| @@ -127,24 +129,26 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} | ||||
|             <li><a href='#' id='show-qr-code'><span class='fas fa-qrcode'></span> {% trans "Show QR Code" %}</a></li> | ||||
|             {% if roles.stock.change %} | ||||
|             {% if item.uid %} | ||||
|             <li><a href='#' id='unlink-barcode'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li> | ||||
|                 {% else %} | ||||
|                 <li><a href='#' id='link-barcode'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li> | ||||
|                 {% endif %} | ||||
|                 {% endif %} | ||||
|             </ul> | ||||
|         </div> | ||||
|         <!-- Document / label menu --> | ||||
|         {% if item.has_labels or item.has_test_reports %} | ||||
|         <div class='btn-group'> | ||||
|             <button id='document-options' title='{% trans "Printing actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-print'></span> <span class='caret'></span></button> | ||||
|             <ul class='dropdown-menu' role='menu'> | ||||
|                 {% if item.has_labels %} | ||||
|                 <li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li> | ||||
|                 {% endif %} | ||||
|                 {% if item.has_test_reports %} | ||||
|                 <li><a href='#' id='stock-test-report'><span class='fas fa-file-pdf'></span> {% trans "Test Report" %}</a></li> | ||||
|                 {% endif %} | ||||
|             <li><a href='#' id='barcode-unlink'><span class='fas fa-unlink'></span> {% trans "Unlink Barcode" %}</a></li> | ||||
|             {% else %} | ||||
|             <li><a href='#' id='barcode-link'><span class='fas fa-link'></span> {% trans "Link Barcode" %}</a></li> | ||||
|             {% endif %} | ||||
|             <li><a href='#' id='barcode-scan-into-location'><span class='fas fa-sitemap'></span> {% trans "Scan to Location" %}</a></li> | ||||
|             {% endif %} | ||||
|         </ul> | ||||
|     </div> | ||||
|     {% endif %} | ||||
|     <!-- Document / label menu --> | ||||
|     {% if item.has_labels or item.has_test_reports %} | ||||
|     <div class='btn-group'> | ||||
|         <button id='document-options' title='{% trans "Printing actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-print'></span> <span class='caret'></span></button> | ||||
|         <ul class='dropdown-menu' role='menu'> | ||||
|             {% if item.has_labels %} | ||||
|             <li><a href='#' id='print-label'><span class='fas fa-tag'></span> {% trans "Print Label" %}</a></li> | ||||
|             {% endif %} | ||||
|             {% if item.has_test_reports %} | ||||
|             <li><a href='#' id='stock-test-report'><span class='fas fa-file-pdf'></span> {% trans "Test Report" %}</a></li> | ||||
|             {% endif %} | ||||
|         </ul> | ||||
|     </div> | ||||
|     {% endif %} | ||||
| @@ -447,14 +451,18 @@ $("#show-qr-code").click(function() { | ||||
|     }); | ||||
| }); | ||||
|  | ||||
| $("#link-barcode").click(function() { | ||||
| $("#barcode-link").click(function() { | ||||
|     linkBarcodeDialog({{ item.id }}); | ||||
| }); | ||||
|  | ||||
| $("#unlink-barcode").click(function() { | ||||
| $("#barcode-unlink").click(function() { | ||||
|     unlinkBarcode({{ item.id }}); | ||||
| }); | ||||
|  | ||||
| $("#barcode-scan-into-location").click(function() { | ||||
|     scanItemsIntoLocation([{{ item.id }}]); | ||||
| }); | ||||
|  | ||||
| {% if item.in_stock %} | ||||
|  | ||||
| $("#stock-assign-to-customer").click(function() { | ||||
|   | ||||
| @@ -37,6 +37,8 @@ | ||||
|                 </button> | ||||
|             {% endif %} | ||||
|         {% endif %}  | ||||
|         {% settings_value 'BARCODE_ENABLE' as barcodes %} | ||||
|         {% if barcodes %} | ||||
|         <!-- Barcode actions menu --> | ||||
|         {% if location %} | ||||
|         <div class='btn-group'> | ||||
| @@ -47,29 +49,30 @@ | ||||
|                 <li><a href='#' id='barcode-check-in'><span class='fas fa-arrow-right'></span> {% trans "Check-in Items" %}</a></li> | ||||
|             </ul> | ||||
|         </div> | ||||
|              <!-- Check permissions and owner --> | ||||
|             {% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %} | ||||
|                 {% if roles.stock.change %} | ||||
|                 <div class='btn-group'> | ||||
|                     <button id='stock-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button> | ||||
|                     <ul class='dropdown-menu' role='menu'> | ||||
|                         <li><a href='#' id='location-count'><span class='fas fa-clipboard-list'></span> | ||||
|                          {% trans "Count stock" %}</a></li> | ||||
|                     </ul> | ||||
|                 </div> | ||||
|                 {% endif %} | ||||
|                 {% if roles.stock_location.change %} | ||||
|                 <div class='btn-group'> | ||||
|                     <button id='location-actions' title='{% trans "Location actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle="dropdown"><span class='fas fa-sitemap'></span> <span class='caret'></span></button> | ||||
|                     <ul class='dropdown-menu' role='menu'> | ||||
|                         <li><a href='#' id='location-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit location" %}</a></li> | ||||
|                         {% if roles.stock.delete %} | ||||
|                         <li><a href='#' id='location-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete location" %}</a></li> | ||||
|                         {% endif %} | ||||
|                     </ul> | ||||
|                 </div> | ||||
|                 {% endif %} | ||||
|         {% endif %} | ||||
|         <!-- Check permissions and owner --> | ||||
|         {% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %} | ||||
|             {% if roles.stock.change %} | ||||
|             <div class='btn-group'> | ||||
|                 <button id='stock-actions' title='{% trans "Stock actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'><span class='fas fa-boxes'></span> <span class='caret'></span></button> | ||||
|                 <ul class='dropdown-menu' role='menu'> | ||||
|                     <li><a href='#' id='location-count'><span class='fas fa-clipboard-list'></span> | ||||
|                         {% trans "Count stock" %}</a></li> | ||||
|                 </ul> | ||||
|             </div> | ||||
|             {% endif %} | ||||
|             {% if roles.stock_location.change %} | ||||
|             <div class='btn-group'> | ||||
|                 <button id='location-actions' title='{% trans "Location actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle="dropdown"><span class='fas fa-sitemap'></span> <span class='caret'></span></button> | ||||
|                 <ul class='dropdown-menu' role='menu'> | ||||
|                     <li><a href='#' id='location-edit'><span class='fas fa-edit icon-green'></span> {% trans "Edit location" %}</a></li> | ||||
|                     {% if roles.stock.delete %} | ||||
|                     <li><a href='#' id='location-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete location" %}</a></li> | ||||
|                     {% endif %} | ||||
|                 </ul> | ||||
|             </div> | ||||
|             {% endif %} | ||||
|         {% endif %} | ||||
|         {% endif %} | ||||
|     </div> | ||||
| </div> | ||||
|   | ||||
| @@ -21,4 +21,12 @@ | ||||
|     </tbody> | ||||
| </table> | ||||
|  | ||||
| <h4>{% trans "Barcode Settings" %}</h4> | ||||
| <table class='table table-striped table-condensed'> | ||||
|     {% include "InvenTree/settings/header.html" %} | ||||
|     <tbody> | ||||
|         {% include "InvenTree/settings/setting.html" with key="BARCODE_ENABLE" icon="fa-qrcode" %} | ||||
|     </tbody> | ||||
| </table> | ||||
|  | ||||
| {% endblock %} | ||||
| @@ -1,12 +1,14 @@ | ||||
| {% load i18n %} | ||||
|  | ||||
| function makeBarcodeInput(placeholderText='') { | ||||
| 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> | ||||
| @@ -17,7 +19,7 @@ function makeBarcodeInput(placeholderText='') { | ||||
|                 </span> | ||||
|                 <input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'> | ||||
|             </div> | ||||
|             <div id='hint_barcode_data' class='help-block'>{% trans "Enter barcode data" %}</div> | ||||
|             <div id='hint_barcode_data' class='help-block'>${hintText}</div> | ||||
|         </div> | ||||
|     </div> | ||||
|     `; | ||||
| @@ -25,6 +27,81 @@ function makeBarcodeInput(placeholderText='') { | ||||
|     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') { | ||||
|  | ||||
| @@ -43,12 +120,6 @@ function showInvalidResponseError(modal, response, status) { | ||||
| } | ||||
|  | ||||
|  | ||||
| function clearBarcodeError(modal, message) { | ||||
|  | ||||
|     $(modal + ' #barcode-error-message').html(''); | ||||
| } | ||||
|  | ||||
|  | ||||
| function enableBarcodeInput(modal, enabled=true) { | ||||
|  | ||||
|     var barcode = $(modal + ' #barcode'); | ||||
| @@ -87,9 +158,7 @@ function barcodeDialog(title, options={}) { | ||||
|  | ||||
|         if (barcode && barcode.length > 0) { | ||||
|              | ||||
|             if (options.onScan) { | ||||
|                 options.onScan(barcode); | ||||
|             } | ||||
|             postBarcodeData(barcode, options); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -189,40 +258,20 @@ function barcodeScanDialog() { | ||||
|     barcodeDialog( | ||||
|         "Scan Barcode", | ||||
|         { | ||||
|             onScan: function(barcode) { | ||||
|                 enableBarcodeInput(modal, false); | ||||
|                 inventreePut( | ||||
|                     '/api/barcode/', | ||||
|                     { | ||||
|                         barcode: barcode, | ||||
|                     }, | ||||
|                     { | ||||
|                         method: 'POST', | ||||
|                         success: function(response, status) { | ||||
|  | ||||
|                             enableBarcodeInput(modal, true); | ||||
|  | ||||
|                             if (status == 'success') { | ||||
|                                  | ||||
|                                 if ('success' in response) { | ||||
|                                     if ('url' in response) { | ||||
|                                         // Redirect to the URL! | ||||
|                                         $(modal).modal('hide'); | ||||
|                                         window.location.href = response.url; | ||||
|                                     } | ||||
|  | ||||
|                                 } else if ('error' in response) { | ||||
|                                     showBarcodeMessage(modal, response.error, 'warning'); | ||||
|                                 } else { | ||||
|                                     showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", 'warning'); | ||||
|                                 } | ||||
|                             } else { | ||||
|                                 showInvalidResponseError(modal, response, status); | ||||
|                             }       | ||||
|                         }, | ||||
|                     }, | ||||
|                 ); | ||||
|             },  | ||||
|             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' | ||||
|                     ); | ||||
|                 } | ||||
|             }  | ||||
|         }, | ||||
|     );  | ||||
| } | ||||
| @@ -238,37 +287,14 @@ function linkBarcodeDialog(stockitem, options={}) { | ||||
|     barcodeDialog( | ||||
|         "{% trans 'Link Barcode to Stock Item' %}", | ||||
|         { | ||||
|             onScan: function(barcode) { | ||||
|                 enableBarcodeInput(modal, false); | ||||
|                 inventreePut( | ||||
|                     '/api/barcode/link/', | ||||
|                     { | ||||
|                         barcode: barcode, | ||||
|                         stockitem: stockitem, | ||||
|                     }, | ||||
|                     { | ||||
|                         method: 'POST', | ||||
|                         success: function(response, status) { | ||||
|             url: '/api/barcode/link/', | ||||
|             data: { | ||||
|                 stockitem: stockitem, | ||||
|             }, | ||||
|             onScan: function(response) { | ||||
|  | ||||
|                             enableBarcodeInput(modal, true); | ||||
|  | ||||
|                             if (status == 'success') { | ||||
|  | ||||
|                                 if ('success' in response) { | ||||
|                                     $(modal).modal('hide'); | ||||
|                                     location.reload(); | ||||
|                                 } else if ('error' in response) { | ||||
|                                     showBarcodeMessage(modal, response.error, 'warning'); | ||||
|                                 } else { | ||||
|                                     showBarcodeMessage(modal, "{% trans 'Unknown response from server' %}", warning); | ||||
|                                 } | ||||
|  | ||||
|                             } else { | ||||
|                                 showInvalidResponseError(modal, response, status); | ||||
|                             } | ||||
|                         }, | ||||
|                     }, | ||||
|                 ); | ||||
|                 $(modal).modal('hide'); | ||||
|                 location.reload(); | ||||
|             } | ||||
|         } | ||||
|     ); | ||||
| @@ -386,22 +412,10 @@ function barcodeCheckIn(location_id, options={}) { | ||||
|     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>`; | ||||
|     var extra = makeNotesField(); | ||||
|  | ||||
|     barcodeDialog( | ||||
|         "{% trans "Check Stock Items into Location" %}", | ||||
|         '{% trans "Check Stock Items into Location" %}', | ||||
|         { | ||||
|             headerContent: table, | ||||
|             preShow: function() { | ||||
| @@ -414,7 +428,6 @@ function barcodeCheckIn(location_id, options={}) { | ||||
|             extraFields: extra, | ||||
|             onSubmit: function() { | ||||
|  | ||||
|  | ||||
|                 // Called when the 'check-in' button is pressed | ||||
|                  | ||||
|                 var data = {location: location_id}; | ||||
| @@ -434,7 +447,7 @@ function barcodeCheckIn(location_id, options={}) { | ||||
|                 data.items = entries; | ||||
|  | ||||
|                 inventreePut( | ||||
|                     '{% url 'api-stock-transfer' %}', | ||||
|                     "{% url 'api-stock-transfer' %}", | ||||
|                     data, | ||||
|                     { | ||||
|                         method: 'POST', | ||||
| @@ -446,69 +459,154 @@ function barcodeCheckIn(location_id, options={}) { | ||||
|                                 showAlertOrCache('alert-success', response.success, true); | ||||
|                                 location.reload(); | ||||
|                             } else { | ||||
|                                 showAlertOrCache('alert-success', 'Error transferring stock', false); | ||||
|                                 showAlertOrCache('alert-success', '{% trans "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) { | ||||
|             onScan: function(response) { | ||||
|                 if ('stockitem' in response) { | ||||
|                     stockitem = response.stockitem; | ||||
|  | ||||
|                             enableBarcodeInput(modal, true); | ||||
|                     var duplicate = false; | ||||
|  | ||||
|                             if (status == 'success') { | ||||
|                                 if ('stockitem' in response) { | ||||
|                                     stockitem = response.stockitem; | ||||
|                     items.forEach(function(item) { | ||||
|                         if (item.pk == stockitem.pk) { | ||||
|                             duplicate = true; | ||||
|                         } | ||||
|                     }); | ||||
|  | ||||
|                                     var duplicate = false; | ||||
|                     if (duplicate) { | ||||
|                         showBarcodeMessage(modal, '{% trans "Stock Item already scanned" %}', "warning"); | ||||
|                     } else { | ||||
|  | ||||
|                                     items.forEach(function(item) { | ||||
|                                         if (item.pk == stockitem.pk) { | ||||
|                                             duplicate = true; | ||||
|                                         } | ||||
|                                     }); | ||||
|                         if (stockitem.location == location_id) { | ||||
|                             showBarcodeMessage(modal, '{% trans "Stock Item already in this location" %}'); | ||||
|                             return; | ||||
|                         } | ||||
|  | ||||
|                                     if (duplicate) { | ||||
|                                         showBarcodeMessage(modal, "{% trans "Stock Item already scanned" %}", "warning"); | ||||
|                                     } else { | ||||
|                         // Add this stock item to the list | ||||
|                         items.push(stockitem); | ||||
|  | ||||
|                                         if (stockitem.location == location_id) { | ||||
|                                             showBarcodeMessage(modal, "{% trans "Stock Item already in this location" %}"); | ||||
|                                             return; | ||||
|                                         } | ||||
|                         showBarcodeMessage(modal, '{% trans "Added stock item" %}', "success"); | ||||
|  | ||||
|                                         // Add this stock item to the list | ||||
|                                         items.push(stockitem); | ||||
|                         reloadTable(); | ||||
|                     } | ||||
|  | ||||
|                                         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); | ||||
|                             } | ||||
|                         }, | ||||
|                     }, | ||||
|                 ); | ||||
|                 } 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", | ||||
|                     ); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     ) | ||||
| } | ||||
| @@ -6,6 +6,7 @@ | ||||
|  * Requires api.js to be loaded first | ||||
|  */ | ||||
|  | ||||
| {% settings_value 'BARCODE_ENABLE' as barcodes %} | ||||
|  | ||||
| function stockStatusCodes() { | ||||
|     return [ | ||||
| @@ -635,6 +636,9 @@ function loadStockTable(table, options) { | ||||
|         table, | ||||
|         [ | ||||
|             '#stock-print-options', | ||||
|             {% if barcodes %} | ||||
|             '#stock-barcode-options', | ||||
|             {% endif %} | ||||
|             '#stock-options', | ||||
|         ] | ||||
|     ); | ||||
| @@ -700,6 +704,20 @@ function loadStockTable(table, options) { | ||||
|         printTestReports(items); | ||||
|     }) | ||||
|  | ||||
|     {% if barcodes %} | ||||
|     $('#multi-item-barcode-scan-into-location').click(function() {         | ||||
|         var selections = $('#stock-table').bootstrapTable('getSelections'); | ||||
|  | ||||
|         var items = []; | ||||
|  | ||||
|         selections.forEach(function(item) { | ||||
|             items.push(item.pk); | ||||
|         }) | ||||
|  | ||||
|         scanItemsIntoLocation(items); | ||||
|     }); | ||||
|     {% endif %} | ||||
|  | ||||
|     $('#multi-item-stocktake').click(function() { | ||||
|         stockAdjustment('count'); | ||||
|     }); | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| {% load i18n %} | ||||
| {% load inventree_extras %} | ||||
|  | ||||
| {% settings_value 'BARCODE_ENABLE' as barcodes %} | ||||
|  | ||||
| {% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} | ||||
| {% if owner_control.value == "True" %} | ||||
|     {% authorized_owners location.owner as owners %} | ||||
| @@ -19,6 +21,17 @@ | ||||
|                 <span class='fas fa-plus-circle'></span> | ||||
|             </button> | ||||
|             {% endif %} | ||||
|             {% if barcodes %} | ||||
|             <!-- Barcode actions menu --> | ||||
|             <div class='btn-group'> | ||||
|                 <button id='stock-barcode-options' class='btn btn-primary dropdown-toggle' type='button' data-toggle='dropdown' title='{% trans "Barcode Actions" %}'> | ||||
|                     <span class='fas fa-qrcode'></span> <span class='caret'></span> | ||||
|                 </button> | ||||
|                 <ul class='dropdown-menu'> | ||||
|                     <li><a href='#' id='multi-item-barcode-scan-into-location' title='{% trans "Scan to Location" %}'><span class='fas fa-sitemap'></span> {% trans "Scan to Location" %}</a></li> | ||||
|                 </ul> | ||||
|             </div> | ||||
|             {% endif %} | ||||
|             <div class='btn-group'> | ||||
|                 <button id='stock-print-options' class='btn btn-primary dropdown-toggle' type='button' data-toggle="dropdown" title='{% trans "Printing Actions" %}'> | ||||
|                     <span class='fas fa-print'></span> <span class='caret'></span> | ||||
|   | ||||
		Reference in New Issue
	
	Block a user