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