From bc31cc550e01a1c30696c3d8da35e92a8d0ece4b Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Wed, 10 Jun 2020 15:27:30 +1000 Subject: [PATCH 01/24] Consolidate buttons for stock item --- InvenTree/InvenTree/static/css/inventree.css | 11 +++ .../stock/templates/stock/item_base.html | 89 ++++++++++--------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css index 7861be295a..c1ecc953a1 100644 --- a/InvenTree/InvenTree/static/css/inventree.css +++ b/InvenTree/InvenTree/static/css/inventree.css @@ -259,6 +259,16 @@ margin-left: 1px; } +.dropdown-buttons { + display: inline-block +} + +.dropdown-menu .open{ + z-index: 1000; + position: relative; + overflow: visible; +} + /* Styles for table buttons and filtering */ .button-toolbar .btn { margin-left: 1px; @@ -455,6 +465,7 @@ .media-body { padding-top: 10px; + overflow: visible; } .navigation { diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 399da8d2b8..86420201b3 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -67,55 +67,60 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} {% endif %} -
- {% include "qr_button.html" %} +
+ + +
+ +
+ + {% if item.in_stock %} - {% if not item.serialized %} - - - - {% if item.part.trackable %} - - {% endif %} - {% endif %} - {% if item.part.salable %} - - {% endif %} - - - {% endif %} - {% if item.part.has_variants %} - + + {% endif %} + + {% if item.part.has_test_report_templates %} {% endif %} - - {% if item.can_delete %} - - {% endif %}
{% endblock %} From 290c0eb225eb0ccf4b35137a6052d50a4e5082d3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 09:56:59 +1000 Subject: [PATCH 02/24] Add barcode javascript file --- .../static/script/inventree/barcode.js | 22 +++++++++++++++++++ InvenTree/templates/base.html | 1 + 2 files changed, 23 insertions(+) create mode 100644 InvenTree/InvenTree/static/script/inventree/barcode.js diff --git a/InvenTree/InvenTree/static/script/inventree/barcode.js b/InvenTree/InvenTree/static/script/inventree/barcode.js new file mode 100644 index 0000000000..77ccf17c6e --- /dev/null +++ b/InvenTree/InvenTree/static/script/inventree/barcode.js @@ -0,0 +1,22 @@ + +/* + * Pass barcode data to the server. + */ +function scanBarcode(barcode, options={}) { + + console.log('Sending barcode data:'); + console.log(barcode); + + inventreePut( + '/api/barcode/', + { + 'barcode': barcode, + }, + { + method: 'POST', + success: function(response, status) { + console.log(response); + }, + } + ); +} \ No newline at end of file diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 63487eb625..745b93d5b2 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -107,6 +107,7 @@ InvenTree + From 0068cd9825223e0676d82d7b22bf014d7a9a26da Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 11:09:07 +1000 Subject: [PATCH 03/24] Refactor barcode endoint - Moved code into 'barcode' directory --- InvenTree/InvenTree/api.py | 66 --------- InvenTree/InvenTree/plugins.py | 44 ++++++ InvenTree/InvenTree/urls.py | 6 +- InvenTree/{plugins => }/barcode/__init__.py | 0 InvenTree/barcode/api.py | 132 ++++++++++++++++++ InvenTree/barcode/barcode.py | 126 +++++++++++++++++ InvenTree/barcode/plugins/digikey_barcode.py | 5 + .../barcode/plugins/inventree_barcode.py | 108 ++++++++++++++ InvenTree/plugins/barcode/barcode.py | 79 ----------- InvenTree/plugins/barcode/digikey.py | 8 -- InvenTree/plugins/barcode/inventree.py | 94 ------------- InvenTree/plugins/plugins.py | 21 --- 12 files changed, 419 insertions(+), 270 deletions(-) create mode 100644 InvenTree/InvenTree/plugins.py rename InvenTree/{plugins => }/barcode/__init__.py (100%) create mode 100644 InvenTree/barcode/api.py create mode 100644 InvenTree/barcode/barcode.py create mode 100644 InvenTree/barcode/plugins/digikey_barcode.py create mode 100644 InvenTree/barcode/plugins/inventree_barcode.py delete mode 100644 InvenTree/plugins/barcode/barcode.py delete mode 100644 InvenTree/plugins/barcode/digikey.py delete mode 100644 InvenTree/plugins/barcode/inventree.py diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index eb87b8f77a..44e7ec383f 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -20,9 +20,6 @@ from .version import inventreeVersion, inventreeInstanceName from plugins import plugins as inventree_plugins -# Load barcode plugins -print("Loading barcode plugins") -barcode_plugins = inventree_plugins.load_barcode_plugins() print("Loading action plugins") action_plugins = inventree_plugins.load_action_plugins() @@ -100,66 +97,3 @@ class ActionPluginView(APIView): 'error': _("No matching action found"), "action": action, }) - - -class BarcodePluginView(APIView): - """ - Endpoint for handling barcode scan requests. - - Barcode data are decoded by the client application, - and sent to this endpoint (as a JSON object) for validation. - - A barcode could follow the internal InvenTree barcode format, - or it could match to a third-party barcode format (e.g. Digikey). - - """ - - permission_classes = [ - permissions.IsAuthenticated, - ] - - def post(self, request, *args, **kwargs): - - response = {} - - barcode_data = request.data.get('barcode', None) - - print("Barcode data:") - print(barcode_data) - - if barcode_data is None: - response['error'] = _('No barcode data provided') - else: - # Look for a barcode plugin that knows how to handle the data - for plugin_class in barcode_plugins: - - # Instantiate the plugin with the provided plugin data - plugin = plugin_class(barcode_data) - - if plugin.validate(): - - # Plugin should return a dict response - response = plugin.decode() - - if type(response) is dict: - if 'success' not in response.keys() and 'error' not in response.keys(): - response['success'] = _('Barcode successfully decoded') - else: - response = { - 'error': _('Barcode plugin returned incorrect response') - } - - response['plugin'] = plugin.plugin_name() - response['hash'] = plugin.hash() - - break - - if 'error' not in response and 'success' not in response: - response = { - 'error': _('Unknown barcode format'), - } - - # Include the original barcode data - response['barcode_data'] = barcode_data - - return Response(response) diff --git a/InvenTree/InvenTree/plugins.py b/InvenTree/InvenTree/plugins.py new file mode 100644 index 0000000000..d0acb3fd8f --- /dev/null +++ b/InvenTree/InvenTree/plugins.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- + +import inspect +import importlib +import pkgutil + + +def iter_namespace(pkg): + + return pkgutil.iter_modules(pkg.__path__, pkg.__name__ + ".") + + +def get_modules(pkg): + # Return all modules in a given package + return [importlib.import_module(name) for finder, name, ispkg in iter_namespace(pkg)] + + +def get_classes(module): + # Return all classes in a given module + return inspect.getmembers(module, inspect.isclass) + + +def get_plugins(pkg, baseclass): + """ + Return a list of all modules under a given package. + + - Modules must be a subclass of the provided 'baseclass' + - Modules must have a non-empty PLUGIN_NAME parameter + """ + + plugins = [] + + modules = get_modules(pkg) + + # Iterate through each module in the package + for mod in modules: + print("mod:", mod) + # Iterate through each class in the module + for item in get_classes(mod): + plugin = item[1] + if issubclass(plugin, baseclass) and plugin.PLUGIN_NAME: + plugins.append(plugin) + + return plugins diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 711df33798..92ea257ce2 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -20,6 +20,7 @@ from stock.urls import stock_urls from build.urls import build_urls from order.urls import order_urls +from barcode.api import barcode_api_urls from common.api import common_api_urls from part.api import part_api_urls, bom_api_urls from company.api import company_api_urls @@ -37,13 +38,15 @@ from .views import IndexView, SearchView, DatabaseStatsView from .views import SettingsView, EditUserView, SetPasswordView from .views import DynamicJsView -from .api import InfoView, BarcodePluginView, ActionPluginView +from .api import InfoView +from .api import ActionPluginView from users.urls import user_urls admin.site.site_header = "InvenTree Admin" apipatterns = [ + url(r'^barcode/', include(barcode_api_urls)), url(r'^common/', include(common_api_urls)), url(r'^part/', include(part_api_urls)), url(r'^bom/', include(bom_api_urls)), @@ -56,7 +59,6 @@ apipatterns = [ url(r'^user/', include(user_urls)), # Plugin endpoints - url(r'^barcode/', BarcodePluginView.as_view(), name='api-barcode-plugin'), url(r'^action/', ActionPluginView.as_view(), name='api-action-plugin'), # InvenTree information endpoint diff --git a/InvenTree/plugins/barcode/__init__.py b/InvenTree/barcode/__init__.py similarity index 100% rename from InvenTree/plugins/barcode/__init__.py rename to InvenTree/barcode/__init__.py diff --git a/InvenTree/barcode/api.py b/InvenTree/barcode/api.py new file mode 100644 index 0000000000..bc89b395dd --- /dev/null +++ b/InvenTree/barcode/api.py @@ -0,0 +1,132 @@ +# -*- coding: utf-8 -*- + +import hashlib +from django.conf.urls import url + +from rest_framework.exceptions import ValidationError + +from rest_framework import permissions +from rest_framework.response import Response +from rest_framework.views import APIView + +import InvenTree.plugins as InvenTreePlugins + +from stock.models import StockItem +from stock.serializers import StockItemSerializer + +import barcode.plugins as BarcodePlugins +from barcode.barcode import BarcodePlugin, load_barcode_plugins, hash_barcode + + +class BarcodeScan(APIView): + """ + Endpoint for handling generic barcode scan requests. + + Barcode data are decoded by the client application, + and sent to this endpoint (as a JSON object) for validation. + + A barcode could follow the internal InvenTree barcode format, + or it could match to a third-party barcode format (e.g. Digikey). + + When a barcode is sent to the server, the following parameters must be provided: + + - barcode: The raw barcode data + + plugins: + Third-party barcode formats may be supported using 'plugins' + (more information to follow) + + hashing: + Barcode hashes are calculated using MD5 + + """ + + permission_classes = [ + permissions.IsAuthenticated, + ] + + def post(self, request, *args, **kwargs): + """ + Respond to a barcode POST request + """ + + data = request.data + + if 'barcode' not in data: + raise ValidationError({'barcode': 'Must provide barcode_data parameter'}) + + plugins = load_barcode_plugins() + + barcode_data = data.get('barcode') + + # Look for a barcode plugin which knows how to deal with this barcode + plugin = None + + for plugin_class in plugins: + plugin_instance = plugin_class(barcode_data) + + if plugin_instance.validate(): + plugin = plugin_instance + break + + match_found = False + response = {} + + response['barcode_data'] = barcode_data + + # A plugin has been found! + if plugin is not None: + + # Try to associate with a stock item + item = plugin.getStockItem() + + if item is not None: + response['stockitem'] = plugin.renderStockItem(item) + match_found = True + + # Try to associate with a stock location + loc = plugin.getStockLocation() + + if loc is not None: + response['stocklocation'] = plugin.renderStockLocation(loc) + match_found = True + + # Try to associate with a part + part = plugin.getPart() + + if part is not None: + response['part'] = plugin.renderPart(part) + match_found = True + + response['hash'] = plugin.hash() + response['plugin'] = plugin.name + + # No plugin is found! + # However, the hash of the barcode may still be associated with a StockItem! + else: + hash = hash_barcode(barcode_data) + + response['hash'] = hash + response['plugin'] = None + + # Try to look for a matching StockItem + try: + item = StockItem.objects.get(uid=hash) + serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True) + response['stockitem'] = serializer.data + match_found = True + except StockItem.DoesNotExist: + pass + + if not match_found: + response['error'] = 'No match found for barcode data' + else: + response['success'] = 'Match found for barcode data' + + return Response(response) + +barcode_api_urls = [ + + # Catch-all performs barcode 'scan' + url(r'^.*$', BarcodeScan.as_view(), name='api-barcode-plugin'), +] \ No newline at end of file diff --git a/InvenTree/barcode/barcode.py b/InvenTree/barcode/barcode.py new file mode 100644 index 0000000000..77fc64c4c6 --- /dev/null +++ b/InvenTree/barcode/barcode.py @@ -0,0 +1,126 @@ +# -*- coding: utf-8 -*- + +import hashlib +import json + +from InvenTree import plugins as InvenTreePlugins +from barcode import plugins as BarcodePlugins + +from stock.serializers import StockItemSerializer, LocationSerializer +from part.serializers import PartSerializer + + +def hash_barcode(barcode_data): + """ + Calculate an MD5 hash of barcode data + """ + + hash = hashlib.md5(str(barcode_data).encode()) + return str(hash.hexdigest()) + + +class BarcodePlugin: + """ + Base class for barcode handling. + Custom barcode plugins should extend this class as necessary. + """ + + # Override the barcode plugin name for each sub-class + PLUGIN_NAME = "" + + @property + def name(self): + return self.PLUGIN_NAME + + def __init__(self, barcode_data): + + self.data = barcode_data + + def getStockItem(self): + """ + Attempt to retrieve a StockItem associated with this barcode. + Default implementation returns None + """ + + return None + + def renderStockItem(self, item): + """ + Render a stock item to JSON response + """ + + serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True) + return serializer.data + + + def getStockLocation(self): + """ + Attempt to retrieve a StockLocation associated with this barcode. + Default implementation returns None + """ + + return None + + def renderStockLocation(self, loc): + """ + Render a stock location to a JSON response + """ + + serializer = LocationSerializer(loc) + return serializer.data + + def getPart(self): + """ + Attempt to retrieve a Part associated with this barcode. + Default implementation returns None + """ + + return None + + def renderPart(self, part): + """ + Render a part to JSON response + """ + + serializer = PartSerializer(part) + return serializer.data + + def hash(self): + """ + Calculate a hash for the barcode data. + This is supposed to uniquely identify the barcode contents, + at least within the bardcode sub-type. + + The default implementation simply returns an MD5 hash of the barcode data, + encoded to a string. + + This may be sufficient for most applications, but can obviously be overridden + by a subclass. + + """ + + return hash_barcode(self.data) + + def validate(self): + """ + Default implementation returns False + """ + return False + + +def load_barcode_plugins(): + """ + Function to load all barcode plugins + """ + + print("Loading barcode plugins") + + plugins = InvenTreePlugins.get_plugins(BarcodePlugins, BarcodePlugin) + + if len(plugins) > 0: + print("Discovered {n} plugins:".format(n=len(plugins))) + + for p in plugins: + print(" - {p}".format(p=p.PLUGIN_NAME)) + + return plugins \ No newline at end of file diff --git a/InvenTree/barcode/plugins/digikey_barcode.py b/InvenTree/barcode/plugins/digikey_barcode.py new file mode 100644 index 0000000000..3a65ff8b23 --- /dev/null +++ b/InvenTree/barcode/plugins/digikey_barcode.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- + +""" +DigiKey barcode decoding +""" diff --git a/InvenTree/barcode/plugins/inventree_barcode.py b/InvenTree/barcode/plugins/inventree_barcode.py new file mode 100644 index 0000000000..821cdc9c88 --- /dev/null +++ b/InvenTree/barcode/plugins/inventree_barcode.py @@ -0,0 +1,108 @@ +""" +The InvenTreeBarcodePlugin validates barcodes generated by InvenTree itself. +It can be used as a template for developing third-party barcode plugins. + +The data format is very simple, and maps directly to database objects, +via the "id" parameter. + +Parsing an InvenTree barcode simply involves validating that the +references model objects actually exist in the database. +""" + +# -*- coding: utf-8 -*- + +import json + +from barcode.barcode import BarcodePlugin + +from stock.models import StockItem, StockLocation +from part.models import Part + +from rest_framework.exceptions import ValidationError + + +class InvenTreeBarcodePlugin(BarcodePlugin): + + PLUGIN_NAME = "InvenTreeBarcode" + + def validate(self): + """ + An "InvenTree" barcode must be a jsonnable-dict with the following tags: + + { + 'tool': 'InvenTree', + 'version': + } + + """ + + # The data must either be dict or be able to dictified + if type(self.data) is dict: + pass + elif type(self.data) is str: + try: + self.data = json.loads(self.data) + except json.JSONDecodeError: + return False + else: + return False + + for key in ['tool', 'version']: + if key not in self.data.keys(): + return False + + if not self.data['tool'] == 'InvenTree': + return False + + return True + + def getStockItem(self): + + for k in self.data.keys(): + if k.lower() == 'stockitem': + try: + pk = self.data[k]['id'] + except (AttributeError, KeyError): + raise ValidationError({k: "id parameter not supplied"}) + + try: + item = StockItem.objects.get(pk=pk) + return item + except (ValueError, StockItem.DoesNotExist): + raise ValidationError({k, "Stock item does not exist"}) + + return None + + def getStockLocation(self): + + for k in self.data.keys(): + if k.lower() == 'stocklocation': + try: + pk = self.data[k]['id'] + except (AttributeError, KeyError): + raise ValidationError({k: "id parameter not supplied"}) + + try: + loc = StockLocation.objects.get(pk=pk) + return loc + except (ValueError, StockLocation.DoesNotExist): + raise ValidationError({k, "Stock location does not exist"}) + + return None + + def getPart(self): + + for k in self.data.keys(): + if k.lower() == 'part': + try: + pk = self.data[k]['id'] + except (AttributeError, KeyError): + raise ValidationError({k, 'id parameter not supplied'}) + + try: + part = Part.objects.get(pk=pk) + return part + except (ValueError, Part.DoesNotExist): + raise ValidationError({k, 'Part does not exist'}) + + return None diff --git a/InvenTree/plugins/barcode/barcode.py b/InvenTree/plugins/barcode/barcode.py deleted file mode 100644 index 94a6c34df4..0000000000 --- a/InvenTree/plugins/barcode/barcode.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- - -import hashlib - -from stock.serializers import StockItemSerializer, LocationSerializer -from part.serializers import PartSerializer - -import plugins.plugin as plugin - - -class BarcodePlugin(plugin.InvenTreePlugin): - """ - The BarcodePlugin class is the base class for any barcode plugin. - """ - - def __init__(self, barcode_data): - plugin.InvenTreePlugin.__init__(self) - - self.data = barcode_data - - def hash(self): - """ - Calculate a hash for the barcode data. - This is supposed to uniquely identify the barcode contents, - at least within the bardcode sub-type. - - The default implementation simply returns an MD5 hash of the barcode data, - encoded to a string. - - This may be sufficient for most applications, but can obviously be overridden - by a subclass. - - """ - - hash = hashlib.md5(str(self.data).encode()) - return str(hash.hexdigest()) - - def validate(self): - """ - Default implementation returns False - """ - return False - - def decode(self): - """ - Decode the barcode, and craft a response - """ - - return None - - def render_part(self, part): - """ - Render a Part object to JSON - Use the existing serializer to do this. - """ - - serializer = PartSerializer(part) - - return serializer.data - - def render_stock_location(self, loc): - """ - Render a StockLocation object to JSON - Use the existing serializer to do this. - """ - - serializer = LocationSerializer(loc) - - return serializer.data - - def render_stock_item(self, item): - """ - Render a StockItem object to JSON. - Use the existing serializer to do this - """ - - serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True) - - return serializer.data diff --git a/InvenTree/plugins/barcode/digikey.py b/InvenTree/plugins/barcode/digikey.py deleted file mode 100644 index 2542fe964a..0000000000 --- a/InvenTree/plugins/barcode/digikey.py +++ /dev/null @@ -1,8 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import barcode - - -class DigikeyBarcodePlugin(barcode.BarcodePlugin): - - PLUGIN_NAME = "DigikeyBarcodePlugin" diff --git a/InvenTree/plugins/barcode/inventree.py b/InvenTree/plugins/barcode/inventree.py deleted file mode 100644 index 93b86d42b7..0000000000 --- a/InvenTree/plugins/barcode/inventree.py +++ /dev/null @@ -1,94 +0,0 @@ -""" -The InvenTreeBarcodePlugin validates barcodes generated by InvenTree itself. -It can be used as a template for developing third-party barcode plugins. - -The data format is very simple, and maps directly to database objects, -via the "id" parameter. - -Parsing an InvenTree barcode simply involves validating that the -references model objects actually exist in the database. -""" - -# -*- coding: utf-8 -*- - -import json - -from . import barcode - -from stock.models import StockItem, StockLocation -from part.models import Part - -from django.utils.translation import ugettext as _ - - -class InvenTreeBarcodePlugin(barcode.BarcodePlugin): - - PLUGIN_NAME = "InvenTreeBarcodePlugin" - - def validate(self): - """ - An "InvenTree" barcode must include the following tags: - - { - 'tool': 'InvenTree', - 'version': - } - - """ - - # The data must either be dict or be able to dictified - if type(self.data) is dict: - pass - elif type(self.data) is str: - try: - self.data = json.loads(self.data) - except json.JSONDecodeError: - return False - else: - return False - - for key in ['tool', 'version']: - if key not in self.data.keys(): - return False - - if not self.data['tool'] == 'InvenTree': - return False - - return True - - def decode(self): - - response = {} - - if 'part' in self.data.keys(): - id = self.data['part'].get('id', None) - - try: - part = Part.objects.get(id=id) - response['part'] = self.render_part(part) - except (ValueError, Part.DoesNotExist): - response['error'] = _('Part does not exist') - - elif 'stocklocation' in self.data.keys(): - id = self.data['stocklocation'].get('id', None) - - try: - loc = StockLocation.objects.get(id=id) - response['stocklocation'] = self.render_stock_location(loc) - except (ValueError, StockLocation.DoesNotExist): - response['error'] = _('StockLocation does not exist') - - elif 'stockitem' in self.data.keys(): - - id = self.data['stockitem'].get('id', None) - - try: - item = StockItem.objects.get(id=id) - response['stockitem'] = self.render_stock_item(item) - except (ValueError, StockItem.DoesNotExist): - response['error'] = _('StockItem does not exist') - - else: - response['error'] = _('No matching data') - - return response diff --git a/InvenTree/plugins/plugins.py b/InvenTree/plugins/plugins.py index f913c1f295..0a1c78f0b8 100644 --- a/InvenTree/plugins/plugins.py +++ b/InvenTree/plugins/plugins.py @@ -4,10 +4,6 @@ import inspect import importlib import pkgutil -# Barcode plugins -import plugins.barcode as barcode -from plugins.barcode.barcode import BarcodePlugin - # Action plugins import plugins.action as action from plugins.action.action import ActionPlugin @@ -51,23 +47,6 @@ def get_plugins(pkg, baseclass): return plugins -def load_barcode_plugins(): - """ - Return a list of all registered barcode plugins - """ - - print("Loading barcode plugins") - - plugins = get_plugins(barcode, BarcodePlugin) - - if len(plugins) > 0: - print("Discovered {n} barcode plugins:".format(n=len(plugins))) - - for bp in plugins: - print(" - {bp}".format(bp=bp.PLUGIN_NAME)) - - return plugins - def load_action_plugins(): """ From 1a15b46d65b0a01107ea6b5fb47d1a2394deb86c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 11:14:02 +1000 Subject: [PATCH 04/24] Remove some debug statements --- InvenTree/InvenTree/plugins.py | 1 - InvenTree/barcode/barcode.py | 16 ++++++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/InvenTree/InvenTree/plugins.py b/InvenTree/InvenTree/plugins.py index d0acb3fd8f..8da725bf9c 100644 --- a/InvenTree/InvenTree/plugins.py +++ b/InvenTree/InvenTree/plugins.py @@ -34,7 +34,6 @@ def get_plugins(pkg, baseclass): # Iterate through each module in the package for mod in modules: - print("mod:", mod) # Iterate through each class in the module for item in get_classes(mod): plugin = item[1] diff --git a/InvenTree/barcode/barcode.py b/InvenTree/barcode/barcode.py index 77fc64c4c6..f4d6c368b4 100644 --- a/InvenTree/barcode/barcode.py +++ b/InvenTree/barcode/barcode.py @@ -108,19 +108,23 @@ class BarcodePlugin: return False -def load_barcode_plugins(): +def load_barcode_plugins(debug=False): """ Function to load all barcode plugins """ - print("Loading barcode plugins") + if debug: + print("Loading barcode plugins") plugins = InvenTreePlugins.get_plugins(BarcodePlugins, BarcodePlugin) - if len(plugins) > 0: - print("Discovered {n} plugins:".format(n=len(plugins))) + if debug: + if len(plugins) > 0: + print("Discovered {n} plugins:".format(n=len(plugins))) - for p in plugins: - print(" - {p}".format(p=p.PLUGIN_NAME)) + for p in plugins: + print(" - {p}".format(p=p.PLUGIN_NAME)) + else: + print("No barcode plugins found") return plugins \ No newline at end of file From a72ed11cb02a2cd176dd8bab2c851722812aa1f0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 11:16:55 +1000 Subject: [PATCH 05/24] Skeleton for DigiKey barcode --- InvenTree/barcode/plugins/digikey_barcode.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/InvenTree/barcode/plugins/digikey_barcode.py b/InvenTree/barcode/plugins/digikey_barcode.py index 3a65ff8b23..9f766fe0b2 100644 --- a/InvenTree/barcode/plugins/digikey_barcode.py +++ b/InvenTree/barcode/plugins/digikey_barcode.py @@ -3,3 +3,17 @@ """ DigiKey barcode decoding """ + +from barcode.barcode import BarcodePlugin + + +class DigikeyBarcodePlugin(BarcodePlugin): + + PLUGIN_NAME = "DigikeyBarcode" + + def validate(self): + """ + TODO: Validation of Digikey barcodes. + """ + + return False \ No newline at end of file From 22a8e82108b633e4531dd150a2c185bd6b34b1fb Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 12:21:33 +1000 Subject: [PATCH 06/24] Add endpoint for assigning a particular barcode to a StockItem --- .../static/script/inventree/barcode.js | 24 ++++ InvenTree/barcode/api.py | 107 ++++++++++++++++++ InvenTree/barcode/barcode.py | 6 + 3 files changed, 137 insertions(+) diff --git a/InvenTree/InvenTree/static/script/inventree/barcode.js b/InvenTree/InvenTree/static/script/inventree/barcode.js index 77ccf17c6e..77c344f778 100644 --- a/InvenTree/InvenTree/static/script/inventree/barcode.js +++ b/InvenTree/InvenTree/static/script/inventree/barcode.js @@ -19,4 +19,28 @@ function scanBarcode(barcode, options={}) { }, } ); +} + + +/* + * Associate barcode data with a StockItem + */ +function associateBarcode(barcode, stockitem, options={}) { + + console.log('Associating barcode data:'); + console.log('barcode: ' + barcode); + + inventreePut( + '/api/barcode/assign/', + { + 'barcode': barcode, + 'stockitem': stockitem, + }, + { + method: 'POST', + success: function(response, status) { + console.log(response); + }, + } + ); } \ No newline at end of file diff --git a/InvenTree/barcode/api.py b/InvenTree/barcode/api.py index bc89b395dd..e53b789794 100644 --- a/InvenTree/barcode/api.py +++ b/InvenTree/barcode/api.py @@ -125,7 +125,114 @@ class BarcodeScan(APIView): return Response(response) + +class BarcodeAssign(APIView): + """ + Endpoint for assigning a barcode to a stock item. + + - This only works if the barcode is not already associated with an object in the database + - If the barcode does not match an object, then the barcode hash is assigned to the StockItem + """ + + permission_classes = [ + permissions.IsAuthenticated + ] + + def post(self, request, *args, **kwargs): + + data = request.data + + if 'barcode' not in data: + raise ValidationError({'barcode': 'Must provide barcode_data parameter'}) + + if 'stockitem' not in data: + raise ValidationError({'stockitem': 'Must provide stockitem parameter'}) + + barcode_data = data['barcode'] + + try: + item = StockItem.objects.get(pk=data['stockitem']) + except (ValueError, StockItem.DoesNotExist): + raise ValidationError({'stockitem': 'No matching stock item found'}) + + plugins = load_barcode_plugins() + + plugin = None + + for plugin_class in plugins: + plugin_instance = plugin_class(barcode_data) + + if plugin_instance.validate(): + plugin = plugin_instance + break + + match_found = False + + response = {} + + response['barcode_data'] = barcode_data + + # Matching plugin was found + if plugin is not None: + + hash = plugin.hash() + response['hash'] = hash + response['plugin'] = plugin.name + + # Ensure that the barcode does not already match a database entry + + if plugin.getStockItem() is not None: + match_found = True + response['error'] = 'Barcode already matches StockItem object' + + if plugin.getStockLocation() is not None: + match_found = True + response['error'] = 'Barcode already matches StockLocation object' + + if plugin.getPart() is not None: + match_found = True + response['error'] = 'Barcode already matches Part object' + + if not match_found: + # Try to associate by hash + try: + item = StockItem.objects.get(uid=hash) + response['error'] = 'Barcode hash already matches StockItem object' + match_found = True + except StockItem.DoesNotExist: + pass + + else: + hash = hash_barcode(barcode_data) + + response['hash'] = hash + response['plugin'] = None + + # Lookup stock item by hash + try: + item = StockItem.objects.get(uid=hash) + response['error'] = 'Barcode hash already matches StockItem object' + match_found = True + except StockItem.DoesNotExist: + pass + + if not match_found: + response['success'] = 'Barcode associated with StockItem' + + # Save the barcode hash + item.uid = response['hash'] + item.save() + + serializer = StockItemSerializer(item, part_detail=True, location_detail=True, supplier_part_detail=True) + response['stockitem'] = serializer.data + + + return Response(response) + + barcode_api_urls = [ + + url(r'^assign/$', BarcodeAssign.as_view(), name='api-barcode-assign'), # Catch-all performs barcode 'scan' url(r'^.*$', BarcodeScan.as_view(), name='api-barcode-plugin'), diff --git a/InvenTree/barcode/barcode.py b/InvenTree/barcode/barcode.py index f4d6c368b4..b871198038 100644 --- a/InvenTree/barcode/barcode.py +++ b/InvenTree/barcode/barcode.py @@ -33,6 +33,12 @@ class BarcodePlugin: return self.PLUGIN_NAME def __init__(self, barcode_data): + """ + Initialize the BarcodePlugin instance + + Args: + barcode_data - The raw barcode data + """ self.data = barcode_data From 05599467913e3ea627762d567220ce5c6005bc91 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 14:44:56 +1000 Subject: [PATCH 07/24] Add unit testing framework for barcodes --- InvenTree/InvenTree/test_api.py | 27 -------- InvenTree/barcode/api.py | 13 ++-- InvenTree/barcode/barcode.py | 12 ++++ InvenTree/barcode/tests.py | 118 ++++++++++++++++++++++++++++++++ Makefile | 4 +- 5 files changed, 139 insertions(+), 35 deletions(-) create mode 100644 InvenTree/barcode/tests.py diff --git a/InvenTree/InvenTree/test_api.py b/InvenTree/InvenTree/test_api.py index 7068593e6b..69d701def0 100644 --- a/InvenTree/InvenTree/test_api.py +++ b/InvenTree/InvenTree/test_api.py @@ -79,30 +79,3 @@ class APITests(APITestCase): self.assertIn('instance', data) self.assertEquals('InvenTree', data['server']) - - def test_barcode_fail(self): - # Test barcode endpoint without auth - response = self.client.post(reverse('api-barcode-plugin'), format='json') - - self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED) - - def test_barcode(self): - """ Test the barcode endpoint """ - - self.tokenAuth() - - url = reverse('api-barcode-plugin') - - data = { - 'barcode': { - 'asdlaksdfalsdkfsa;fdlkasd;' - }, - } - - response = self.client.post(url, format='json', data=data) - - self.assertEqual(response.status_code, status.HTTP_200_OK) - - self.assertIn('error', response.data) - self.assertIn('barcode_data', response.data) - self.assertEqual(response.data['error'], 'Unknown barcode format') diff --git a/InvenTree/barcode/api.py b/InvenTree/barcode/api.py index e53b789794..0a5afd270f 100644 --- a/InvenTree/barcode/api.py +++ b/InvenTree/barcode/api.py @@ -80,6 +80,9 @@ class BarcodeScan(APIView): # Try to associate with a stock item item = plugin.getStockItem() + if item is None: + item = plugin.getStockItemByHash() + if item is not None: response['stockitem'] = plugin.renderStockItem(item) match_found = True @@ -194,13 +197,11 @@ class BarcodeAssign(APIView): response['error'] = 'Barcode already matches Part object' if not match_found: - # Try to associate by hash - try: - item = StockItem.objects.get(uid=hash) + item = plugin.getStockItemByHash() + + if item is not None: response['error'] = 'Barcode hash already matches StockItem object' match_found = True - except StockItem.DoesNotExist: - pass else: hash = hash_barcode(barcode_data) @@ -235,5 +236,5 @@ barcode_api_urls = [ url(r'^assign/$', BarcodeAssign.as_view(), name='api-barcode-assign'), # Catch-all performs barcode 'scan' - url(r'^.*$', BarcodeScan.as_view(), name='api-barcode-plugin'), + url(r'^.*$', BarcodeScan.as_view(), name='api-barcode-scan'), ] \ No newline at end of file diff --git a/InvenTree/barcode/barcode.py b/InvenTree/barcode/barcode.py index b871198038..25d775b72f 100644 --- a/InvenTree/barcode/barcode.py +++ b/InvenTree/barcode/barcode.py @@ -50,6 +50,18 @@ class BarcodePlugin: return None + def getStockItemByHash(self): + """ + Attempt to retrieve a StockItem associated with this barcode, + based on the barcode hash. + """ + + try: + item = StockItem.objects.get(uid=self.hash()) + return item + except StockItem.DoesNotExist: + return None + def renderStockItem(self, item): """ Render a stock item to JSON response diff --git a/InvenTree/barcode/tests.py b/InvenTree/barcode/tests.py new file mode 100644 index 0000000000..e0513b6465 --- /dev/null +++ b/InvenTree/barcode/tests.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- + +""" +Unit tests for Barcode endpoints +""" + +from django.contrib.auth import get_user_model +from django.urls import reverse + +from rest_framework.test import APITestCase +from rest_framework import status + +from stock.models import StockItem + + +class BarcodeAPITest(APITestCase): + + fixtures = [ + 'category', + 'part', + 'location', + 'stock' + ] + + def setUp(self): + # Create a user for auth + User = get_user_model() + User.objects.create_user('testuser', 'test@testing.com', 'password') + + self.client.login(username='testuser', password='password') + + self.scan_url = reverse('api-barcode-scan') + self.assign_url = reverse('api-barcode-assign') + + def postBarcode(self, url, barcode): + + return self.client.post(url, format='json', data={'barcode': str(barcode)}) + + def test_invalid(self): + + response = self.client.post(self.scan_url, format='json', data={}) + + self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) + + def test_empty(self): + + response = self.postBarcode(self.scan_url, '') + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + data = response.data + self.assertIn('error', data) + + self.assertIn('barcode_data', data) + self.assertIn('hash', data) + self.assertIn('plugin', data) + self.assertIsNone(data['plugin']) + + def test_barcode_generation(self): + + item = StockItem.objects.get(pk=522) + + response = self.postBarcode(self.scan_url, item.format_barcode()) + data = response.data + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + self.assertIn('stockitem', data) + + pk = data['stockitem']['pk'] + + self.assertEqual(pk, item.pk) + + def test_association(self): + """ + Test that a barcode can be associated with a StockItem + """ + + item = StockItem.objects.get(pk=522) + + self.assertEqual(len(item.uid), 0) + + barcode_data = 'A-TEST-BARCODE-STRING' + + response = self.client.post( + self.assign_url, format='json', + data = { + 'barcode': barcode_data, + 'stockitem': item.pk + } + ) + + data = response.data + + self.assertEqual(response.status_code, status.HTTP_200_OK) + + self.assertIn('success', data) + + hash = data['hash'] + + # Read the item out from the database again + item = StockItem.objects.get(pk=522) + + self.assertEqual(hash, item.uid) + + # Ensure that the same UID cannot be assigned to a different stock item! + response = self.client.post( + self.assign_url, format='json', + data = { + 'barcode': barcode_data, + 'stockitem': 521 + } + ) + + data = response.data + + self.assertIn('error', data) + self.assertNotIn('success', data) diff --git a/Makefile b/Makefile index 556813a149..0072e1ad9c 100644 --- a/Makefile +++ b/Makefile @@ -51,12 +51,12 @@ style: # Run unit tests test: cd InvenTree && python3 manage.py check - cd InvenTree && python3 manage.py test build common company order part report stock InvenTree + cd InvenTree && python3 manage.py test barcode build common company order part report stock InvenTree # Run code coverage coverage: cd InvenTree && python3 manage.py check - coverage run InvenTree/manage.py test build common company order part report stock InvenTree + coverage run InvenTree/manage.py test barcode build common company order part report stock InvenTree coverage html # Install packages required to generate code docs From e943681baadd7c9ff6ded71f9219c384daecad53 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 18:09:06 +1000 Subject: [PATCH 08/24] Add translations for error messages --- InvenTree/barcode/api.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/InvenTree/barcode/api.py b/InvenTree/barcode/api.py index 0a5afd270f..52d8a01f85 100644 --- a/InvenTree/barcode/api.py +++ b/InvenTree/barcode/api.py @@ -1,10 +1,11 @@ # -*- coding: utf-8 -*- import hashlib + from django.conf.urls import url +from django.utils.translation import ugettext as _ from rest_framework.exceptions import ValidationError - from rest_framework import permissions from rest_framework.response import Response from rest_framework.views import APIView @@ -53,7 +54,7 @@ class BarcodeScan(APIView): data = request.data if 'barcode' not in data: - raise ValidationError({'barcode': 'Must provide barcode_data parameter'}) + raise ValidationError({'barcode': _('Must provide barcode_data parameter')}) plugins = load_barcode_plugins() @@ -122,9 +123,9 @@ class BarcodeScan(APIView): pass if not match_found: - response['error'] = 'No match found for barcode data' + response['error'] = _('No match found for barcode data') else: - response['success'] = 'Match found for barcode data' + response['success'] = _('Match found for barcode data') return Response(response) @@ -146,17 +147,17 @@ class BarcodeAssign(APIView): data = request.data if 'barcode' not in data: - raise ValidationError({'barcode': 'Must provide barcode_data parameter'}) + raise ValidationError({'barcode': _('Must provide barcode_data parameter')}) if 'stockitem' not in data: - raise ValidationError({'stockitem': 'Must provide stockitem parameter'}) + raise ValidationError({'stockitem': _('Must provide stockitem parameter')}) barcode_data = data['barcode'] try: item = StockItem.objects.get(pk=data['stockitem']) except (ValueError, StockItem.DoesNotExist): - raise ValidationError({'stockitem': 'No matching stock item found'}) + raise ValidationError({'stockitem': _('No matching stock item found')}) plugins = load_barcode_plugins() @@ -186,21 +187,21 @@ class BarcodeAssign(APIView): if plugin.getStockItem() is not None: match_found = True - response['error'] = 'Barcode already matches StockItem object' + response['error'] = _('Barcode already matches StockItem object') if plugin.getStockLocation() is not None: match_found = True - response['error'] = 'Barcode already matches StockLocation object' + response['error'] = _('Barcode already matches StockLocation object') if plugin.getPart() is not None: match_found = True - response['error'] = 'Barcode already matches Part object' + response['error'] = _('Barcode already matches Part object') if not match_found: item = plugin.getStockItemByHash() if item is not None: - response['error'] = 'Barcode hash already matches StockItem object' + response['error'] = _('Barcode hash already matches StockItem object') match_found = True else: @@ -212,13 +213,13 @@ class BarcodeAssign(APIView): # Lookup stock item by hash try: item = StockItem.objects.get(uid=hash) - response['error'] = 'Barcode hash already matches StockItem object' + response['error'] = _('Barcode hash already matches StockItem object') match_found = True except StockItem.DoesNotExist: pass if not match_found: - response['success'] = 'Barcode associated with StockItem' + response['success'] = _('Barcode associated with StockItem') # Save the barcode hash item.uid = response['hash'] From 503d5a41b1c655d0a8bca068d281f5f3864e76ce Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 11 Jun 2020 18:09:43 +1000 Subject: [PATCH 09/24] Add global 'barcode-scan' button --- .../static/script/inventree/barcode.js | 123 ++++++++++++++++++ .../static/script/inventree/modals.js | 3 +- InvenTree/templates/base.html | 5 + InvenTree/templates/navbar.html | 5 + InvenTree/templates/search_form.html | 4 +- 5 files changed, 138 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/static/script/inventree/barcode.js b/InvenTree/InvenTree/static/script/inventree/barcode.js index 77c344f778..04662fe5f0 100644 --- a/InvenTree/InvenTree/static/script/inventree/barcode.js +++ b/InvenTree/InvenTree/static/script/inventree/barcode.js @@ -43,4 +43,127 @@ function associateBarcode(barcode, stockitem, options={}) { }, } ); +} + + +function makeBarcodeInput(placeholderText='') { + /* + * Generate HTML for a barcode input + */ + + var html = ` +
+
+ +
+ +
Enter barcode data
+
+
+
+ `; + + return html; +} + + +function getBarcodeData(modal) { + + return $(modal + ' #barcode').val(); +} + + +function barcodeDialog(title, options={}) { + /* + * Handle a barcode display dialog. + */ + + var modal = '#modal-form'; + + $(modal).on('shown.bs.modal', function() { + $(modal + ' .modal-form-content').scrollTop(0); + + // Ensure the barcode field has focus + $(modal + ' #barcode').focus(); + + var form = $(modal).find('.js-modal-form'); + + // Override form submission + form.submit(function() { + + var barcode = getBarcodeData(modal); + + if (options.submit) { + modalEnable(modal, false); + options.submit(barcode); + } + + return false; + }); + + modalSubmit(modal, function() { + + var barcode = getBarcodeData(modal); + + if (options.submit) { + modalEnable(modal, false); + options.submit(barcode); + } + }); + + }); + + modalSetTitle(modal, title); + modalShowSubmitButton(modal, true); + + var content = ''; + + if (options.headerContent) { + content += options.headerContent; + } + + content += makeBarcodeInput(); + + if (options.footerContent) { + content += options.footerContent; + } + + modalSetContent(modal, content); + + $(modal).modal({ + backdrop: 'static', + keyboard: false, + }); + + $(modal).modal('show'); +} + + +function barcodeScanDialog() { + /* + * Perform a barcode scan, + * and (potentially) redirect the browser + */ + + var modal = '#modal-form'; + + barcodeDialog( + "Scan Barcode", + { + submit: function(barcode) { + inventreePut( + '/api/barcode/', + { + barcode: barcode, + }, + { + method: 'POST', + success: function(response, status) { + console.log(response); + }, + }, + ); + }, + }, + ); } \ No newline at end of file diff --git a/InvenTree/InvenTree/static/script/inventree/modals.js b/InvenTree/InvenTree/static/script/inventree/modals.js index 24a1a38ed3..158cf67a77 100644 --- a/InvenTree/InvenTree/static/script/inventree/modals.js +++ b/InvenTree/InvenTree/static/script/inventree/modals.js @@ -442,7 +442,8 @@ function attachSecondaryModal(modal, options) { */ var select = '#id_' + options.field; - var option = new Option(response.text, response.pk, true, true) + + var option = new Option(response.text, response.pk, true, true); $(modal).find(select).append(option).trigger('change'); } diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 745b93d5b2..376ca98c7b 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -133,6 +133,11 @@ $(document).ready(function () { inventreeDocReady(); showCachedAlerts(); + + $('#barcode-scan').click(function() { + barcodeScanDialog(); + }); + }); diff --git a/InvenTree/templates/navbar.html b/InvenTree/templates/navbar.html index 7bccb6d62d..1caa1fa950 100644 --- a/InvenTree/templates/navbar.html +++ b/InvenTree/templates/navbar.html @@ -28,6 +28,11 @@
{% if item.in_stock %} @@ -335,6 +338,10 @@ $("#show-qr-code").click(function() { }); }); +$("#unlink-barcode").click(function() { + unlinkBarcode({{ item.id }}); +}); + {% if item.in_stock %} {% if item.part.salable %} From b559816dca1ba6ecfb497c6868df7ca429bf60f2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 12 Jun 2020 10:25:07 +1000 Subject: [PATCH 16/24] Update translations --- InvenTree/locale/de/LC_MESSAGES/django.mo | Bin 51410 -> 50749 bytes InvenTree/locale/de/LC_MESSAGES/django.po | 303 ++++++++++++++-------- InvenTree/locale/en/LC_MESSAGES/django.po | 224 +++++++++------- InvenTree/locale/es/LC_MESSAGES/django.po | 224 +++++++++------- InvenTree/stock/views.py | 2 +- 5 files changed, 455 insertions(+), 298 deletions(-) diff --git a/InvenTree/locale/de/LC_MESSAGES/django.mo b/InvenTree/locale/de/LC_MESSAGES/django.mo index 6cdb283c865efeaa301ad19cc331fe34bc76119c..93b1affc812a1b20499aa917caeb284ddbaf61c9 100644 GIT binary patch delta 15903 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 delta 16470 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\n" "Language-Team: C \n" @@ -17,30 +17,14 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Lokalize 19.12.0\n" -#: InvenTree/api.py:86 +#: InvenTree/api.py:83 msgid "No action specified" msgstr "Keine Aktion angegeben" -#: InvenTree/api.py:100 +#: InvenTree/api.py:97 msgid "No matching action found" msgstr "Keine passende Aktion gefunden" -#: InvenTree/api.py:131 -msgid "No barcode data provided" -msgstr "Keine Strichcodedaten bereitgestellt" - -#: InvenTree/api.py:146 -msgid "Barcode successfully decoded" -msgstr "Strichcode erfolgreich dekodiert" - -#: InvenTree/api.py:149 -msgid "Barcode plugin returned incorrect response" -msgstr "Ungültige Antwort vom Strichcode-Plugin" - -#: InvenTree/api.py:159 -msgid "Unknown barcode format" -msgstr "Unbekanntes Strichcode-Format" - #: InvenTree/forms.py:101 build/forms.py:37 msgid "Confirm" msgstr "Bestätigen" @@ -225,6 +209,50 @@ msgstr "Überschuss muss eine Ganzzahl oder ein Prozentwert sein" msgid "Database Statistics" msgstr "Datenbankstatistiken" +#: barcode/api.py:53 barcode/api.py:150 +msgid "Must provide barcode_data parameter" +msgstr "" + +#: barcode/api.py:126 +msgid "No match found for barcode data" +msgstr "" + +#: barcode/api.py:128 +msgid "Match found for barcode data" +msgstr "" + +#: barcode/api.py:153 +msgid "Must provide stockitem parameter" +msgstr "" + +#: barcode/api.py:160 +#, fuzzy +#| msgid "No matching action found" +msgid "No matching stock item found" +msgstr "Keine passende Aktion gefunden" + +#: barcode/api.py:190 +msgid "Barcode already matches StockItem object" +msgstr "" + +#: barcode/api.py:194 +msgid "Barcode already matches StockLocation object" +msgstr "" + +#: barcode/api.py:198 +msgid "Barcode already matches Part object" +msgstr "" + +#: barcode/api.py:204 barcode/api.py:216 +msgid "Barcode hash already matches StockItem object" +msgstr "" + +#: barcode/api.py:222 +#, fuzzy +#| msgid "Create new Stock Item" +msgid "Barcode associated with StockItem" +msgstr "Neues Lagerobjekt hinzufügen" + #: build/forms.py:58 #, fuzzy #| msgid "Location Details" @@ -333,7 +361,7 @@ msgstr "Chargennummer für diese Bau-Ausgabe" #: company/templates/company/supplier_part_base.html:60 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:74 part/templates/part/part_base.html:88 -#: stock/models.py:368 stock/templates/stock/item_base.html:221 +#: stock/models.py:368 stock/templates/stock/item_base.html:229 msgid "External Link" msgstr "Externer Link" @@ -414,7 +442,7 @@ msgstr "Neues Lagerobjekt" #: build/templates/build/allocate.html:161 #: order/templates/order/sales_order_detail.html:68 #: order/templates/order/sales_order_detail.html:150 stock/models.py:362 -#: stock/templates/stock/item_base.html:180 +#: stock/templates/stock/item_base.html:188 msgid "Serial Number" msgstr "Seriennummer" @@ -431,7 +459,7 @@ msgstr "Seriennummer" #: part/templates/part/allocation.html:49 #: stock/templates/stock/item_base.html:26 #: stock/templates/stock/item_base.html:32 -#: stock/templates/stock/item_base.html:186 +#: stock/templates/stock/item_base.html:194 #: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172 #: templates/js/build.html:52 templates/js/stock.html:653 msgid "Quantity" @@ -439,7 +467,7 @@ msgstr "Anzahl" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:162 +#: stock/templates/stock/item_base.html:170 #: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493 msgid "Location" msgstr "Standort" @@ -521,7 +549,7 @@ msgstr "Keine Lagerobjekt gefunden, die diesem Bau zugewiesen werden können" #: build/templates/build/build_base.html:8 #: build/templates/build/build_base.html:34 #: build/templates/build/complete.html:6 -#: stock/templates/stock/item_base.html:200 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:208 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "Bau" @@ -541,7 +569,7 @@ msgstr "Bau-Status" #: build/templates/build/build_base.html:80 #: build/templates/build/detail.html:42 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:253 templates/js/build.html:57 +#: stock/templates/stock/item_base.html:261 templates/js/build.html:57 #: templates/js/order.html:162 templates/js/order.html:235 #: templates/js/stock.html:480 msgid "Status" @@ -553,7 +581,7 @@ msgstr "Status" #: order/templates/order/sales_order_notes.html:10 #: order/templates/order/sales_order_ship.html:25 #: part/templates/part/allocation.html:27 -#: stock/templates/stock/item_base.html:150 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:158 templates/js/order.html:209 msgid "Sales Order" msgstr "Bestellung" @@ -622,7 +650,7 @@ msgid "Stock can be taken from any available location." msgstr "Bestand kann jedem verfügbaren Lagerort entnommen werden." #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:193 templates/js/stock.html:488 +#: stock/templates/stock/item_base.html:201 templates/js/stock.html:488 msgid "Batch" msgstr "Los" @@ -976,14 +1004,14 @@ msgstr "Hersteller" #: company/templates/company/supplier_part_detail.html:21 order/models.py:148 #: order/templates/order/order_base.html:74 #: order/templates/order/order_wizard/select_pos.html:30 -#: stock/templates/stock/item_base.html:228 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:236 templates/js/company.html:52 #: templates/js/company.html:134 templates/js/order.html:144 msgid "Supplier" msgstr "Zulieferer" #: company/templates/company/detail.html:26 order/models.py:314 #: order/templates/order/sales_order_base.html:73 stock/models.py:357 -#: stock/models.py:358 stock/templates/stock/item_base.html:137 +#: stock/models.py:358 stock/templates/stock/item_base.html:145 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "Kunde" @@ -1098,7 +1126,7 @@ msgstr "Neuer Auftrag" #: company/templates/company/supplier_part_base.html:6 #: company/templates/company/supplier_part_base.html:19 stock/models.py:331 -#: stock/templates/stock/item_base.html:233 templates/js/company.html:150 +#: stock/templates/stock/item_base.html:241 templates/js/company.html:150 msgid "Supplier Part" msgstr "Zulieferer-Teil" @@ -1385,7 +1413,7 @@ msgstr "Position - Notizen" #: order/models.py:466 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:23 -#: stock/templates/stock/item_base.html:207 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:215 templates/js/order.html:136 msgid "Purchase Order" msgstr "Kaufvertrag" @@ -2114,14 +2142,14 @@ msgstr "Bestellung" #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 #: stock/templates/stock/item_base.html:58 -#: stock/templates/stock/item_base.html:215 +#: stock/templates/stock/item_base.html:223 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 #: templates/js/stock.html:623 msgid "Stock Item" msgstr "Lagerobjekt" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:156 +#: stock/templates/stock/item_base.html:164 msgid "Build Order" msgstr "Bauauftrag" @@ -2539,7 +2567,7 @@ msgstr "Benutzt in" msgid "Tracking" msgstr "Tracking" -#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:259 +#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:267 msgid "Tests" msgstr "" @@ -2738,22 +2766,6 @@ msgstr "BOM-Position beaarbeiten" msgid "Confim BOM item deletion" msgstr "Löschung von BOM-Position bestätigen" -#: plugins/barcode/inventree.py:70 -msgid "Part does not exist" -msgstr "Teil existiert nicht" - -#: plugins/barcode/inventree.py:79 -msgid "StockLocation does not exist" -msgstr "Lagerort existiert nicht" - -#: plugins/barcode/inventree.py:89 -msgid "StockItem does not exist" -msgstr "Lagerobjekt existiert nicht" - -#: plugins/barcode/inventree.py:92 -msgid "No matching data" -msgstr "Keine passenden Daten" - #: report/models.py:167 #, fuzzy #| msgid "Template part" @@ -2834,7 +2846,7 @@ msgstr "Teil kann nicht zu sich selbst gehören" msgid "Parent Stock Item" msgstr "Eltern-Lagerobjekt" -#: stock/models.py:322 stock/templates/stock/item_base.html:129 +#: stock/models.py:322 stock/templates/stock/item_base.html:137 msgid "Base Part" msgstr "Basisteil" @@ -3066,99 +3078,131 @@ msgstr "" "Dieses Lagerobjekt wird automatisch gelöscht wenn der Lagerbestand " "aufgebraucht ist." -#: stock/templates/stock/item_base.html:74 +#: stock/templates/stock/item_base.html:78 #, fuzzy -#| msgid "Add stock" -msgid "Add to stock" -msgstr "Bestand hinzufügen" +#| msgid "Source Location" +msgid "Barcode actions" +msgstr "Quell-Standort" -#: stock/templates/stock/item_base.html:77 +#: stock/templates/stock/item_base.html:80 #, fuzzy -#| msgid "Remove From Stock" -msgid "Take from stock" -msgstr "Aus Lagerbestand entfernen" +#| msgid "Part QR Code" +msgid "Show QR Code" +msgstr "Teil-QR-Code" -#: stock/templates/stock/item_base.html:80 templates/stock_table.html:14 +#: stock/templates/stock/item_base.html:81 +msgid "Print Label" +msgstr "" + +#: stock/templates/stock/item_base.html:82 +msgid "Link Barcode" +msgstr "" + +#: stock/templates/stock/item_base.html:84 +msgid "Unlink Barcode" +msgstr "" + +#: stock/templates/stock/item_base.html:91 +#, fuzzy +#| msgid "Confirm stock adjustment" +msgid "Stock adjustment actions" +msgstr "Bestands-Anpassung bestätigen" + +#: stock/templates/stock/item_base.html:94 templates/stock_table.html:14 msgid "Count stock" msgstr "Bestand zählen" -#: stock/templates/stock/item_base.html:84 -#, fuzzy -#| msgid "Serialize Stock" -msgid "Serialize stock" -msgstr "Lagerbestand erfassen" +#: stock/templates/stock/item_base.html:95 templates/stock_table.html:12 +msgid "Add stock" +msgstr "Bestand hinzufügen" -#: stock/templates/stock/item_base.html:90 stock/views.py:232 -#, fuzzy -#| msgid "Item assigned to customer?" -msgid "Assign to Customer" -msgstr "Ist dieses Objekt einem Kunden zugeteilt?" +#: stock/templates/stock/item_base.html:96 templates/stock_table.html:13 +msgid "Remove stock" +msgstr "Bestand entfernen" -#: stock/templates/stock/item_base.html:94 +#: stock/templates/stock/item_base.html:98 #, fuzzy #| msgid "Order stock" msgid "Transfer stock" msgstr "Bestand bestellen" -#: stock/templates/stock/item_base.html:97 +#: stock/templates/stock/item_base.html:104 +#, fuzzy +#| msgid "Stock Locations" +msgid "Stock actions" +msgstr "Lagerobjekt-Standorte" + +#: stock/templates/stock/item_base.html:107 +#, fuzzy +#| msgid "Serialize Stock" +msgid "Serialize stock" +msgstr "Lagerbestand erfassen" + +#: stock/templates/stock/item_base.html:110 +#, fuzzy +#| msgid "Item assigned to customer?" +msgid "Assign to customer" +msgstr "Ist dieses Objekt einem Kunden zugeteilt?" + +#: stock/templates/stock/item_base.html:113 +#, fuzzy +#| msgid "Count stock items" +msgid "Convert to variant" +msgstr "Lagerobjekte zählen" + +#: stock/templates/stock/item_base.html:115 #, fuzzy #| msgid "Count stock items" msgid "Duplicate stock item" msgstr "Lagerobjekte zählen" -#: stock/templates/stock/item_base.html:102 -#, fuzzy -#| msgid "Count stock items" -msgid "Convert stock to variant" -msgstr "Lagerobjekte zählen" - -#: stock/templates/stock/item_base.html:107 -msgid "Generate test report" -msgstr "" - -#: stock/templates/stock/item_base.html:111 +#: stock/templates/stock/item_base.html:116 #, fuzzy #| msgid "Edit Stock Item" msgid "Edit stock item" msgstr "Lagerobjekt bearbeiten" -#: stock/templates/stock/item_base.html:115 +#: stock/templates/stock/item_base.html:118 #, fuzzy #| msgid "Delete Stock Item" msgid "Delete stock item" msgstr "Lagerobjekt löschen" -#: stock/templates/stock/item_base.html:124 +#: stock/templates/stock/item_base.html:123 +msgid "Generate test report" +msgstr "" + +#: stock/templates/stock/item_base.html:132 msgid "Stock Item Details" msgstr "Lagerbestands-Details" -#: stock/templates/stock/item_base.html:144 +#: stock/templates/stock/item_base.html:152 msgid "Belongs To" msgstr "Gehört zu" -#: stock/templates/stock/item_base.html:166 +#: stock/templates/stock/item_base.html:174 #, fuzzy #| msgid "No stock location set" msgid "No location set" msgstr "Kein Lagerort gesetzt" -#: stock/templates/stock/item_base.html:173 +#: stock/templates/stock/item_base.html:181 msgid "Unique Identifier" msgstr "Eindeutiger Bezeichner" -#: stock/templates/stock/item_base.html:214 +#: stock/templates/stock/item_base.html:222 msgid "Parent Item" msgstr "Elternposition" -#: stock/templates/stock/item_base.html:239 +#: stock/templates/stock/item_base.html:247 msgid "Last Updated" msgstr "Zuletzt aktualisiert" -#: stock/templates/stock/item_base.html:244 +#: stock/templates/stock/item_base.html:252 msgid "Last Stocktake" msgstr "Letzte Inventur" -#: stock/templates/stock/item_base.html:248 +#: stock/templates/stock/item_base.html:256 msgid "No stocktake performed" msgstr "Keine Inventur ausgeführt" @@ -3289,6 +3333,12 @@ msgstr "Lagerobjekt bearbeiten" msgid "Delete Stock Item Attachment" msgstr "Teilanhang löschen" +#: stock/views.py:232 +#, fuzzy +#| msgid "Item assigned to customer?" +msgid "Assign to Customer" +msgstr "Ist dieses Objekt einem Kunden zugeteilt?" + #: stock/views.py:270 #, fuzzy #| msgid "Delete Template" @@ -3424,8 +3474,10 @@ msgid "Create new Stock Item" msgstr "Neues Lagerobjekt hinzufügen" #: stock/views.py:1167 -msgid "Copy Stock Item" -msgstr "Lagerobjekt kopieren" +#, fuzzy +#| msgid "Count stock items" +msgid "Duplicate Stock Item" +msgstr "Lagerobjekte zählen" #: stock/views.py:1240 msgid "Invalid quantity" @@ -3832,42 +3884,38 @@ msgstr "Kaufen" msgid "Sell" msgstr "Verkaufen" -#: templates/navbar.html:36 +#: templates/navbar.html:32 +msgid "Scan Barcode" +msgstr "" + +#: templates/navbar.html:41 msgid "Admin" msgstr "Admin" -#: templates/navbar.html:39 +#: templates/navbar.html:44 msgid "Settings" msgstr "Einstellungen" -#: templates/navbar.html:40 +#: templates/navbar.html:45 msgid "Logout" msgstr "Ausloggen" -#: templates/navbar.html:42 +#: templates/navbar.html:47 msgid "Login" msgstr "Einloggen" -#: templates/navbar.html:45 +#: templates/navbar.html:50 msgid "About InvenTree" msgstr "Über InvenBaum" -#: templates/navbar.html:46 +#: templates/navbar.html:51 msgid "Statistics" msgstr "Statistiken" -#: templates/search_form.html:6 +#: templates/search_form.html:6 templates/search_form.html:8 msgid "Search" msgstr "Suche" -#: templates/stock_table.html:12 -msgid "Add stock" -msgstr "Bestand hinzufügen" - -#: templates/stock_table.html:13 -msgid "Remove stock" -msgstr "Bestand entfernen" - #: templates/stock_table.html:15 msgid "Move stock" msgstr "Bestand bewegen" @@ -3880,6 +3928,43 @@ msgstr "Bestand bestellen" msgid "Delete Stock" msgstr "Bestand löschen" +#~ msgid "No barcode data provided" +#~ msgstr "Keine Strichcodedaten bereitgestellt" + +#~ msgid "Barcode successfully decoded" +#~ msgstr "Strichcode erfolgreich dekodiert" + +#~ msgid "Barcode plugin returned incorrect response" +#~ msgstr "Ungültige Antwort vom Strichcode-Plugin" + +#~ msgid "Unknown barcode format" +#~ msgstr "Unbekanntes Strichcode-Format" + +#~ msgid "Part does not exist" +#~ msgstr "Teil existiert nicht" + +#~ msgid "StockLocation does not exist" +#~ msgstr "Lagerort existiert nicht" + +#~ msgid "StockItem does not exist" +#~ msgstr "Lagerobjekt existiert nicht" + +#~ msgid "No matching data" +#~ msgstr "Keine passenden Daten" + +#, fuzzy +#~| msgid "Add stock" +#~ msgid "Add to stock" +#~ msgstr "Bestand hinzufügen" + +#, fuzzy +#~| msgid "Remove From Stock" +#~ msgid "Take from stock" +#~ msgstr "Aus Lagerbestand entfernen" + +#~ msgid "Copy Stock Item" +#~ msgstr "Lagerobjekt kopieren" + #~ msgid "Part cannot be a variant of another part if it is already a template" #~ msgstr "" #~ "Teil kann keine Variante eines anderen Teils sein wenn es bereits eine " diff --git a/InvenTree/locale/en/LC_MESSAGES/django.po b/InvenTree/locale/en/LC_MESSAGES/django.po index fb77271de8..70f1e96161 100644 --- a/InvenTree/locale/en/LC_MESSAGES/django.po +++ b/InvenTree/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-09 06:17+0000\n" +"POT-Creation-Date: 2020-06-12 00:13+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,30 +18,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: InvenTree/api.py:86 +#: InvenTree/api.py:83 msgid "No action specified" msgstr "" -#: InvenTree/api.py:100 +#: InvenTree/api.py:97 msgid "No matching action found" msgstr "" -#: InvenTree/api.py:131 -msgid "No barcode data provided" -msgstr "" - -#: InvenTree/api.py:146 -msgid "Barcode successfully decoded" -msgstr "" - -#: InvenTree/api.py:149 -msgid "Barcode plugin returned incorrect response" -msgstr "" - -#: InvenTree/api.py:159 -msgid "Unknown barcode format" -msgstr "" - #: InvenTree/forms.py:101 build/forms.py:37 msgid "Confirm" msgstr "" @@ -218,6 +202,46 @@ msgstr "" msgid "Database Statistics" msgstr "" +#: barcode/api.py:53 barcode/api.py:150 +msgid "Must provide barcode_data parameter" +msgstr "" + +#: barcode/api.py:126 +msgid "No match found for barcode data" +msgstr "" + +#: barcode/api.py:128 +msgid "Match found for barcode data" +msgstr "" + +#: barcode/api.py:153 +msgid "Must provide stockitem parameter" +msgstr "" + +#: barcode/api.py:160 +msgid "No matching stock item found" +msgstr "" + +#: barcode/api.py:190 +msgid "Barcode already matches StockItem object" +msgstr "" + +#: barcode/api.py:194 +msgid "Barcode already matches StockLocation object" +msgstr "" + +#: barcode/api.py:198 +msgid "Barcode already matches Part object" +msgstr "" + +#: barcode/api.py:204 barcode/api.py:216 +msgid "Barcode hash already matches StockItem object" +msgstr "" + +#: barcode/api.py:222 +msgid "Barcode associated with StockItem" +msgstr "" + #: build/forms.py:58 msgid "Location of completed parts" msgstr "" @@ -318,7 +342,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:60 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:74 part/templates/part/part_base.html:88 -#: stock/models.py:368 stock/templates/stock/item_base.html:221 +#: stock/models.py:368 stock/templates/stock/item_base.html:229 msgid "External Link" msgstr "" @@ -398,7 +422,7 @@ msgstr "" #: build/templates/build/allocate.html:161 #: order/templates/order/sales_order_detail.html:68 #: order/templates/order/sales_order_detail.html:150 stock/models.py:362 -#: stock/templates/stock/item_base.html:180 +#: stock/templates/stock/item_base.html:188 msgid "Serial Number" msgstr "" @@ -415,7 +439,7 @@ msgstr "" #: part/templates/part/allocation.html:49 #: stock/templates/stock/item_base.html:26 #: stock/templates/stock/item_base.html:32 -#: stock/templates/stock/item_base.html:186 +#: stock/templates/stock/item_base.html:194 #: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172 #: templates/js/build.html:52 templates/js/stock.html:653 msgid "Quantity" @@ -423,7 +447,7 @@ msgstr "" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:162 +#: stock/templates/stock/item_base.html:170 #: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493 msgid "Location" msgstr "" @@ -504,7 +528,7 @@ msgstr "" #: build/templates/build/build_base.html:8 #: build/templates/build/build_base.html:34 #: build/templates/build/complete.html:6 -#: stock/templates/stock/item_base.html:200 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:208 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "" @@ -524,7 +548,7 @@ msgstr "" #: build/templates/build/build_base.html:80 #: build/templates/build/detail.html:42 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:253 templates/js/build.html:57 +#: stock/templates/stock/item_base.html:261 templates/js/build.html:57 #: templates/js/order.html:162 templates/js/order.html:235 #: templates/js/stock.html:480 msgid "Status" @@ -536,7 +560,7 @@ msgstr "" #: order/templates/order/sales_order_notes.html:10 #: order/templates/order/sales_order_ship.html:25 #: part/templates/part/allocation.html:27 -#: stock/templates/stock/item_base.html:150 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:158 templates/js/order.html:209 msgid "Sales Order" msgstr "" @@ -603,7 +627,7 @@ msgid "Stock can be taken from any available location." msgstr "" #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:193 templates/js/stock.html:488 +#: stock/templates/stock/item_base.html:201 templates/js/stock.html:488 msgid "Batch" msgstr "" @@ -950,14 +974,14 @@ msgstr "" #: company/templates/company/supplier_part_detail.html:21 order/models.py:148 #: order/templates/order/order_base.html:74 #: order/templates/order/order_wizard/select_pos.html:30 -#: stock/templates/stock/item_base.html:228 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:236 templates/js/company.html:52 #: templates/js/company.html:134 templates/js/order.html:144 msgid "Supplier" msgstr "" #: company/templates/company/detail.html:26 order/models.py:314 #: order/templates/order/sales_order_base.html:73 stock/models.py:357 -#: stock/models.py:358 stock/templates/stock/item_base.html:137 +#: stock/models.py:358 stock/templates/stock/item_base.html:145 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "" @@ -1071,7 +1095,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:6 #: company/templates/company/supplier_part_base.html:19 stock/models.py:331 -#: stock/templates/stock/item_base.html:233 templates/js/company.html:150 +#: stock/templates/stock/item_base.html:241 templates/js/company.html:150 msgid "Supplier Part" msgstr "" @@ -1354,7 +1378,7 @@ msgstr "" #: order/models.py:466 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:23 -#: stock/templates/stock/item_base.html:207 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:215 templates/js/order.html:136 msgid "Purchase Order" msgstr "" @@ -2052,14 +2076,14 @@ msgstr "" #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 #: stock/templates/stock/item_base.html:58 -#: stock/templates/stock/item_base.html:215 +#: stock/templates/stock/item_base.html:223 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 #: templates/js/stock.html:623 msgid "Stock Item" msgstr "" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:156 +#: stock/templates/stock/item_base.html:164 msgid "Build Order" msgstr "" @@ -2453,7 +2477,7 @@ msgstr "" msgid "Tracking" msgstr "" -#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:259 +#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:267 msgid "Tests" msgstr "" @@ -2646,22 +2670,6 @@ msgstr "" msgid "Confim BOM item deletion" msgstr "" -#: plugins/barcode/inventree.py:70 -msgid "Part does not exist" -msgstr "" - -#: plugins/barcode/inventree.py:79 -msgid "StockLocation does not exist" -msgstr "" - -#: plugins/barcode/inventree.py:89 -msgid "StockItem does not exist" -msgstr "" - -#: plugins/barcode/inventree.py:92 -msgid "No matching data" -msgstr "" - #: report/models.py:167 msgid "Template name" msgstr "" @@ -2731,7 +2739,7 @@ msgstr "" msgid "Parent Stock Item" msgstr "" -#: stock/models.py:322 stock/templates/stock/item_base.html:129 +#: stock/models.py:322 stock/templates/stock/item_base.html:137 msgid "Base Part" msgstr "" @@ -2937,79 +2945,107 @@ msgid "" "This stock item will be automatically deleted when all stock is depleted." msgstr "" -#: stock/templates/stock/item_base.html:74 -msgid "Add to stock" +#: stock/templates/stock/item_base.html:78 +msgid "Barcode actions" msgstr "" -#: stock/templates/stock/item_base.html:77 -msgid "Take from stock" +#: stock/templates/stock/item_base.html:80 +msgid "Show QR Code" msgstr "" -#: stock/templates/stock/item_base.html:80 templates/stock_table.html:14 -msgid "Count stock" +#: stock/templates/stock/item_base.html:81 +msgid "Print Label" +msgstr "" + +#: stock/templates/stock/item_base.html:82 +msgid "Link Barcode" msgstr "" #: stock/templates/stock/item_base.html:84 -msgid "Serialize stock" +msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:90 stock/views.py:232 -msgid "Assign to Customer" +#: stock/templates/stock/item_base.html:91 +msgid "Stock adjustment actions" msgstr "" -#: stock/templates/stock/item_base.html:94 +#: stock/templates/stock/item_base.html:94 templates/stock_table.html:14 +msgid "Count stock" +msgstr "" + +#: stock/templates/stock/item_base.html:95 templates/stock_table.html:12 +msgid "Add stock" +msgstr "" + +#: stock/templates/stock/item_base.html:96 templates/stock_table.html:13 +msgid "Remove stock" +msgstr "" + +#: stock/templates/stock/item_base.html:98 msgid "Transfer stock" msgstr "" -#: stock/templates/stock/item_base.html:97 -msgid "Duplicate stock item" -msgstr "" - -#: stock/templates/stock/item_base.html:102 -msgid "Convert stock to variant" +#: stock/templates/stock/item_base.html:104 +msgid "Stock actions" msgstr "" #: stock/templates/stock/item_base.html:107 -msgid "Generate test report" +msgid "Serialize stock" msgstr "" -#: stock/templates/stock/item_base.html:111 -msgid "Edit stock item" +#: stock/templates/stock/item_base.html:110 +msgid "Assign to customer" +msgstr "" + +#: stock/templates/stock/item_base.html:113 +msgid "Convert to variant" msgstr "" #: stock/templates/stock/item_base.html:115 +msgid "Duplicate stock item" +msgstr "" + +#: stock/templates/stock/item_base.html:116 +msgid "Edit stock item" +msgstr "" + +#: stock/templates/stock/item_base.html:118 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:124 +#: stock/templates/stock/item_base.html:123 +msgid "Generate test report" +msgstr "" + +#: stock/templates/stock/item_base.html:132 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:144 +#: stock/templates/stock/item_base.html:152 msgid "Belongs To" msgstr "" -#: stock/templates/stock/item_base.html:166 +#: stock/templates/stock/item_base.html:174 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:173 +#: stock/templates/stock/item_base.html:181 msgid "Unique Identifier" msgstr "" -#: stock/templates/stock/item_base.html:214 +#: stock/templates/stock/item_base.html:222 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:239 +#: stock/templates/stock/item_base.html:247 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:244 +#: stock/templates/stock/item_base.html:252 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:248 +#: stock/templates/stock/item_base.html:256 msgid "No stocktake performed" msgstr "" @@ -3128,6 +3164,10 @@ msgstr "" msgid "Delete Stock Item Attachment" msgstr "" +#: stock/views.py:232 +msgid "Assign to Customer" +msgstr "" + #: stock/views.py:270 msgid "Delete All Test Data" msgstr "" @@ -3251,7 +3291,7 @@ msgid "Create new Stock Item" msgstr "" #: stock/views.py:1167 -msgid "Copy Stock Item" +msgid "Duplicate Stock Item" msgstr "" #: stock/views.py:1240 @@ -3635,42 +3675,38 @@ msgstr "" msgid "Sell" msgstr "" -#: templates/navbar.html:36 +#: templates/navbar.html:32 +msgid "Scan Barcode" +msgstr "" + +#: templates/navbar.html:41 msgid "Admin" msgstr "" -#: templates/navbar.html:39 +#: templates/navbar.html:44 msgid "Settings" msgstr "" -#: templates/navbar.html:40 +#: templates/navbar.html:45 msgid "Logout" msgstr "" -#: templates/navbar.html:42 +#: templates/navbar.html:47 msgid "Login" msgstr "" -#: templates/navbar.html:45 +#: templates/navbar.html:50 msgid "About InvenTree" msgstr "" -#: templates/navbar.html:46 +#: templates/navbar.html:51 msgid "Statistics" msgstr "" -#: templates/search_form.html:6 +#: templates/search_form.html:6 templates/search_form.html:8 msgid "Search" msgstr "" -#: templates/stock_table.html:12 -msgid "Add stock" -msgstr "" - -#: templates/stock_table.html:13 -msgid "Remove stock" -msgstr "" - #: templates/stock_table.html:15 msgid "Move stock" msgstr "" diff --git a/InvenTree/locale/es/LC_MESSAGES/django.po b/InvenTree/locale/es/LC_MESSAGES/django.po index fb77271de8..70f1e96161 100644 --- a/InvenTree/locale/es/LC_MESSAGES/django.po +++ b/InvenTree/locale/es/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-09 06:17+0000\n" +"POT-Creation-Date: 2020-06-12 00:13+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,30 +18,14 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" -#: InvenTree/api.py:86 +#: InvenTree/api.py:83 msgid "No action specified" msgstr "" -#: InvenTree/api.py:100 +#: InvenTree/api.py:97 msgid "No matching action found" msgstr "" -#: InvenTree/api.py:131 -msgid "No barcode data provided" -msgstr "" - -#: InvenTree/api.py:146 -msgid "Barcode successfully decoded" -msgstr "" - -#: InvenTree/api.py:149 -msgid "Barcode plugin returned incorrect response" -msgstr "" - -#: InvenTree/api.py:159 -msgid "Unknown barcode format" -msgstr "" - #: InvenTree/forms.py:101 build/forms.py:37 msgid "Confirm" msgstr "" @@ -218,6 +202,46 @@ msgstr "" msgid "Database Statistics" msgstr "" +#: barcode/api.py:53 barcode/api.py:150 +msgid "Must provide barcode_data parameter" +msgstr "" + +#: barcode/api.py:126 +msgid "No match found for barcode data" +msgstr "" + +#: barcode/api.py:128 +msgid "Match found for barcode data" +msgstr "" + +#: barcode/api.py:153 +msgid "Must provide stockitem parameter" +msgstr "" + +#: barcode/api.py:160 +msgid "No matching stock item found" +msgstr "" + +#: barcode/api.py:190 +msgid "Barcode already matches StockItem object" +msgstr "" + +#: barcode/api.py:194 +msgid "Barcode already matches StockLocation object" +msgstr "" + +#: barcode/api.py:198 +msgid "Barcode already matches Part object" +msgstr "" + +#: barcode/api.py:204 barcode/api.py:216 +msgid "Barcode hash already matches StockItem object" +msgstr "" + +#: barcode/api.py:222 +msgid "Barcode associated with StockItem" +msgstr "" + #: build/forms.py:58 msgid "Location of completed parts" msgstr "" @@ -318,7 +342,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:60 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:74 part/templates/part/part_base.html:88 -#: stock/models.py:368 stock/templates/stock/item_base.html:221 +#: stock/models.py:368 stock/templates/stock/item_base.html:229 msgid "External Link" msgstr "" @@ -398,7 +422,7 @@ msgstr "" #: build/templates/build/allocate.html:161 #: order/templates/order/sales_order_detail.html:68 #: order/templates/order/sales_order_detail.html:150 stock/models.py:362 -#: stock/templates/stock/item_base.html:180 +#: stock/templates/stock/item_base.html:188 msgid "Serial Number" msgstr "" @@ -415,7 +439,7 @@ msgstr "" #: part/templates/part/allocation.html:49 #: stock/templates/stock/item_base.html:26 #: stock/templates/stock/item_base.html:32 -#: stock/templates/stock/item_base.html:186 +#: stock/templates/stock/item_base.html:194 #: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172 #: templates/js/build.html:52 templates/js/stock.html:653 msgid "Quantity" @@ -423,7 +447,7 @@ msgstr "" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:162 +#: stock/templates/stock/item_base.html:170 #: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493 msgid "Location" msgstr "" @@ -504,7 +528,7 @@ msgstr "" #: build/templates/build/build_base.html:8 #: build/templates/build/build_base.html:34 #: build/templates/build/complete.html:6 -#: stock/templates/stock/item_base.html:200 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:208 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "" @@ -524,7 +548,7 @@ msgstr "" #: build/templates/build/build_base.html:80 #: build/templates/build/detail.html:42 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:253 templates/js/build.html:57 +#: stock/templates/stock/item_base.html:261 templates/js/build.html:57 #: templates/js/order.html:162 templates/js/order.html:235 #: templates/js/stock.html:480 msgid "Status" @@ -536,7 +560,7 @@ msgstr "" #: order/templates/order/sales_order_notes.html:10 #: order/templates/order/sales_order_ship.html:25 #: part/templates/part/allocation.html:27 -#: stock/templates/stock/item_base.html:150 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:158 templates/js/order.html:209 msgid "Sales Order" msgstr "" @@ -603,7 +627,7 @@ msgid "Stock can be taken from any available location." msgstr "" #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:193 templates/js/stock.html:488 +#: stock/templates/stock/item_base.html:201 templates/js/stock.html:488 msgid "Batch" msgstr "" @@ -950,14 +974,14 @@ msgstr "" #: company/templates/company/supplier_part_detail.html:21 order/models.py:148 #: order/templates/order/order_base.html:74 #: order/templates/order/order_wizard/select_pos.html:30 -#: stock/templates/stock/item_base.html:228 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:236 templates/js/company.html:52 #: templates/js/company.html:134 templates/js/order.html:144 msgid "Supplier" msgstr "" #: company/templates/company/detail.html:26 order/models.py:314 #: order/templates/order/sales_order_base.html:73 stock/models.py:357 -#: stock/models.py:358 stock/templates/stock/item_base.html:137 +#: stock/models.py:358 stock/templates/stock/item_base.html:145 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "" @@ -1071,7 +1095,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:6 #: company/templates/company/supplier_part_base.html:19 stock/models.py:331 -#: stock/templates/stock/item_base.html:233 templates/js/company.html:150 +#: stock/templates/stock/item_base.html:241 templates/js/company.html:150 msgid "Supplier Part" msgstr "" @@ -1354,7 +1378,7 @@ msgstr "" #: order/models.py:466 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:23 -#: stock/templates/stock/item_base.html:207 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:215 templates/js/order.html:136 msgid "Purchase Order" msgstr "" @@ -2052,14 +2076,14 @@ msgstr "" #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 #: stock/templates/stock/item_base.html:58 -#: stock/templates/stock/item_base.html:215 +#: stock/templates/stock/item_base.html:223 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 #: templates/js/stock.html:623 msgid "Stock Item" msgstr "" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:156 +#: stock/templates/stock/item_base.html:164 msgid "Build Order" msgstr "" @@ -2453,7 +2477,7 @@ msgstr "" msgid "Tracking" msgstr "" -#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:259 +#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:267 msgid "Tests" msgstr "" @@ -2646,22 +2670,6 @@ msgstr "" msgid "Confim BOM item deletion" msgstr "" -#: plugins/barcode/inventree.py:70 -msgid "Part does not exist" -msgstr "" - -#: plugins/barcode/inventree.py:79 -msgid "StockLocation does not exist" -msgstr "" - -#: plugins/barcode/inventree.py:89 -msgid "StockItem does not exist" -msgstr "" - -#: plugins/barcode/inventree.py:92 -msgid "No matching data" -msgstr "" - #: report/models.py:167 msgid "Template name" msgstr "" @@ -2731,7 +2739,7 @@ msgstr "" msgid "Parent Stock Item" msgstr "" -#: stock/models.py:322 stock/templates/stock/item_base.html:129 +#: stock/models.py:322 stock/templates/stock/item_base.html:137 msgid "Base Part" msgstr "" @@ -2937,79 +2945,107 @@ msgid "" "This stock item will be automatically deleted when all stock is depleted." msgstr "" -#: stock/templates/stock/item_base.html:74 -msgid "Add to stock" +#: stock/templates/stock/item_base.html:78 +msgid "Barcode actions" msgstr "" -#: stock/templates/stock/item_base.html:77 -msgid "Take from stock" +#: stock/templates/stock/item_base.html:80 +msgid "Show QR Code" msgstr "" -#: stock/templates/stock/item_base.html:80 templates/stock_table.html:14 -msgid "Count stock" +#: stock/templates/stock/item_base.html:81 +msgid "Print Label" +msgstr "" + +#: stock/templates/stock/item_base.html:82 +msgid "Link Barcode" msgstr "" #: stock/templates/stock/item_base.html:84 -msgid "Serialize stock" +msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:90 stock/views.py:232 -msgid "Assign to Customer" +#: stock/templates/stock/item_base.html:91 +msgid "Stock adjustment actions" msgstr "" -#: stock/templates/stock/item_base.html:94 +#: stock/templates/stock/item_base.html:94 templates/stock_table.html:14 +msgid "Count stock" +msgstr "" + +#: stock/templates/stock/item_base.html:95 templates/stock_table.html:12 +msgid "Add stock" +msgstr "" + +#: stock/templates/stock/item_base.html:96 templates/stock_table.html:13 +msgid "Remove stock" +msgstr "" + +#: stock/templates/stock/item_base.html:98 msgid "Transfer stock" msgstr "" -#: stock/templates/stock/item_base.html:97 -msgid "Duplicate stock item" -msgstr "" - -#: stock/templates/stock/item_base.html:102 -msgid "Convert stock to variant" +#: stock/templates/stock/item_base.html:104 +msgid "Stock actions" msgstr "" #: stock/templates/stock/item_base.html:107 -msgid "Generate test report" +msgid "Serialize stock" msgstr "" -#: stock/templates/stock/item_base.html:111 -msgid "Edit stock item" +#: stock/templates/stock/item_base.html:110 +msgid "Assign to customer" +msgstr "" + +#: stock/templates/stock/item_base.html:113 +msgid "Convert to variant" msgstr "" #: stock/templates/stock/item_base.html:115 +msgid "Duplicate stock item" +msgstr "" + +#: stock/templates/stock/item_base.html:116 +msgid "Edit stock item" +msgstr "" + +#: stock/templates/stock/item_base.html:118 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:124 +#: stock/templates/stock/item_base.html:123 +msgid "Generate test report" +msgstr "" + +#: stock/templates/stock/item_base.html:132 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:144 +#: stock/templates/stock/item_base.html:152 msgid "Belongs To" msgstr "" -#: stock/templates/stock/item_base.html:166 +#: stock/templates/stock/item_base.html:174 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:173 +#: stock/templates/stock/item_base.html:181 msgid "Unique Identifier" msgstr "" -#: stock/templates/stock/item_base.html:214 +#: stock/templates/stock/item_base.html:222 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:239 +#: stock/templates/stock/item_base.html:247 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:244 +#: stock/templates/stock/item_base.html:252 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:248 +#: stock/templates/stock/item_base.html:256 msgid "No stocktake performed" msgstr "" @@ -3128,6 +3164,10 @@ msgstr "" msgid "Delete Stock Item Attachment" msgstr "" +#: stock/views.py:232 +msgid "Assign to Customer" +msgstr "" + #: stock/views.py:270 msgid "Delete All Test Data" msgstr "" @@ -3251,7 +3291,7 @@ msgid "Create new Stock Item" msgstr "" #: stock/views.py:1167 -msgid "Copy Stock Item" +msgid "Duplicate Stock Item" msgstr "" #: stock/views.py:1240 @@ -3635,42 +3675,38 @@ msgstr "" msgid "Sell" msgstr "" -#: templates/navbar.html:36 +#: templates/navbar.html:32 +msgid "Scan Barcode" +msgstr "" + +#: templates/navbar.html:41 msgid "Admin" msgstr "" -#: templates/navbar.html:39 +#: templates/navbar.html:44 msgid "Settings" msgstr "" -#: templates/navbar.html:40 +#: templates/navbar.html:45 msgid "Logout" msgstr "" -#: templates/navbar.html:42 +#: templates/navbar.html:47 msgid "Login" msgstr "" -#: templates/navbar.html:45 +#: templates/navbar.html:50 msgid "About InvenTree" msgstr "" -#: templates/navbar.html:46 +#: templates/navbar.html:51 msgid "Statistics" msgstr "" -#: templates/search_form.html:6 +#: templates/search_form.html:6 templates/search_form.html:8 msgid "Search" msgstr "" -#: templates/stock_table.html:12 -msgid "Add stock" -msgstr "" - -#: templates/stock_table.html:13 -msgid "Remove stock" -msgstr "" - #: templates/stock_table.html:15 msgid "Move stock" msgstr "" diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 6f9eeeec2e..49db90d578 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -1164,7 +1164,7 @@ class StockItemCreate(AjaxCreateView): try: original = StockItem.objects.get(pk=item_to_copy) initials = model_to_dict(original) - self.ajax_form_title = _("Copy Stock Item") + self.ajax_form_title = _("Duplicate Stock Item") except StockItem.DoesNotExist: initials = super(StockItemCreate, self).get_initial().copy() From 6064c6ceb544f8b82db472c806dc37cdea74153f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 12 Jun 2020 10:26:23 +1000 Subject: [PATCH 17/24] Add dialog for linking a barcode with a stock item --- .../static/script/inventree/barcode.js | 138 +++++++++++------- InvenTree/barcode/api.py | 2 +- InvenTree/barcode/tests.py | 2 +- .../stock/templates/stock/item_base.html | 7 +- 4 files changed, 90 insertions(+), 59 deletions(-) diff --git a/InvenTree/InvenTree/static/script/inventree/barcode.js b/InvenTree/InvenTree/static/script/inventree/barcode.js index d4c87f361e..2ccf4a69d9 100644 --- a/InvenTree/InvenTree/static/script/inventree/barcode.js +++ b/InvenTree/InvenTree/static/script/inventree/barcode.js @@ -22,60 +22,6 @@ function scanBarcode(barcode, options={}) { } -function unlinkBarcode(stockitem) { - /* - * Remove barcode association from a device. - */ - - showQuestionDialog( - "Unlink Barcode", - "Remove barcode association from this Stock Item", - { - accept_text: "Unlink", - accept: function() { - inventreePut( - `/api/stock/${stockitem}/`, - { - // Clear the UID field - uid: '', - }, - { - method: 'PATCH', - success: function(response, status) { - location.reload(); - }, - }, - ); - }, - } - ); -} - - -/* - * Associate barcode data with a StockItem - */ -function associateBarcode(barcode, stockitem, options={}) { - - console.log('Associating barcode data:'); - console.log('barcode: ' + barcode); - - inventreePut( - '/api/barcode/assign/', - { - 'barcode': barcode, - 'stockitem': stockitem, - }, - { - method: 'POST', - success: function(response, status) { - console.log(response); - }, - } - ); -} - - function makeBarcodeInput(placeholderText='') { /* * Generate HTML for a barcode input @@ -191,6 +137,8 @@ function barcodeDialog(title, options={}) { content += options.headerContent; } + content += `
Scan barcode data below
`; + content += makeBarcodeInput(); if (options.footerContent) { @@ -219,7 +167,6 @@ function barcodeScanDialog() { barcodeDialog( "Scan Barcode", { - headerContent: `
Scan barcode data below
`, submit: function(barcode) { enableBarcodeInput(modal, false); inventreePut( @@ -258,4 +205,83 @@ function barcodeScanDialog() { }, }, ); -} \ No newline at end of file +} + + +/* + * Dialog for linking a particular barcode to a stock item. + */ +function linkBarcodeDialog(stockitem, options={}) { + + var modal = '#modal-form'; + + barcodeDialog( + "Link Barcode", + { + submit: function(barcode) { + enableBarcodeInput(modal, false); + inventreePut( + '/api/barcode/link/', + { + barcode: barcode, + stockitem: stockitem, + }, + { + method: 'POST', + success: function(response, status) { + + console.log(response); + + enableBarcodeInput(modal, true); + + if (status == 'success') { + + if ('success' in response) { + $(modal).modal('hide'); + location.reload(); + } else if ('error' in response) { + showBarcodeError(modal, response.error, 'warning'); + } else { + showBarcodeError(modal, "Unknown response from server", warning); + } + + } else { + showBarcodeError(modal, `Invalid server response.
Status code: '${status}'`); + } + }, + }, + ); + } + } + ); +} + + +/* + * Remove barcode association from a device. + */ +function unlinkBarcode(stockitem) { + + showQuestionDialog( + "Unlink Barcode", + "Remove barcode association from this Stock Item", + { + accept_text: "Unlink", + accept: function() { + inventreePut( + `/api/stock/${stockitem}/`, + { + // Clear the UID field + uid: '', + }, + { + method: 'PATCH', + success: function(response, status) { + location.reload(); + }, + }, + ); + }, + } + ); +} diff --git a/InvenTree/barcode/api.py b/InvenTree/barcode/api.py index c4872ec781..cecdf0b349 100644 --- a/InvenTree/barcode/api.py +++ b/InvenTree/barcode/api.py @@ -233,7 +233,7 @@ class BarcodeAssign(APIView): barcode_api_urls = [ - url(r'^assign/$', BarcodeAssign.as_view(), name='api-barcode-assign'), + url(r'^link/$', BarcodeAssign.as_view(), name='api-barcode-link'), # Catch-all performs barcode 'scan' url(r'^.*$', BarcodeScan.as_view(), name='api-barcode-scan'), diff --git a/InvenTree/barcode/tests.py b/InvenTree/barcode/tests.py index 260dff5249..98e126eba1 100644 --- a/InvenTree/barcode/tests.py +++ b/InvenTree/barcode/tests.py @@ -30,7 +30,7 @@ class BarcodeAPITest(APITestCase): self.client.login(username='testuser', password='password') self.scan_url = reverse('api-barcode-scan') - self.assign_url = reverse('api-barcode-assign') + self.assign_url = reverse('api-barcode-link') def postBarcode(self, url, barcode): diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 837509c975..7b9f083f48 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -79,9 +79,10 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} @@ -338,6 +339,10 @@ $("#show-qr-code").click(function() { }); }); +$("#link-barcode").click(function() { + linkBarcodeDialog({{ item.id }}); +}); + $("#unlink-barcode").click(function() { unlinkBarcode({{ item.id }}); }); From 8cafaa0e62cbd11abae35166b9abc971ec7b965a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 12 Jun 2020 10:43:27 +1000 Subject: [PATCH 18/24] Make barcode javascript translatable --- InvenTree/InvenTree/urls.py | 2 + InvenTree/locale/de/LC_MESSAGES/django.po | 140 +++++++++++------- InvenTree/locale/en/LC_MESSAGES/django.po | 120 +++++++++------ InvenTree/locale/es/LC_MESSAGES/django.po | 120 +++++++++------ InvenTree/templates/base.html | 2 +- .../barcode.js => templates/js/barcode.html} | 32 ++-- 6 files changed, 264 insertions(+), 152 deletions(-) rename InvenTree/{InvenTree/static/script/inventree/barcode.js => templates/js/barcode.html} (84%) diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index 92ea257ce2..c2d0d0f48f 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -76,7 +76,9 @@ settings_urls = [ url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'), ] +# Some javascript files are served 'dynamically', allowing them to pass through the Django translation layer dynamic_javascript_urls = [ + url(r'^barcode.js', DynamicJsView.as_view(template_name='js/barcode.html'), name='barcode.js'), url(r'^part.js', DynamicJsView.as_view(template_name='js/part.html'), name='part.js'), url(r'^stock.js', DynamicJsView.as_view(template_name='js/stock.html'), name='stock.js'), url(r'^build.js', DynamicJsView.as_view(template_name='js/build.html'), name='build.js'), diff --git a/InvenTree/locale/de/LC_MESSAGES/django.po b/InvenTree/locale/de/LC_MESSAGES/django.po index 2d5cb5b980..18a24b1387 100644 --- a/InvenTree/locale/de/LC_MESSAGES/django.po +++ b/InvenTree/locale/de/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-12 00:13+0000\n" +"POT-Creation-Date: 2020-06-12 00:43+0000\n" "PO-Revision-Date: 2020-05-03 11:32+0200\n" "Last-Translator: Christian Schlüter \n" "Language-Team: C \n" @@ -361,7 +361,7 @@ msgstr "Chargennummer für diese Bau-Ausgabe" #: company/templates/company/supplier_part_base.html:60 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:74 part/templates/part/part_base.html:88 -#: stock/models.py:368 stock/templates/stock/item_base.html:229 +#: stock/models.py:368 stock/templates/stock/item_base.html:230 msgid "External Link" msgstr "Externer Link" @@ -442,7 +442,7 @@ msgstr "Neues Lagerobjekt" #: build/templates/build/allocate.html:161 #: order/templates/order/sales_order_detail.html:68 #: order/templates/order/sales_order_detail.html:150 stock/models.py:362 -#: stock/templates/stock/item_base.html:188 +#: stock/templates/stock/item_base.html:189 msgid "Serial Number" msgstr "Seriennummer" @@ -459,7 +459,7 @@ msgstr "Seriennummer" #: part/templates/part/allocation.html:49 #: stock/templates/stock/item_base.html:26 #: stock/templates/stock/item_base.html:32 -#: stock/templates/stock/item_base.html:194 +#: stock/templates/stock/item_base.html:195 #: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172 #: templates/js/build.html:52 templates/js/stock.html:653 msgid "Quantity" @@ -467,7 +467,7 @@ msgstr "Anzahl" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:170 +#: stock/templates/stock/item_base.html:171 #: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493 msgid "Location" msgstr "Standort" @@ -549,7 +549,7 @@ msgstr "Keine Lagerobjekt gefunden, die diesem Bau zugewiesen werden können" #: build/templates/build/build_base.html:8 #: build/templates/build/build_base.html:34 #: build/templates/build/complete.html:6 -#: stock/templates/stock/item_base.html:208 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:209 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "Bau" @@ -569,7 +569,7 @@ msgstr "Bau-Status" #: build/templates/build/build_base.html:80 #: build/templates/build/detail.html:42 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:261 templates/js/build.html:57 +#: stock/templates/stock/item_base.html:262 templates/js/build.html:57 #: templates/js/order.html:162 templates/js/order.html:235 #: templates/js/stock.html:480 msgid "Status" @@ -581,7 +581,7 @@ msgstr "Status" #: order/templates/order/sales_order_notes.html:10 #: order/templates/order/sales_order_ship.html:25 #: part/templates/part/allocation.html:27 -#: stock/templates/stock/item_base.html:158 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:159 templates/js/order.html:209 msgid "Sales Order" msgstr "Bestellung" @@ -650,7 +650,7 @@ msgid "Stock can be taken from any available location." msgstr "Bestand kann jedem verfügbaren Lagerort entnommen werden." #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:201 templates/js/stock.html:488 +#: stock/templates/stock/item_base.html:202 templates/js/stock.html:488 msgid "Batch" msgstr "Los" @@ -1004,14 +1004,14 @@ msgstr "Hersteller" #: company/templates/company/supplier_part_detail.html:21 order/models.py:148 #: order/templates/order/order_base.html:74 #: order/templates/order/order_wizard/select_pos.html:30 -#: stock/templates/stock/item_base.html:236 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:237 templates/js/company.html:52 #: templates/js/company.html:134 templates/js/order.html:144 msgid "Supplier" msgstr "Zulieferer" #: company/templates/company/detail.html:26 order/models.py:314 #: order/templates/order/sales_order_base.html:73 stock/models.py:357 -#: stock/models.py:358 stock/templates/stock/item_base.html:145 +#: stock/models.py:358 stock/templates/stock/item_base.html:146 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "Kunde" @@ -1126,7 +1126,7 @@ msgstr "Neuer Auftrag" #: company/templates/company/supplier_part_base.html:6 #: company/templates/company/supplier_part_base.html:19 stock/models.py:331 -#: stock/templates/stock/item_base.html:241 templates/js/company.html:150 +#: stock/templates/stock/item_base.html:242 templates/js/company.html:150 msgid "Supplier Part" msgstr "Zulieferer-Teil" @@ -1413,7 +1413,7 @@ msgstr "Position - Notizen" #: order/models.py:466 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:23 -#: stock/templates/stock/item_base.html:215 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:216 templates/js/order.html:136 msgid "Purchase Order" msgstr "Kaufvertrag" @@ -2142,14 +2142,14 @@ msgstr "Bestellung" #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 #: stock/templates/stock/item_base.html:58 -#: stock/templates/stock/item_base.html:223 +#: stock/templates/stock/item_base.html:224 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 #: templates/js/stock.html:623 msgid "Stock Item" msgstr "Lagerobjekt" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:164 +#: stock/templates/stock/item_base.html:165 msgid "Build Order" msgstr "Bauauftrag" @@ -2567,7 +2567,7 @@ msgstr "Benutzt in" msgid "Tracking" msgstr "Tracking" -#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:267 +#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:268 msgid "Tests" msgstr "" @@ -2846,7 +2846,7 @@ msgstr "Teil kann nicht zu sich selbst gehören" msgid "Parent Stock Item" msgstr "Eltern-Lagerobjekt" -#: stock/models.py:322 stock/templates/stock/item_base.html:137 +#: stock/models.py:322 stock/templates/stock/item_base.html:138 msgid "Base Part" msgstr "Basisteil" @@ -3094,115 +3094,116 @@ msgstr "Teil-QR-Code" msgid "Print Label" msgstr "" -#: stock/templates/stock/item_base.html:82 -msgid "Link Barcode" -msgstr "" - -#: stock/templates/stock/item_base.html:84 +#: stock/templates/stock/item_base.html:83 templates/js/barcode.html:263 +#: templates/js/barcode.html:268 msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:91 +#: stock/templates/stock/item_base.html:85 +msgid "Link Barcode" +msgstr "" + +#: stock/templates/stock/item_base.html:92 #, fuzzy #| msgid "Confirm stock adjustment" msgid "Stock adjustment actions" msgstr "Bestands-Anpassung bestätigen" -#: stock/templates/stock/item_base.html:94 templates/stock_table.html:14 +#: stock/templates/stock/item_base.html:95 templates/stock_table.html:14 msgid "Count stock" msgstr "Bestand zählen" -#: stock/templates/stock/item_base.html:95 templates/stock_table.html:12 +#: stock/templates/stock/item_base.html:96 templates/stock_table.html:12 msgid "Add stock" msgstr "Bestand hinzufügen" -#: stock/templates/stock/item_base.html:96 templates/stock_table.html:13 +#: stock/templates/stock/item_base.html:97 templates/stock_table.html:13 msgid "Remove stock" msgstr "Bestand entfernen" -#: stock/templates/stock/item_base.html:98 +#: stock/templates/stock/item_base.html:99 #, fuzzy #| msgid "Order stock" msgid "Transfer stock" msgstr "Bestand bestellen" -#: stock/templates/stock/item_base.html:104 +#: stock/templates/stock/item_base.html:105 #, fuzzy #| msgid "Stock Locations" msgid "Stock actions" msgstr "Lagerobjekt-Standorte" -#: stock/templates/stock/item_base.html:107 +#: stock/templates/stock/item_base.html:108 #, fuzzy #| msgid "Serialize Stock" msgid "Serialize stock" msgstr "Lagerbestand erfassen" -#: stock/templates/stock/item_base.html:110 +#: stock/templates/stock/item_base.html:111 #, fuzzy #| msgid "Item assigned to customer?" msgid "Assign to customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/templates/stock/item_base.html:113 +#: stock/templates/stock/item_base.html:114 #, fuzzy #| msgid "Count stock items" msgid "Convert to variant" msgstr "Lagerobjekte zählen" -#: stock/templates/stock/item_base.html:115 +#: stock/templates/stock/item_base.html:116 #, fuzzy #| msgid "Count stock items" msgid "Duplicate stock item" msgstr "Lagerobjekte zählen" -#: stock/templates/stock/item_base.html:116 +#: stock/templates/stock/item_base.html:117 #, fuzzy #| msgid "Edit Stock Item" msgid "Edit stock item" msgstr "Lagerobjekt bearbeiten" -#: stock/templates/stock/item_base.html:118 +#: stock/templates/stock/item_base.html:119 #, fuzzy #| msgid "Delete Stock Item" msgid "Delete stock item" msgstr "Lagerobjekt löschen" -#: stock/templates/stock/item_base.html:123 +#: stock/templates/stock/item_base.html:124 msgid "Generate test report" msgstr "" -#: stock/templates/stock/item_base.html:132 +#: stock/templates/stock/item_base.html:133 msgid "Stock Item Details" msgstr "Lagerbestands-Details" -#: stock/templates/stock/item_base.html:152 +#: stock/templates/stock/item_base.html:153 msgid "Belongs To" msgstr "Gehört zu" -#: stock/templates/stock/item_base.html:174 +#: stock/templates/stock/item_base.html:175 #, fuzzy #| msgid "No stock location set" msgid "No location set" msgstr "Kein Lagerort gesetzt" -#: stock/templates/stock/item_base.html:181 +#: stock/templates/stock/item_base.html:182 msgid "Unique Identifier" msgstr "Eindeutiger Bezeichner" -#: stock/templates/stock/item_base.html:222 +#: stock/templates/stock/item_base.html:223 msgid "Parent Item" msgstr "Elternposition" -#: stock/templates/stock/item_base.html:247 +#: stock/templates/stock/item_base.html:248 msgid "Last Updated" msgstr "Zuletzt aktualisiert" -#: stock/templates/stock/item_base.html:252 +#: stock/templates/stock/item_base.html:253 msgid "Last Stocktake" msgstr "Letzte Inventur" -#: stock/templates/stock/item_base.html:256 +#: stock/templates/stock/item_base.html:257 msgid "No stocktake performed" msgstr "Keine Inventur ausgeführt" @@ -3602,6 +3603,49 @@ msgstr "" msgid "Delete attachment" msgstr "Anhang löschen" +#: templates/js/barcode.html:28 +#, fuzzy +#| msgid "No barcode data provided" +msgid "Scan barcode data here using wedge scanner" +msgstr "Keine Strichcodedaten bereitgestellt" + +#: templates/js/barcode.html:34 +#, fuzzy +#| msgid "Source Location" +msgid "Barcode" +msgstr "Quell-Standort" + +#: templates/js/barcode.html:42 +#, fuzzy +#| msgid "No barcode data provided" +msgid "Enter barcode data" +msgstr "Keine Strichcodedaten bereitgestellt" + +#: templates/js/barcode.html:140 +#, fuzzy +#| msgid "No barcode data provided" +msgid "Scan barcode data below" +msgstr "Keine Strichcodedaten bereitgestellt" + +#: templates/js/barcode.html:195 templates/js/barcode.html:243 +#, fuzzy +#| msgid "Unknown barcode format" +msgid "Unknown response from server" +msgstr "Unbekanntes Strichcode-Format" + +#: templates/js/barcode.html:198 templates/js/barcode.html:247 +msgid "Invalid server response" +msgstr "" + +#: templates/js/barcode.html:265 +msgid "" +"This will remove the association between this stock item and the barcode" +msgstr "" + +#: templates/js/barcode.html:271 +msgid "Unlink" +msgstr "" + #: templates/js/bom.html:143 msgid "Open subassembly" msgstr "Unterbaugruppe öffnen" @@ -3745,8 +3789,10 @@ msgid "NO RESULT" msgstr "" #: templates/js/stock.html:58 +#, fuzzy +#| msgid "Edit Sales Order" msgid "Add test result" -msgstr "" +msgstr "Auftrag bearbeiten" #: templates/js/stock.html:76 #, fuzzy @@ -3928,18 +3974,12 @@ msgstr "Bestand bestellen" msgid "Delete Stock" msgstr "Bestand löschen" -#~ msgid "No barcode data provided" -#~ msgstr "Keine Strichcodedaten bereitgestellt" - #~ msgid "Barcode successfully decoded" #~ msgstr "Strichcode erfolgreich dekodiert" #~ msgid "Barcode plugin returned incorrect response" #~ msgstr "Ungültige Antwort vom Strichcode-Plugin" -#~ msgid "Unknown barcode format" -#~ msgstr "Unbekanntes Strichcode-Format" - #~ msgid "Part does not exist" #~ msgstr "Teil existiert nicht" diff --git a/InvenTree/locale/en/LC_MESSAGES/django.po b/InvenTree/locale/en/LC_MESSAGES/django.po index 70f1e96161..c539fbcae5 100644 --- a/InvenTree/locale/en/LC_MESSAGES/django.po +++ b/InvenTree/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-12 00:13+0000\n" +"POT-Creation-Date: 2020-06-12 00:43+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -342,7 +342,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:60 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:74 part/templates/part/part_base.html:88 -#: stock/models.py:368 stock/templates/stock/item_base.html:229 +#: stock/models.py:368 stock/templates/stock/item_base.html:230 msgid "External Link" msgstr "" @@ -422,7 +422,7 @@ msgstr "" #: build/templates/build/allocate.html:161 #: order/templates/order/sales_order_detail.html:68 #: order/templates/order/sales_order_detail.html:150 stock/models.py:362 -#: stock/templates/stock/item_base.html:188 +#: stock/templates/stock/item_base.html:189 msgid "Serial Number" msgstr "" @@ -439,7 +439,7 @@ msgstr "" #: part/templates/part/allocation.html:49 #: stock/templates/stock/item_base.html:26 #: stock/templates/stock/item_base.html:32 -#: stock/templates/stock/item_base.html:194 +#: stock/templates/stock/item_base.html:195 #: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172 #: templates/js/build.html:52 templates/js/stock.html:653 msgid "Quantity" @@ -447,7 +447,7 @@ msgstr "" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:170 +#: stock/templates/stock/item_base.html:171 #: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493 msgid "Location" msgstr "" @@ -528,7 +528,7 @@ msgstr "" #: build/templates/build/build_base.html:8 #: build/templates/build/build_base.html:34 #: build/templates/build/complete.html:6 -#: stock/templates/stock/item_base.html:208 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:209 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "" @@ -548,7 +548,7 @@ msgstr "" #: build/templates/build/build_base.html:80 #: build/templates/build/detail.html:42 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:261 templates/js/build.html:57 +#: stock/templates/stock/item_base.html:262 templates/js/build.html:57 #: templates/js/order.html:162 templates/js/order.html:235 #: templates/js/stock.html:480 msgid "Status" @@ -560,7 +560,7 @@ msgstr "" #: order/templates/order/sales_order_notes.html:10 #: order/templates/order/sales_order_ship.html:25 #: part/templates/part/allocation.html:27 -#: stock/templates/stock/item_base.html:158 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:159 templates/js/order.html:209 msgid "Sales Order" msgstr "" @@ -627,7 +627,7 @@ msgid "Stock can be taken from any available location." msgstr "" #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:201 templates/js/stock.html:488 +#: stock/templates/stock/item_base.html:202 templates/js/stock.html:488 msgid "Batch" msgstr "" @@ -974,14 +974,14 @@ msgstr "" #: company/templates/company/supplier_part_detail.html:21 order/models.py:148 #: order/templates/order/order_base.html:74 #: order/templates/order/order_wizard/select_pos.html:30 -#: stock/templates/stock/item_base.html:236 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:237 templates/js/company.html:52 #: templates/js/company.html:134 templates/js/order.html:144 msgid "Supplier" msgstr "" #: company/templates/company/detail.html:26 order/models.py:314 #: order/templates/order/sales_order_base.html:73 stock/models.py:357 -#: stock/models.py:358 stock/templates/stock/item_base.html:145 +#: stock/models.py:358 stock/templates/stock/item_base.html:146 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "" @@ -1095,7 +1095,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:6 #: company/templates/company/supplier_part_base.html:19 stock/models.py:331 -#: stock/templates/stock/item_base.html:241 templates/js/company.html:150 +#: stock/templates/stock/item_base.html:242 templates/js/company.html:150 msgid "Supplier Part" msgstr "" @@ -1378,7 +1378,7 @@ msgstr "" #: order/models.py:466 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:23 -#: stock/templates/stock/item_base.html:215 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:216 templates/js/order.html:136 msgid "Purchase Order" msgstr "" @@ -2076,14 +2076,14 @@ msgstr "" #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 #: stock/templates/stock/item_base.html:58 -#: stock/templates/stock/item_base.html:223 +#: stock/templates/stock/item_base.html:224 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 #: templates/js/stock.html:623 msgid "Stock Item" msgstr "" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:164 +#: stock/templates/stock/item_base.html:165 msgid "Build Order" msgstr "" @@ -2477,7 +2477,7 @@ msgstr "" msgid "Tracking" msgstr "" -#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:267 +#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:268 msgid "Tests" msgstr "" @@ -2739,7 +2739,7 @@ msgstr "" msgid "Parent Stock Item" msgstr "" -#: stock/models.py:322 stock/templates/stock/item_base.html:137 +#: stock/models.py:322 stock/templates/stock/item_base.html:138 msgid "Base Part" msgstr "" @@ -2957,95 +2957,96 @@ msgstr "" msgid "Print Label" msgstr "" -#: stock/templates/stock/item_base.html:82 -msgid "Link Barcode" -msgstr "" - -#: stock/templates/stock/item_base.html:84 +#: stock/templates/stock/item_base.html:83 templates/js/barcode.html:263 +#: templates/js/barcode.html:268 msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:91 +#: stock/templates/stock/item_base.html:85 +msgid "Link Barcode" +msgstr "" + +#: stock/templates/stock/item_base.html:92 msgid "Stock adjustment actions" msgstr "" -#: stock/templates/stock/item_base.html:94 templates/stock_table.html:14 +#: stock/templates/stock/item_base.html:95 templates/stock_table.html:14 msgid "Count stock" msgstr "" -#: stock/templates/stock/item_base.html:95 templates/stock_table.html:12 +#: stock/templates/stock/item_base.html:96 templates/stock_table.html:12 msgid "Add stock" msgstr "" -#: stock/templates/stock/item_base.html:96 templates/stock_table.html:13 +#: stock/templates/stock/item_base.html:97 templates/stock_table.html:13 msgid "Remove stock" msgstr "" -#: stock/templates/stock/item_base.html:98 +#: stock/templates/stock/item_base.html:99 msgid "Transfer stock" msgstr "" -#: stock/templates/stock/item_base.html:104 +#: stock/templates/stock/item_base.html:105 msgid "Stock actions" msgstr "" -#: stock/templates/stock/item_base.html:107 +#: stock/templates/stock/item_base.html:108 msgid "Serialize stock" msgstr "" -#: stock/templates/stock/item_base.html:110 +#: stock/templates/stock/item_base.html:111 msgid "Assign to customer" msgstr "" -#: stock/templates/stock/item_base.html:113 +#: stock/templates/stock/item_base.html:114 msgid "Convert to variant" msgstr "" -#: stock/templates/stock/item_base.html:115 +#: stock/templates/stock/item_base.html:116 msgid "Duplicate stock item" msgstr "" -#: stock/templates/stock/item_base.html:116 +#: stock/templates/stock/item_base.html:117 msgid "Edit stock item" msgstr "" -#: stock/templates/stock/item_base.html:118 +#: stock/templates/stock/item_base.html:119 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:123 +#: stock/templates/stock/item_base.html:124 msgid "Generate test report" msgstr "" -#: stock/templates/stock/item_base.html:132 +#: stock/templates/stock/item_base.html:133 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:152 +#: stock/templates/stock/item_base.html:153 msgid "Belongs To" msgstr "" -#: stock/templates/stock/item_base.html:174 +#: stock/templates/stock/item_base.html:175 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:181 +#: stock/templates/stock/item_base.html:182 msgid "Unique Identifier" msgstr "" -#: stock/templates/stock/item_base.html:222 +#: stock/templates/stock/item_base.html:223 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:247 +#: stock/templates/stock/item_base.html:248 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:252 +#: stock/templates/stock/item_base.html:253 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:256 +#: stock/templates/stock/item_base.html:257 msgid "No stocktake performed" msgstr "" @@ -3415,6 +3416,39 @@ msgstr "" msgid "Delete attachment" msgstr "" +#: templates/js/barcode.html:28 +msgid "Scan barcode data here using wedge scanner" +msgstr "" + +#: templates/js/barcode.html:34 +msgid "Barcode" +msgstr "" + +#: templates/js/barcode.html:42 +msgid "Enter barcode data" +msgstr "" + +#: templates/js/barcode.html:140 +msgid "Scan barcode data below" +msgstr "" + +#: templates/js/barcode.html:195 templates/js/barcode.html:243 +msgid "Unknown response from server" +msgstr "" + +#: templates/js/barcode.html:198 templates/js/barcode.html:247 +msgid "Invalid server response" +msgstr "" + +#: templates/js/barcode.html:265 +msgid "" +"This will remove the association between this stock item and the barcode" +msgstr "" + +#: templates/js/barcode.html:271 +msgid "Unlink" +msgstr "" + #: templates/js/bom.html:143 msgid "Open subassembly" msgstr "" diff --git a/InvenTree/locale/es/LC_MESSAGES/django.po b/InvenTree/locale/es/LC_MESSAGES/django.po index 70f1e96161..c539fbcae5 100644 --- a/InvenTree/locale/es/LC_MESSAGES/django.po +++ b/InvenTree/locale/es/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-06-12 00:13+0000\n" +"POT-Creation-Date: 2020-06-12 00:43+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -342,7 +342,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:60 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:74 part/templates/part/part_base.html:88 -#: stock/models.py:368 stock/templates/stock/item_base.html:229 +#: stock/models.py:368 stock/templates/stock/item_base.html:230 msgid "External Link" msgstr "" @@ -422,7 +422,7 @@ msgstr "" #: build/templates/build/allocate.html:161 #: order/templates/order/sales_order_detail.html:68 #: order/templates/order/sales_order_detail.html:150 stock/models.py:362 -#: stock/templates/stock/item_base.html:188 +#: stock/templates/stock/item_base.html:189 msgid "Serial Number" msgstr "" @@ -439,7 +439,7 @@ msgstr "" #: part/templates/part/allocation.html:49 #: stock/templates/stock/item_base.html:26 #: stock/templates/stock/item_base.html:32 -#: stock/templates/stock/item_base.html:194 +#: stock/templates/stock/item_base.html:195 #: stock/templates/stock/stock_adjust.html:18 templates/js/bom.html:172 #: templates/js/build.html:52 templates/js/stock.html:653 msgid "Quantity" @@ -447,7 +447,7 @@ msgstr "" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:170 +#: stock/templates/stock/item_base.html:171 #: stock/templates/stock/stock_adjust.html:17 templates/js/stock.html:493 msgid "Location" msgstr "" @@ -528,7 +528,7 @@ msgstr "" #: build/templates/build/build_base.html:8 #: build/templates/build/build_base.html:34 #: build/templates/build/complete.html:6 -#: stock/templates/stock/item_base.html:208 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:209 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "" @@ -548,7 +548,7 @@ msgstr "" #: build/templates/build/build_base.html:80 #: build/templates/build/detail.html:42 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:261 templates/js/build.html:57 +#: stock/templates/stock/item_base.html:262 templates/js/build.html:57 #: templates/js/order.html:162 templates/js/order.html:235 #: templates/js/stock.html:480 msgid "Status" @@ -560,7 +560,7 @@ msgstr "" #: order/templates/order/sales_order_notes.html:10 #: order/templates/order/sales_order_ship.html:25 #: part/templates/part/allocation.html:27 -#: stock/templates/stock/item_base.html:158 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:159 templates/js/order.html:209 msgid "Sales Order" msgstr "" @@ -627,7 +627,7 @@ msgid "Stock can be taken from any available location." msgstr "" #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:201 templates/js/stock.html:488 +#: stock/templates/stock/item_base.html:202 templates/js/stock.html:488 msgid "Batch" msgstr "" @@ -974,14 +974,14 @@ msgstr "" #: company/templates/company/supplier_part_detail.html:21 order/models.py:148 #: order/templates/order/order_base.html:74 #: order/templates/order/order_wizard/select_pos.html:30 -#: stock/templates/stock/item_base.html:236 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:237 templates/js/company.html:52 #: templates/js/company.html:134 templates/js/order.html:144 msgid "Supplier" msgstr "" #: company/templates/company/detail.html:26 order/models.py:314 #: order/templates/order/sales_order_base.html:73 stock/models.py:357 -#: stock/models.py:358 stock/templates/stock/item_base.html:145 +#: stock/models.py:358 stock/templates/stock/item_base.html:146 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "" @@ -1095,7 +1095,7 @@ msgstr "" #: company/templates/company/supplier_part_base.html:6 #: company/templates/company/supplier_part_base.html:19 stock/models.py:331 -#: stock/templates/stock/item_base.html:241 templates/js/company.html:150 +#: stock/templates/stock/item_base.html:242 templates/js/company.html:150 msgid "Supplier Part" msgstr "" @@ -1378,7 +1378,7 @@ msgstr "" #: order/models.py:466 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:23 -#: stock/templates/stock/item_base.html:215 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:216 templates/js/order.html:136 msgid "Purchase Order" msgstr "" @@ -2076,14 +2076,14 @@ msgstr "" #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 #: stock/templates/stock/item_base.html:58 -#: stock/templates/stock/item_base.html:223 +#: stock/templates/stock/item_base.html:224 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 #: templates/js/stock.html:623 msgid "Stock Item" msgstr "" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:164 +#: stock/templates/stock/item_base.html:165 msgid "Build Order" msgstr "" @@ -2477,7 +2477,7 @@ msgstr "" msgid "Tracking" msgstr "" -#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:267 +#: part/templates/part/tabs.html:64 stock/templates/stock/item_base.html:268 msgid "Tests" msgstr "" @@ -2739,7 +2739,7 @@ msgstr "" msgid "Parent Stock Item" msgstr "" -#: stock/models.py:322 stock/templates/stock/item_base.html:137 +#: stock/models.py:322 stock/templates/stock/item_base.html:138 msgid "Base Part" msgstr "" @@ -2957,95 +2957,96 @@ msgstr "" msgid "Print Label" msgstr "" -#: stock/templates/stock/item_base.html:82 -msgid "Link Barcode" -msgstr "" - -#: stock/templates/stock/item_base.html:84 +#: stock/templates/stock/item_base.html:83 templates/js/barcode.html:263 +#: templates/js/barcode.html:268 msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:91 +#: stock/templates/stock/item_base.html:85 +msgid "Link Barcode" +msgstr "" + +#: stock/templates/stock/item_base.html:92 msgid "Stock adjustment actions" msgstr "" -#: stock/templates/stock/item_base.html:94 templates/stock_table.html:14 +#: stock/templates/stock/item_base.html:95 templates/stock_table.html:14 msgid "Count stock" msgstr "" -#: stock/templates/stock/item_base.html:95 templates/stock_table.html:12 +#: stock/templates/stock/item_base.html:96 templates/stock_table.html:12 msgid "Add stock" msgstr "" -#: stock/templates/stock/item_base.html:96 templates/stock_table.html:13 +#: stock/templates/stock/item_base.html:97 templates/stock_table.html:13 msgid "Remove stock" msgstr "" -#: stock/templates/stock/item_base.html:98 +#: stock/templates/stock/item_base.html:99 msgid "Transfer stock" msgstr "" -#: stock/templates/stock/item_base.html:104 +#: stock/templates/stock/item_base.html:105 msgid "Stock actions" msgstr "" -#: stock/templates/stock/item_base.html:107 +#: stock/templates/stock/item_base.html:108 msgid "Serialize stock" msgstr "" -#: stock/templates/stock/item_base.html:110 +#: stock/templates/stock/item_base.html:111 msgid "Assign to customer" msgstr "" -#: stock/templates/stock/item_base.html:113 +#: stock/templates/stock/item_base.html:114 msgid "Convert to variant" msgstr "" -#: stock/templates/stock/item_base.html:115 +#: stock/templates/stock/item_base.html:116 msgid "Duplicate stock item" msgstr "" -#: stock/templates/stock/item_base.html:116 +#: stock/templates/stock/item_base.html:117 msgid "Edit stock item" msgstr "" -#: stock/templates/stock/item_base.html:118 +#: stock/templates/stock/item_base.html:119 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:123 +#: stock/templates/stock/item_base.html:124 msgid "Generate test report" msgstr "" -#: stock/templates/stock/item_base.html:132 +#: stock/templates/stock/item_base.html:133 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:152 +#: stock/templates/stock/item_base.html:153 msgid "Belongs To" msgstr "" -#: stock/templates/stock/item_base.html:174 +#: stock/templates/stock/item_base.html:175 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:181 +#: stock/templates/stock/item_base.html:182 msgid "Unique Identifier" msgstr "" -#: stock/templates/stock/item_base.html:222 +#: stock/templates/stock/item_base.html:223 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:247 +#: stock/templates/stock/item_base.html:248 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:252 +#: stock/templates/stock/item_base.html:253 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:256 +#: stock/templates/stock/item_base.html:257 msgid "No stocktake performed" msgstr "" @@ -3415,6 +3416,39 @@ msgstr "" msgid "Delete attachment" msgstr "" +#: templates/js/barcode.html:28 +msgid "Scan barcode data here using wedge scanner" +msgstr "" + +#: templates/js/barcode.html:34 +msgid "Barcode" +msgstr "" + +#: templates/js/barcode.html:42 +msgid "Enter barcode data" +msgstr "" + +#: templates/js/barcode.html:140 +msgid "Scan barcode data below" +msgstr "" + +#: templates/js/barcode.html:195 templates/js/barcode.html:243 +msgid "Unknown response from server" +msgstr "" + +#: templates/js/barcode.html:198 templates/js/barcode.html:247 +msgid "Invalid server response" +msgstr "" + +#: templates/js/barcode.html:265 +msgid "" +"This will remove the association between this stock item and the barcode" +msgstr "" + +#: templates/js/barcode.html:271 +msgid "Unlink" +msgstr "" + #: templates/js/bom.html:143 msgid "Open subassembly" msgstr "" diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 376ca98c7b..1c869a8ca4 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -107,8 +107,8 @@ InvenTree - + diff --git a/InvenTree/InvenTree/static/script/inventree/barcode.js b/InvenTree/templates/js/barcode.html similarity index 84% rename from InvenTree/InvenTree/static/script/inventree/barcode.js rename to InvenTree/templates/js/barcode.html index 2ccf4a69d9..ced4ebbf7e 100644 --- a/InvenTree/InvenTree/static/script/inventree/barcode.js +++ b/InvenTree/templates/js/barcode.html @@ -1,12 +1,10 @@ +{% load i18n %} /* * Pass barcode data to the server. */ function scanBarcode(barcode, options={}) { - console.log('Sending barcode data:'); - console.log(barcode); - inventreePut( '/api/barcode/', { @@ -27,11 +25,13 @@ function makeBarcodeInput(placeholderText='') { * Generate HTML for a barcode input */ + placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}'; + var html = `
- +
@@ -39,7 +39,7 @@ function makeBarcodeInput(placeholderText='') {
-
Enter barcode data
+
{% trans "Enter barcode data" %}
@@ -137,7 +137,7 @@ function barcodeDialog(title, options={}) { content += options.headerContent; } - content += `
Scan barcode data below
`; + content += `
{% trans "Scan barcode data below" %}
`; content += makeBarcodeInput(); @@ -178,8 +178,6 @@ function barcodeScanDialog() { method: 'POST', success: function(response, status) { - console.log(response); - enableBarcodeInput(modal, true); if (status == 'success') { @@ -194,10 +192,10 @@ function barcodeScanDialog() { } else if ('error' in response) { showBarcodeError(modal, response.error, 'warning'); } else { - showBarcodeError(modal, "Unknown response from server", 'warning'); + showBarcodeError(modal, "{% trans 'Unknown response from server' %}", 'warning'); } } else { - showBarcodeError(modal, `Invalid server response.
Status code: '${status}'`); + showBarcodeError(modal, `{% trans "Invalid server response" %}.
Status code: '${status}'`); } }, }, @@ -242,11 +240,11 @@ function linkBarcodeDialog(stockitem, options={}) { } else if ('error' in response) { showBarcodeError(modal, response.error, 'warning'); } else { - showBarcodeError(modal, "Unknown response from server", warning); + showBarcodeError(modal, "{% trans 'Unknown response from server' %}", warning); } } else { - showBarcodeError(modal, `Invalid server response.
Status code: '${status}'`); + showBarcodeError(modal, `{% trans "Invalid server response" %}.
Status code: '${status}'`); } }, }, @@ -262,11 +260,15 @@ function linkBarcodeDialog(stockitem, options={}) { */ function unlinkBarcode(stockitem) { + var html = `{% trans "Unlink Barcode" %}
`; + + html += "{% trans 'This will remove the association between this stock item and the barcode' %}"; + showQuestionDialog( - "Unlink Barcode", - "Remove barcode association from this Stock Item", + "{% trans 'Unlink Barcode' %}", + html, { - accept_text: "Unlink", + accept_text: "{% trans 'Unlink' %}", accept: function() { inventreePut( `/api/stock/${stockitem}/`, From e1c503836d585191d788ee2203df7499c7d718f2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 12 Jun 2020 10:56:52 +1000 Subject: [PATCH 19/24] Improve barcode icon in navbar --- InvenTree/InvenTree/static/css/inventree.css | 13 +++++++++++++ InvenTree/stock/templates/stock/item_base.html | 2 +- InvenTree/templates/navbar.html | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css index c1ecc953a1..ccf0671d56 100644 --- a/InvenTree/InvenTree/static/css/inventree.css +++ b/InvenTree/InvenTree/static/css/inventree.css @@ -82,12 +82,25 @@ float: left; } +.navbar-barcode-li { + border-left: none; + border-right: none; +} + .navbar-nav > li { border-left: 1px solid; border-right: 1px solid; border-color: #eee; } +.navbar-form { + padding-right: 3px; +} + +#barcode-scan { + margin-top: 8px; +} + .icon-header { margin-right: 10px; } diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 7b9f083f48..dc83144dfd 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -78,7 +78,7 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}