From b8b3a933ab1abe3a60db7aede337971836a45889 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 15 Aug 2020 19:48:42 +1000 Subject: [PATCH 01/27] Add django-debug-toolbar plugin - Must be running in DEBUG mode - Must set debug_toolbar to True in config file --- InvenTree/InvenTree/settings.py | 14 +++++++++++++- InvenTree/InvenTree/urls.py | 8 ++++++++ InvenTree/config_template.yaml | 6 ++++++ requirements.txt | 3 ++- 4 files changed, 29 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index e5b14314b5..6d68136fb2 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -172,9 +172,16 @@ MIDDLEWARE = [ 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'InvenTree.middleware.AuthRequiredMiddleware', + + 'InvenTree.middleware.AuthRequiredMiddleware' ] +# If the debug toolbar is enabled, add the modules +if DEBUG and CONFIG.get('debug_toolbar', False): + print("Running with DEBUG_TOOLBAR enabled") + INSTALLED_APPS.append('debug_toolbar') + MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware') + if CONFIG.get('log_queries', False): MIDDLEWARE.append('InvenTree.middleware.QueryCountMiddleware') @@ -377,3 +384,8 @@ DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage' DBBACKUP_STORAGE_OPTIONS = { 'location': CONFIG.get('backup_dir', tempfile.gettempdir()), } + +# Internal IP addresses allowed to see the debug toolbar +INTERNAL_IPS = [ + '127.0.0.1', +] \ No newline at end of file diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index c2d0d0f48f..d0076714ae 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -6,6 +6,7 @@ Passes URL lookup downstream to each app as required. from django.conf.urls import url, include +from django.urls import path from django.contrib import admin from django.contrib.auth import views as auth_views from qr_code import urls as qr_code_urls @@ -135,5 +136,12 @@ urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) # Media file access urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +# Debug toolbar access (if in DEBUG mode) +if settings.DEBUG and 'debug_toolbar' in settings.INSTALLED_APPS: + import debug_toolbar + urlpatterns = [ + path('__debug/', include(debug_toolbar.urls)), + ] + urlpatterns + # Send any unknown URLs to the parts page urlpatterns += [url(r'^.*$', RedirectView.as_view(url='/index/', permanent=False), name='index')] diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index 5447606337..eef3e8727e 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -58,8 +58,14 @@ static_root: '../inventree_static' # - git # - ssh +# Set debug_toolbar to True to enable a debugging toolbar for InvenTree +# Note: This will only be displayed if DEBUG mode is enabled, +# and only if InvenTree is accessed from a local IP (127.0.0.1) +debug_toolbar: False + # Logging options # If debug mode is enabled, set log_queries to True to show aggregate database queries in the debug console +# TODO - Remove me! log_queries: False # Backup options diff --git a/requirements.txt b/requirements.txt index f09cc35167..c668612914 100644 --- a/requirements.txt +++ b/requirements.txt @@ -21,4 +21,5 @@ python-coveralls==2.9.1 # Coveralls linking (for Travis) rapidfuzz==0.7.6 # Fuzzy string matching django-stdimage==5.1.1 # Advanced ImageField management django-tex==1.1.7 # LaTeX PDF export -django-weasyprint==1.0.1 # HTML PDF export \ No newline at end of file +django-weasyprint==1.0.1 # HTML PDF export +django-debug-toolbar==2.2 # Debug / profiling toolbar \ No newline at end of file From 2bb669d7de5e9cb4248fc816f3fd12e2e696b5a7 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 15 Aug 2020 19:52:04 +1000 Subject: [PATCH 02/27] Remove defunct "log_queries" option --- InvenTree/InvenTree/middleware.py | 2 ++ InvenTree/InvenTree/settings.py | 3 --- InvenTree/config_template.yaml | 5 ----- 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/InvenTree/InvenTree/middleware.py b/InvenTree/InvenTree/middleware.py index 26cbc95bbf..37b9a27c63 100644 --- a/InvenTree/InvenTree/middleware.py +++ b/InvenTree/InvenTree/middleware.py @@ -91,6 +91,8 @@ class QueryCountMiddleware(object): To enable this middleware, set 'log_queries: True' in the local InvenTree config file. Reference: https://www.dabapps.com/blog/logging-sql-queries-django-13/ + + Note: 2020-08-15 - This is no longer used, instead we now rely on the django-debug-toolbar addon """ def __init__(self, get_response): diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 6d68136fb2..7a3ab02433 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -182,9 +182,6 @@ if DEBUG and CONFIG.get('debug_toolbar', False): INSTALLED_APPS.append('debug_toolbar') MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware') -if CONFIG.get('log_queries', False): - MIDDLEWARE.append('InvenTree.middleware.QueryCountMiddleware') - ROOT_URLCONF = 'InvenTree.urls' TEMPLATES = [ diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index eef3e8727e..1c776a6f7a 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -63,11 +63,6 @@ static_root: '../inventree_static' # and only if InvenTree is accessed from a local IP (127.0.0.1) debug_toolbar: False -# Logging options -# If debug mode is enabled, set log_queries to True to show aggregate database queries in the debug console -# TODO - Remove me! -log_queries: False - # Backup options # Set the backup_dir parameter to store backup files in a specific location # If unspecified, the local user's temp directory will be used From 3c4cfc9a1b61a076a527efc87b0efdb86bb4d922 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 15 Aug 2020 19:56:02 +1000 Subject: [PATCH 03/27] Flake fix --- InvenTree/InvenTree/settings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 7a3ab02433..fb68f65497 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -385,4 +385,4 @@ DBBACKUP_STORAGE_OPTIONS = { # Internal IP addresses allowed to see the debug toolbar INTERNAL_IPS = [ '127.0.0.1', -] \ No newline at end of file +] From 2f77007dbe9abe19b61749731aa9484491e90615 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 15 Aug 2020 21:15:11 +1000 Subject: [PATCH 04/27] Significant query speed improvements to stock list API - Thanks, django-debug-toolbar! - Gah, django DRF is the worst. Enforcing a PrimaryKeyRelatedSerializer seems to really improve speed --- InvenTree/stock/api.py | 7 ++----- InvenTree/stock/serializers.py | 29 ++++++++++++++++++++++++----- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 7284f8d5cd..4368f654cf 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -338,11 +338,6 @@ class StockList(generics.ListCreateAPIView): queryset = self.filter_queryset(self.get_queryset()) - page = self.paginate_queryset(queryset) - if page is not None: - serializer = self.get_serializer(page, many=True) - return self.get_paginated_response(serializer.data) - serializer = self.get_serializer(queryset, many=True) data = serializer.data @@ -363,6 +358,7 @@ class StockList(generics.ListCreateAPIView): part_ids.add(part) sp = item['supplier_part'] + if sp: supplier_part_ids.add(sp) @@ -434,6 +430,7 @@ class StockList(generics.ListCreateAPIView): def get_queryset(self, *args, **kwargs): queryset = super().get_queryset(*args, **kwargs) + queryset = StockItemSerializer.prefetch_queryset(queryset) queryset = StockItemSerializer.annotate_queryset(queryset) diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 07fdd7952e..bed3f8f7c1 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -99,15 +99,34 @@ class StockItemSerializer(InvenTreeModelSerializer): return queryset + belongs_to = serializers.PrimaryKeyRelatedField(read_only=True) + + build_order = serializers.PrimaryKeyRelatedField(read_only=True) + + customer = serializers.PrimaryKeyRelatedField(read_only=True) + + location = serializers.PrimaryKeyRelatedField(read_only=True) + + in_stock = serializers.BooleanField(read_only=True) + + sales_order = serializers.PrimaryKeyRelatedField(read_only=True) + status_text = serializers.CharField(source='get_status_display', read_only=True) - part_detail = PartBriefSerializer(source='part', many=False, read_only=True) - location_detail = LocationBriefSerializer(source='location', many=False, read_only=True) + supplier_part = serializers.PrimaryKeyRelatedField(read_only=True) + supplier_part_detail = SupplierPartSerializer(source='supplier_part', many=False, read_only=True) + part = serializers.PrimaryKeyRelatedField(read_only=True) + + part_detail = PartBriefSerializer(source='part', many=False, read_only=True) + + location_detail = LocationBriefSerializer(source='location', many=False, read_only=True) + tracking_items = serializers.IntegerField(source='tracking_info_count', read_only=True, required=False) quantity = serializers.FloatField() + allocated = serializers.FloatField(source='allocation_count', required=False) serial = serializers.IntegerField(required=False) @@ -140,9 +159,9 @@ class StockItemSerializer(InvenTreeModelSerializer): fields = [ 'allocated', 'batch', - 'build_order', 'belongs_to', 'customer', + 'build_order', 'in_stock', 'link', 'location', @@ -155,10 +174,10 @@ class StockItemSerializer(InvenTreeModelSerializer): 'required_tests', 'sales_order', 'serial', - 'supplier_part', - 'supplier_part_detail', 'status', 'status_text', + 'supplier_part', + 'supplier_part_detail', 'tracking_items', 'uid', ] From d34383e842bd1feb0ec01691dcc9bf4cf99626fd Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 15 Aug 2020 21:20:31 +1000 Subject: [PATCH 05/27] Specify serializer type for part category --- InvenTree/part/serializers.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index d460748468..d9c041c252 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -236,6 +236,9 @@ class PartSerializer(InvenTreeModelSerializer): thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True) starred = serializers.SerializerMethodField() + # PrimaryKeyRelated fields (Note: enforcing field type here results in much faster queries, somehow...) + category = serializers.PrimaryKeyRelatedField(queryset=PartCategory.objects.all()) + # TODO - Include annotation for the following fields: # allocated_stock = serializers.FloatField(source='allocation_count', read_only=True) # bom_items = serializers.IntegerField(source='bom_count', read_only=True) From e1a8772af67819a43f739827f4ae67dea43e4b8f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 15 Aug 2020 21:24:01 +1000 Subject: [PATCH 06/27] Update serializers for SupplierPart --- InvenTree/company/serializers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/company/serializers.py b/InvenTree/company/serializers.py index 09f2b4dee8..baa618fb28 100644 --- a/InvenTree/company/serializers.py +++ b/InvenTree/company/serializers.py @@ -99,6 +99,10 @@ class SupplierPartSerializer(InvenTreeModelSerializer): if manufacturer_detail is not True: self.fields.pop('manufacturer_detail') + supplier = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_supplier=True)) + + manufacturer = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_manufacturer=True)) + class Meta: model = SupplierPart fields = [ From f1373e3bea6502ac8907f8e324dbc4e5ea4b99be Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 15 Aug 2020 21:52:32 +1000 Subject: [PATCH 07/27] Improve speed of BomItem API queries - Including the pricing information takes absolutely ages, takes too long!! --- InvenTree/part/api.py | 19 +++++++++++++++++-- InvenTree/part/serializers.py | 6 ++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 9db4ef5425..f866a08850 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -603,7 +603,20 @@ class BomList(generics.ListCreateAPIView): """ serializer_class = part_serializers.BomItemSerializer - + + def list(self, request, *args, **kwargs): + + queryset = self.filter_queryset(self.get_queryset()) + + serializer = self.get_serializer(queryset, many=True) + + data = serializer.data + + if request.is_ajax(): + return JsonResponse(data, safe=False) + else: + return Response(data) + def get_serializer(self, *args, **kwargs): # Do we wish to include extra detail? @@ -622,8 +635,10 @@ class BomList(generics.ListCreateAPIView): return self.serializer_class(*args, **kwargs) - def get_queryset(self): + def get_queryset(self, *args, **kwargs): + queryset = BomItem.objects.all() + queryset = self.get_serializer_class().setup_eager_loading(queryset) return queryset diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index d9c041c252..c1ecfb5cce 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -305,8 +305,13 @@ class BomItemSerializer(InvenTreeModelSerializer): price_range = serializers.CharField(read_only=True) quantity = serializers.FloatField() + + part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(assembly=True)) part_detail = PartBriefSerializer(source='part', many=False, read_only=True) + + sub_part = serializers.PrimaryKeyRelatedField(queryset=Part.objects.filter(component=True)) + sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True) validated = serializers.BooleanField(read_only=True, source='is_line_valid') @@ -331,6 +336,7 @@ class BomItemSerializer(InvenTreeModelSerializer): queryset = queryset.prefetch_related('part') queryset = queryset.prefetch_related('part__category') queryset = queryset.prefetch_related('part__stock_items') + queryset = queryset.prefetch_related('sub_part') queryset = queryset.prefetch_related('sub_part__category') queryset = queryset.prefetch_related('sub_part__stock_items') From b27ecf54d76c44ef4131a2b8bc3c21d7e056de35 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 08:29:36 +1000 Subject: [PATCH 08/27] Allow user to set "depth" of exported multi-level BOM --- InvenTree/part/bom.py | 7 ++++--- InvenTree/part/forms.py | 2 ++ InvenTree/part/views.py | 18 +++++++++++++++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index 16a8b0b16d..43c6ea7ca7 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -40,7 +40,7 @@ def MakeBomTemplate(fmt): return DownloadFile(data, filename) -def ExportBom(part, fmt='csv', cascade=False): +def ExportBom(part, fmt='csv', cascade=False, max_levels=None): """ Export a BOM (Bill of Materials) for a given part. Args: @@ -67,8 +67,9 @@ def ExportBom(part, fmt='csv', cascade=False): bom_items.append(item) - if item.sub_part.assembly: - add_items(item.sub_part.bom_items.all().order_by('id'), level + 1) + if item.sub_part.assembly: + if max_levels is None or level < max_levels: + add_items(item.sub_part.bom_items.all().order_by('id'), level + 1) if cascade: # Cascading (multi-level) BOM diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 986749f7b3..dbbced352b 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -56,6 +56,8 @@ class BomExportForm(forms.Form): cascading = forms.BooleanField(label=_("Cascading"), required=False, initial=False, help_text=_("Download cascading / multi-level BOM")) + levels = forms.IntegerField(label=_("Levels"), required=True, initial=0, help_text=_("Select maximum number of BOM levels to export (0 = all levels)")) + def get_choices(self): """ BOM export format choices """ diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index eda1db923b..9ae5cc8026 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -1392,10 +1392,22 @@ class BomDownload(AjaxView): cascade = str2bool(request.GET.get('cascade', False)) + levels = request.GET.get('levels', None) + + if levels is not None: + try: + levels = int(levels) + + if levels <= 0: + levels = None + + except ValueError: + levels = None + if not IsValidBOMFormat(export_format): export_format = 'csv' - return ExportBom(part, fmt=export_format, cascade=cascade) + return ExportBom(part, fmt=export_format, cascade=cascade, max_levels=levels) def get_data(self): return { @@ -1419,6 +1431,7 @@ class BomExport(AjaxView): # Extract POSTed form data fmt = request.POST.get('file_format', 'csv').lower() cascade = str2bool(request.POST.get('cascading', False)) + levels = request.POST.get('levels', None) try: part = Part.objects.get(pk=self.kwargs['pk']) @@ -1434,6 +1447,9 @@ class BomExport(AjaxView): url += '?file_format=' + fmt url += '&cascade=' + str(cascade) + if levels: + url += '&levels=' + str(levels) + data = { 'form_valid': part is not None, 'url': url, From 97103207b69631e51366f5198e16df188a12ac03 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 08:34:54 +1000 Subject: [PATCH 09/27] PEP fixes --- InvenTree/part/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index 43c6ea7ca7..ab7c9ed658 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -67,7 +67,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None): bom_items.append(item) - if item.sub_part.assembly: + if item.sub_part.assembly: if max_levels is None or level < max_levels: add_items(item.sub_part.bom_items.all().order_by('id'), level + 1) From e5406cae240554f67593d0f56fec0891de7601e9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 09:05:16 +1000 Subject: [PATCH 10/27] More PEP fixes --- InvenTree/part/bom.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index ab7c9ed658..47f6bd082e 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -67,7 +67,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None): bom_items.append(item) - if item.sub_part.assembly: + if item.sub_part.assembly: if max_levels is None or level < max_levels: add_items(item.sub_part.bom_items.all().order_by('id'), level + 1) From dd77cc00b7ea6fdab1136a8a8ce41b26b39b2173 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 09:07:27 +1000 Subject: [PATCH 11/27] Add requirement for "blabel" plugin --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index c668612914..354c7ea316 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ wheel>=0.34.2 # Wheel Django==3.0.7 # Django package pillow==7.1.0 # Image manipulation +blabel==0.1.3 # Simple PDF label printing djangorestframework==3.10.3 # DRF framework django-dbbackup==3.3.0 # Database backup / restore functionality django-cors-headers==3.2.0 # CORS headers extension for DRF From a45902bd4fdaed1db244e65d8341aba7ca3be70e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 09:28:12 +1000 Subject: [PATCH 12/27] Add "label" app --- InvenTree/InvenTree/helpers.py | 53 ++++++++++++ InvenTree/InvenTree/settings.py | 1 + InvenTree/label/__init__.py | 0 InvenTree/label/admin.py | 3 + InvenTree/label/apps.py | 5 ++ InvenTree/label/migrations/0001_initial.py | 30 +++++++ InvenTree/label/migrations/__init__.py | 0 InvenTree/label/models.py | 93 ++++++++++++++++++++++ InvenTree/label/tests.py | 3 + InvenTree/label/views.py | 3 + Makefile | 4 +- 11 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 InvenTree/label/__init__.py create mode 100644 InvenTree/label/admin.py create mode 100644 InvenTree/label/apps.py create mode 100644 InvenTree/label/migrations/0001_initial.py create mode 100644 InvenTree/label/migrations/__init__.py create mode 100644 InvenTree/label/models.py create mode 100644 InvenTree/label/tests.py create mode 100644 InvenTree/label/views.py diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 9880662e63..dbd59eecb0 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -383,3 +383,56 @@ def ExtractSerialNumbers(serials, expected_quantity): raise ValidationError([_("Number of unique serial number ({s}) must match quantity ({q})".format(s=len(numbers), q=expected_quantity))]) return numbers + + +def validateFilterString(value): + """ + Validate that a provided filter string looks like a list of comma-separated key=value pairs + + These should nominally match to a valid database filter based on the model being filtered. + + e.g. "category=6, IPN=12" + e.g. "part__name=widget" + + The ReportTemplate class uses the filter string to work out which items a given report applies to. + For example, an acceptance test report template might only apply to stock items with a given IPN, + so the string could be set to: + + filters = "IPN = ACME0001" + + Returns a map of key:value pairs + """ + + # Empty results map + results = {} + + value = str(value).strip() + + if not value or len(value) == 0: + return results + + groups = value.split(',') + + for group in groups: + group = group.strip() + + pair = group.split('=') + + if not len(pair) == 2: + raise ValidationError( + "Invalid group: {g}".format(g=group) + ) + + k, v = pair + + k = k.strip() + v = v.strip() + + if not k or not v: + raise ValidationError( + "Invalid group: {g}".format(g=group) + ) + + results[k] = v + + return results \ No newline at end of file diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index fb68f65497..d3a5ee919d 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -130,6 +130,7 @@ INSTALLED_APPS = [ 'build.apps.BuildConfig', 'common.apps.CommonConfig', 'company.apps.CompanyConfig', + 'label.apps.LabelConfig', 'order.apps.OrderConfig', 'part.apps.PartConfig', 'report.apps.ReportConfig', diff --git a/InvenTree/label/__init__.py b/InvenTree/label/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/InvenTree/label/admin.py b/InvenTree/label/admin.py new file mode 100644 index 0000000000..8c38f3f3da --- /dev/null +++ b/InvenTree/label/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/InvenTree/label/apps.py b/InvenTree/label/apps.py new file mode 100644 index 0000000000..ea4fa152ff --- /dev/null +++ b/InvenTree/label/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class LabelConfig(AppConfig): + name = 'label' diff --git a/InvenTree/label/migrations/0001_initial.py b/InvenTree/label/migrations/0001_initial.py new file mode 100644 index 0000000000..e960bcef67 --- /dev/null +++ b/InvenTree/label/migrations/0001_initial.py @@ -0,0 +1,30 @@ +# Generated by Django 3.0.7 on 2020-08-15 23:27 + +import InvenTree.helpers +import django.core.validators +from django.db import migrations, models +import label.models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='StockItemLabel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(help_text='Label name', max_length=100, unique=True)), + ('description', models.CharField(blank=True, help_text='Label description', max_length=250, null=True)), + ('label', models.FileField(help_text='Label template file', upload_to=label.models.rename_label, validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['html'])])), + ('filters', models.CharField(blank=True, help_text='Query filters (comma-separated list of key=value pairs', max_length=250, validators=[InvenTree.helpers.validateFilterString])), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/InvenTree/label/migrations/__init__.py b/InvenTree/label/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py new file mode 100644 index 0000000000..74c811a633 --- /dev/null +++ b/InvenTree/label/models.py @@ -0,0 +1,93 @@ +""" +Label printing models +""" + +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import os + +from blabel import LabelWriter + +from django.db import models +from django.core.validators import FileExtensionValidator + +from django.utils.translation import gettext_lazy as _ + +from InvenTree.helpers import validateFilterString + +from stock.models import StockItem + + +def rename_label(instance, filename): + """ Place the label file into the correct subdirectory """ + + filename = os.path.basename(filename) + + return os.path.join('label', 'template', instance.SUBDIR, filename) + + +class LabelTemplate(models.Model): + """ + Base class for generic, filterable labels. + """ + + class Meta: + abstract = True + + # Each class of label files will be stored in a separate subdirectory + SUBDIR = "label" + + name = models.CharField( + unique=True, + blank=False, max_length=100, + help_text=_('Label name'), + ) + + description = models.CharField(max_length=250, help_text=_('Label description'), blank=True, null=True) + + label = models.FileField( + upload_to=rename_label, + blank=False, null=False, + help_text=_('Label template file'), + validators=[FileExtensionValidator(allowed_extensions=['html'])], + ) + + filters = models.CharField( + blank=True, max_length=250, + help_text=_('Query filters (comma-separated list of key=value pairs'), + validators=[validateFilterString] + ) + + def get_record_data(self, items): + + return [] + + def render(self, items, **kwargs): + + records = self.get_record_data(items) + + writer = LabelWriter(self.label.filename) + + writer.write_label(records, 'out.pdf') + + +class StockItemLabel(LabelTemplate): + """ + Template for printing StockItem labels + """ + + SUBDIR = "stockitem" + + def matches_stock_item(self, item): + """ + Test if this label template matches a given StockItem object + """ + + filters = validateFilterString(self.filters) + + items = StockItem.objects.filter(**filters) + + items = items.filter(pk=item.pk) + + return items.exists() diff --git a/InvenTree/label/tests.py b/InvenTree/label/tests.py new file mode 100644 index 0000000000..7ce503c2dd --- /dev/null +++ b/InvenTree/label/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/InvenTree/label/views.py b/InvenTree/label/views.py new file mode 100644 index 0000000000..91ea44a218 --- /dev/null +++ b/InvenTree/label/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/Makefile b/Makefile index 0072e1ad9c..cc3a3043a6 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 barcode build common company order part report stock InvenTree + cd InvenTree && python3 manage.py test barcode build common company label order part report stock InvenTree # Run code coverage coverage: cd InvenTree && python3 manage.py check - coverage run InvenTree/manage.py test barcode build common company order part report stock InvenTree + coverage run InvenTree/manage.py test barcode build common company label order part report stock InvenTree coverage html # Install packages required to generate code docs From aefd70ce490d3a0673b6b82c6efb1dd046bcf46a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 09:29:45 +1000 Subject: [PATCH 13/27] Add admin interface for StockItemLabel --- InvenTree/label/admin.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/InvenTree/label/admin.py b/InvenTree/label/admin.py index 8c38f3f3da..bc03e122b6 100644 --- a/InvenTree/label/admin.py +++ b/InvenTree/label/admin.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + from django.contrib import admin -# Register your models here. +from .models import StockItemLabel + + +class StockItemLabelAdmin(admin.ModelAdmin): + + list_display = ('name', 'description', 'label') + + +admin.site.register(StockItemLabel, StockItemLabelAdmin) From 18b3fd3256fda430d9079b3f6797cbb170710ace Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 10:24:15 +1000 Subject: [PATCH 14/27] Add context data to StockItemLabel model --- InvenTree/label/models.py | 36 +++++++++++++++++++++++++++++++++--- InvenTree/stock/models.py | 7 +++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py index 74c811a633..6da973d85f 100644 --- a/InvenTree/label/models.py +++ b/InvenTree/label/models.py @@ -14,7 +14,7 @@ from django.core.validators import FileExtensionValidator from django.utils.translation import gettext_lazy as _ -from InvenTree.helpers import validateFilterString +from InvenTree.helpers import validateFilterString, normalize from stock.models import StockItem @@ -38,6 +38,10 @@ class LabelTemplate(models.Model): # Each class of label files will be stored in a separate subdirectory SUBDIR = "label" + @property + def template(self): + return self.label.path + name = models.CharField( unique=True, blank=False, max_length=100, @@ -60,6 +64,9 @@ class LabelTemplate(models.Model): ) def get_record_data(self, items): + """ + Return a list of dict objects, one for each item. + """ return [] @@ -67,9 +74,9 @@ class LabelTemplate(models.Model): records = self.get_record_data(items) - writer = LabelWriter(self.label.filename) + writer = LabelWriter(self.template) - writer.write_label(records, 'out.pdf') + writer.write_labels(records, 'out.html') class StockItemLabel(LabelTemplate): @@ -91,3 +98,26 @@ class StockItemLabel(LabelTemplate): items = items.filter(pk=item.pk) return items.exists() + + def get_record_data(self, items): + """ + Generate context data for each provided StockItem + """ + records = [] + + for item in items: + + # Add some basic information + records.append({ + 'item': item, + 'part': item.part, + 'name': item.part.name, + 'ipn': item.part.IPN, + 'quantity': normalize(item.quantity), + 'serial': item.serial, + 'uid': item.uid, + 'pk': item.pk, + 'qr_data': item.format_short_barcode(), + }) + + return records diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 0a9fc6b8bd..664593e600 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -302,6 +302,13 @@ class StockItem(MPTTModel): } ) + def format_short_barcode(self): + """ + Return a short barcode + """ + + return "stockid={pk}".format(pk=self.pk) + uid = models.CharField(blank=True, max_length=128, help_text=("Unique identifier field")) parent = TreeForeignKey( From 2b1d6c268c35eb2e7b3e6f69fbdf937bbddc9e41 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 10:29:03 +1000 Subject: [PATCH 15/27] Render to an in-memory PDF --- InvenTree/label/models.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py index 6da973d85f..c506d796db 100644 --- a/InvenTree/label/models.py +++ b/InvenTree/label/models.py @@ -6,6 +6,7 @@ Label printing models from __future__ import unicode_literals import os +import io from blabel import LabelWriter @@ -70,13 +71,31 @@ class LabelTemplate(models.Model): return [] - def render(self, items, **kwargs): + def render_to_file(self, filename, items, **kwargs): + """ + Render labels to a PDF file + """ records = self.get_record_data(items) writer = LabelWriter(self.template) - writer.write_labels(records, 'out.html') + writer.write_labels(records, filename) + + def render(self, items, **kwargs): + """ + Render labels to an in-memory PDF object, and return it + """ + + records = self.get_record_data(items) + + writer = LabelWriter(self.template) + + buffer = io.BytesIO() + + writer.write_labels(records, buffer) + + return buffer class StockItemLabel(LabelTemplate): From cdde0f8c433d344c0725af5fe090f6ed99891e23 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 11:12:21 +1000 Subject: [PATCH 16/27] Add URL endpoint for downloading PDF labels --- InvenTree/stock/urls.py | 1 + InvenTree/stock/views.py | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/InvenTree/stock/urls.py b/InvenTree/stock/urls.py index b48795b86e..c886f8ccaa 100644 --- a/InvenTree/stock/urls.py +++ b/InvenTree/stock/urls.py @@ -59,6 +59,7 @@ stock_urls = [ url(r'^item/new/?', views.StockItemCreate.as_view(), name='stock-item-create'), url(r'^item/test-report-download/', views.StockItemTestReportDownload.as_view(), name='stock-item-test-report-download'), + url(r'^item/print-stock-labels/', views.StockItemPrintLabels.as_view(), name='stock-item-print-labels'), # URLs for StockItem attachments url(r'^item/attachment/', include([ diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 56a3abaaad..5a5cba9126 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -28,6 +28,7 @@ from datetime import datetime from company.models import Company, SupplierPart from part.models import Part from report.models import TestReport +from label.models import StockItemLabel from .models import StockItem, StockLocation, StockItemTracking, StockItemAttachment, StockItemTestResult from .admin import StockItemResource @@ -295,6 +296,44 @@ class StockItemReturnToStock(AjaxUpdateView): return self.renderJsonResponse(request, self.get_form(), data) +class StockItemPrintLabels(AjaxView): + """ + View for printing labels and returning a PDF + + Requires the following arguments to be passed as URL params: + + items: List of valid StockItem pk values + label: Valid pk of a StockItemLabel template + """ + + def get(self, request, *args, **kwargs): + + label = request.GET.get('label', None) + + try: + label = StockItemLabel.objects.get(pk=label) + except (ValueError, StockItemLabel.DoesNotExist): + raise ValidationError({'label': 'Invalid label ID'}) + + item_pks = request.GET.getlist('items[]') + + items = [] + + for pk in item_pks: + try: + item = StockItem.objects.get(pk=pk) + items.append(item) + except (ValueError, StockItem.DoesNotExist): + pass + + if len(items) == 0: + raise ValidationError({'items': 'Must provide valid stockitems'}) + + pdf = label.render(items).getbuffer() + + return DownloadFile(pdf, 'stock_labels.pdf', content_type='application/pdf') + + class StockItemDeleteTestData(AjaxUpdateView): """ View for deleting all test data From 30e24f19d993a15d8ab0b5bc522cd5ced76871dd Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 11:13:17 +1000 Subject: [PATCH 17/27] PEP fixes --- InvenTree/InvenTree/helpers.py | 2 +- InvenTree/label/tests.py | 2 -- InvenTree/label/views.py | 2 -- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index dbd59eecb0..bf7e9083b5 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -435,4 +435,4 @@ def validateFilterString(value): results[k] = v - return results \ No newline at end of file + return results diff --git a/InvenTree/label/tests.py b/InvenTree/label/tests.py index 7ce503c2dd..a39b155ac3 100644 --- a/InvenTree/label/tests.py +++ b/InvenTree/label/tests.py @@ -1,3 +1 @@ -from django.test import TestCase - # Create your tests here. diff --git a/InvenTree/label/views.py b/InvenTree/label/views.py index 91ea44a218..60f00ef0ef 100644 --- a/InvenTree/label/views.py +++ b/InvenTree/label/views.py @@ -1,3 +1 @@ -from django.shortcuts import render - # Create your views here. From 1807ba4e7b14f788816f9dd03819544cbb363fc4 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 12:10:58 +1000 Subject: [PATCH 18/27] Add form for selecting label template for a StockItem --- InvenTree/label/models.py | 7 +++ InvenTree/stock/forms.py | 31 +++++++++++++ .../stock/templates/stock/item_base.html | 12 +++++ InvenTree/stock/urls.py | 1 + InvenTree/stock/views.py | 44 +++++++++++++++++++ 5 files changed, 95 insertions(+) diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py index c506d796db..6faf966d7e 100644 --- a/InvenTree/label/models.py +++ b/InvenTree/label/models.py @@ -43,6 +43,12 @@ class LabelTemplate(models.Model): def template(self): return self.label.path + def __str__(self): + return "{n} - {d}".format( + n=self.name, + d=self.description + ) + name = models.CharField( unique=True, blank=False, max_length=100, @@ -137,6 +143,7 @@ class StockItemLabel(LabelTemplate): 'uid': item.uid, 'pk': item.pk, 'qr_data': item.format_short_barcode(), + 'tests': item.testResultMap() }) return records diff --git a/InvenTree/stock/forms.py b/InvenTree/stock/forms.py index 06d90e33df..234da6d53b 100644 --- a/InvenTree/stock/forms.py +++ b/InvenTree/stock/forms.py @@ -178,6 +178,37 @@ class SerializeStockForm(HelperForm): ] +class StockItemLabelSelectForm(HelperForm): + """ Form for selecting a label template for a StockItem """ + + label = forms.ChoiceField( + label=_('Label'), + help_text=_('Select test report template') + ) + + class Meta: + model = StockItem + fields = [ + 'label', + ] + + def get_label_choices(self, labels): + + choices = [] + + if len(labels) > 0: + for label in labels: + choices.append((label.pk, label)) + + return choices + + def __init__(self, labels, *args, **kwargs): + + super().__init__(*args, **kwargs) + + self.fields['label'].choices = self.get_label_choices(labels) + + class TestReportFormatForm(HelperForm): """ Form for selection a test report template """ diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index ce055d65c5..a7bc263bbf 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -129,6 +129,9 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} {% endif %} + {% endblock %} @@ -314,6 +317,15 @@ $("#stock-test-report").click(function() { }); {% endif %} +$("#stock-print-label").click(function() { + launchModalForm( + "{% url 'stock-item-label-select' item.id %}", + { + follow: true, + } + ) +}); + $("#stock-duplicate").click(function() { launchModalForm( "{% url 'stock-item-create' %}", diff --git a/InvenTree/stock/urls.py b/InvenTree/stock/urls.py index c886f8ccaa..51067b57de 100644 --- a/InvenTree/stock/urls.py +++ b/InvenTree/stock/urls.py @@ -29,6 +29,7 @@ stock_item_detail_urls = [ url(r'^add_tracking/', views.StockItemTrackingCreate.as_view(), name='stock-tracking-create'), url(r'^test-report-select/', views.StockItemTestReportSelect.as_view(), name='stock-item-test-report-select'), + url(r'^label-select/', views.StockItemSelectLabels.as_view(), name='stock-item-label-select'), url(r'^test/', views.StockItemDetail.as_view(template_name='stock/item_tests.html'), name='stock-item-test-results'), url(r'^children/', views.StockItemDetail.as_view(template_name='stock/item_childs.html'), name='stock-item-children'), diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 5a5cba9126..1c9b78a3f0 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -296,6 +296,50 @@ class StockItemReturnToStock(AjaxUpdateView): return self.renderJsonResponse(request, self.get_form(), data) +class StockItemSelectLabels(AjaxView): + """ + View for selecting a template for printing labels for one (or more) StockItem objects + """ + + model = StockItem + ajax_form_title = _('Select Label Template') + + def get_form(self): + + item = StockItem.objects.get(pk=self.kwargs['pk']) + + labels = [] + + for label in StockItemLabel.objects.all(): + if label.matches_stock_item(item): + labels.append(label) + + return StockForms.StockItemLabelSelectForm(labels) + + def post(self, request, *args, **kwargs): + + label = request.POST.get('label', None) + + try: + label = StockItemLabel.objects.get(pk=label) + except (ValueError, StockItemLabel.DoesNotExist): + raise ValidationError({'label': _("Select valid label")}) + + stock_item = StockItem.objects.get(pk=self.kwargs['pk']) + + url = reverse('stock-item-print-labels') + + url += '?label={pk}'.format(pk=label.pk) + url += '&items[]={pk}'.format(pk=stock_item.pk) + + data = { + 'form_valid': True, + 'url': url, + } + + return self.renderJsonResponse(request, self.get_form(), data=data) + + class StockItemPrintLabels(AjaxView): """ View for printing labels and returning a PDF From 2f5e3efada4b5f4efda6863c0d1d19b581098f75 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 12:11:22 +1000 Subject: [PATCH 19/27] Update translations --- InvenTree/locale/de/LC_MESSAGES/django.mo | Bin 50558 -> 50288 bytes InvenTree/locale/de/LC_MESSAGES/django.po | 835 +++++++++++++--------- InvenTree/locale/en/LC_MESSAGES/django.po | 777 +++++++++++--------- InvenTree/locale/es/LC_MESSAGES/django.po | 777 +++++++++++--------- 4 files changed, 1357 insertions(+), 1032 deletions(-) diff --git a/InvenTree/locale/de/LC_MESSAGES/django.mo b/InvenTree/locale/de/LC_MESSAGES/django.mo index 766a862f9d50bc0ab1252c60a0bf54afb543b655..758fa8c4cd300c45d252a01eb8f6f23e28b66a87 100644 GIT binary patch delta 15632 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 delta 15920 zcmYk>2Y63c1IO`eB_u|Kkc31cVkg92wMXqeYm-UVXp6|2cUc_v!IDd*7SThsoJq&B^9|@1K3S!}DEM$0>?`6>^*_*&OF$ zlyV)%?+wSvhxyS9!!b9O#=KY+^I&6Cy$%?H{V^D)*!(pZNO~t~oX;@7x{i~JbP)Ps1gc>b^v2ex33SFB*dJAYFlqvcsEJL(!nhb2$k}f5k6ORLT#WDB zAfkppVFP@EL0Gk(u`O04Jp{EWtC6`n=WM=LeN$c?E08}JRlW)}ku>zdUoa;=wElzs zE*V)F)fWR$D-K6(X(iNF)wlLSt#krv!ZVPQa+cZrRj2_spg-S)AENrn z*O2`WAQD7G85K}lS`#(ndKiW+Pz{IK{5hzOm)rcUsCH*j6TgUBP`dS3j3fORb#&bu znR;=J*niD9i3}A?M>Sl8n(gK3R+X1s- zcho|ASqC*{|4WlGij2y*8YA!$mcz%WfkK-Y%UNrnwzvtZ!*19V`(rKKj?wtDEiXW! zCKQ4Nu_WrKYr90WWvx)JOAoAqV^AyGfogaRb^FhtcIZp%4b%jFvE>g?cjGy#U+-on z9e`R$Bx;8$S>2{YG(Zp3Q4FvqqB@>xor^k(<>-yOQFkX5HSvq6^1E0Boi`n)7!*a7 z*Fn9GU6D_WlYq2$omoWGaIN(qs^KNnQT&PC=-=E77>wG&im07vg6gon%^!@~xkfb{x^gmR+z?KBYcS;jc#RjOAyk*mgsz7=kY6mu>5AH&(@Gz?5FHo21dsO=e zs1kBML z+PAf_I%+2dpcjruO?(P!2bQ$v{Bsl8K!zr=#}=GGb$kgm(;rYPc!uiGw~aZfa8&#H zsEN0>>At9ehTD7>)ouxDXSSjG`>YN7ubJE+Lk%)e1OIE&x!anv4MjDmhEdoOHQ;zu zyLqTP@V+$_eMw(J-Tri&|J0hNoym`MiD--KqqevO=EV+J8vCKXd~>h_?!suiYSUTT zoBAbD6R3(>X=~IG4M3gwV9bY8QAfQP^Ps!hMz*0=l#1HY^EN*XHKCtv`4iL~@akZG zI0d72raJmzD^z(en;wOl&@|KpR@w4x$fb9kV?_KZNJGsi16A=kYGCh<=GGTNbrg=O zUmZ2k7N{-mia9XRrr$>G=u%X_YmkqkvmLA9LoCGjPN`1jgHspFQ_vfAc5_i5pp~dw zeH0_{F4n|6oz1OohT7sbsDTDyejI~3g4wA4=AkCM3bnA!=%x4n01@4RqsqWDs582V z`l)pt^?Se#EQZfeJ5;EPsaMik0rdf^fqH$rqVCKX48YY`5ck^rGw5nYzZ22rd5&5^ zzOKd)RD*I@2`@6&ZEO z=#Hgum922froTr`#H+j6$|9(pDuvpaXw(2LQ41J=x(lOFTRjega29H3Q*8eB?(DyI z;4m55(lb~9ucIcEfjYy|JxqQSY9b9#TiXS-wG%N47o!F^iK=%FwG+2c6Z;c2v6rX? zWOd&%1LQ*8fxOm0)Y%tDO|T>eVr5iEtx#Lt!=^`J5z^{U--|VQ6(}TW;NM^N*w2eTBLs z85oXPdTR$c{|F+QVJ*}dHb+floTmcsEvkc!sMqfpYA0@@&i+2C<7|C+^{@zrU?)_0 zBI>A;QT5iNCVB|7yF@M!(WUqbHBg%M0oEm*v#;5qW~i-hfto-^)E90bs(n1_tP@c? zGzqn^cWrtVs(h18??G2*c9e+D<^t+$zD0HLJF3AmRJ|hj&kV9Xk8e&~c?RnmSTYwvR#`7b6RFofIP4;-jdQUqzkWT`Z2zQ5^;k zHn+bNmLy#fD`HnHg~?bR_oCX}LcLAS5VONUSe;~L)FmB;dG!7#6VatwfjYZws4e;& zbK+ghg%45nUZBn}_fV6LMy;$q>L^>GCLD(mI2twJN>qObP&;>AX~uUh*@AB{8|mBV zjX$G0`U5qW0={}5Y&WXP#>B`sQTTpHpXE&+<@8f66)x#p^hLOU1ekt(aayB zI`oM*9Tr6$MJ23^4N35>v4n-n97zhMRh2un+l7PDW#GSus`1@#(UKz)jTMV(>fNK-!=E0Au4d2uxQ;56%e)I?UHj&ut) z#@(oKp14GGOLL4eGYmz|xEAKdQJ4=Wqqceh`r#(jfCo_>e})?9BC1_Fs@+}Ggr1|0 zI%}f2l(|p~bfbvqz3qVNpc`r@;!q7IqCd_;4g8)>??AmJ$5BUj3;B9G8K{Z19Bp3f z9;k_oLgi0IO>hx<-v4#B!eP`_omG>UElk`EeEMYG`M+&|1``JdGOg4(e7v zKusvuSo7V8L=9X4Rj&q?#-fqEH{EMAX2GQ9E!L`4&0X zQ4^{6wwXXLtWJ6yM&TaR>v;>yVYc!7h{g)&HYc)#i0;5m%!NOqX8Z?gN1mdN!e@fH z6Va%o5TK+Vt58y#H!=gA8?e54FOVs9PN|(HucJ z)O%eEb*A-D?OI_l_C^gn9(8G5)WqhXj&dogzpd6isJnD%BKxm`yJTpE4^Wq{z&qws zTovQ8uu{}5PMKr|+KZYODU??iTCYHqxs1ML2mx#7xD;C1z zsES{sW`5tAg^B9Y1lS4JMYU^+T6s^@%KM{sX1FbPQ43m*>hBQhQeVg{PbZ>=4^a*O z#kQDdin+zTP!k%Djc^HuVj8x@r&t3UOeNq1tc}O84gQU~Q_ZHCaoVHOJx$tm;)rNw zqfloy6SbuaP&@JghT~ytI%>i%QCsXi-F$$;Q45H|VptEgV|`F}BpJ2k8!-mIM9;tf z|3^d}=9*zFh}lRNL(Mb-OJEdgA{|imlTcqi7mMIrRJ*OVd?)J8q}udZ>__?v>h4sY zNqctNsZT^5wz76b4cs5qaXc2rWYpHDpmypcs@*x%nO;M!{72N?$U4h>^8+zE>0&lr z0yTkh==uA94I=trG{KrU3H6~kgjMh-)R!x8w&}Q}wGQg2I-n*Ti@FmDSRapKWqgSm zuwt^Q_Xg@{nxF&*{gN*ZO=UwwQs9U}V zHIXk+AEY~|l|I8T^qOb#OQGtyb%O0{#-*j9CwFC80JJcIhJ_*(T3S?Z@*-u0RpGDpBn>PIyYKA@wOoMRDNxClP#%8FA zcS3FDAnQcb#1^6k-hrxr9@SqO>aDu(k@L^B(7Zm8n1g~UHeCzzk#3H<8{N?t`=VAH zkE%Zvb!TRyCh{Iuz+D)Hw@~$c7nucwVLsAjP@i+BCK1iBE#}3ZsFe*vbvzl>@gmd; zHd&9Nw)iTl-9yw){)c`Tu-FV-5|v*Y)n9uog#*ylnam}k2AfbbJBa!5jP)DTgfdX| zUt%-{EHUjGqslv@Ryr7U#*6*bW#sGU5&#J>O6$;d^s zH9wxqp;p`$E8uX{O4gwka0WHOG}Ik$Gvgxl;Z_Ojrf?i-<^j&E_?QRPq5oEZi z4ma9@P~Dy4R{`PckZCt-9vq%pI~VWUTuCsX@t5v@1S;S zHHI_3bBKtx@CF9sZ>SYzTVqy|4|N0~7=STY0-K?BE*=ZvBI{Pv#7>}gFb%a6cWwSl z)FmvqmIX1sQ<8`#&=i|sXVjfogPQRftb{+I1}e19OehLs!WNvIFj0$aWu zLrABfcJ2gf0(a2!?|)B-XolI|HJE8|$HdftZNep()lis18$66T5*i_z-oJ z#n+o1D~H;N2B^Ey0d-`3*R%hM3?)Mil29|7gt{aPQNL2HN3C=tYM|XVeFh^*e`WKZ zVHoK=DdtW^qZU>h)n7X-ihWT>IVr_8D_cs2wr&+_rXQgOJdL^wmrzHPjw;VUb@;;S z^MR=!j2bW!RbC6V;uffdbwjluikiSg*H&1J+T!(C1UI2(dJ6S++(NDVcMQQS8_WR3 zFpP8s)Bx>lx<6_`(^2i#VJIFz^?wa@iQNZ8RI%VjGxKQF>(dCsaVV;Td8mdPu{fSY zwfhkR@g-`)0h`QUS}UPmzj3IQrl9UlDrzEEkoKxb$v5Y=G}cEUE8 z6*r>p$`;hb_Sp1U)RA3AZFxFs;NMU?^9ohpZ;Ls~QkYNge>G2p*ALaO3u*!bZT=+G z+0RFv-C9(=U8s(aqK@cGtbyrR77J`OJ5U#O$p@j{s!^zRGbQ6YD~V`;?N|{HqZ(wO zR`kS{zd{X^XPbFVgRuWfwyRbJnw+hPUMgD?hHVg#PYl6W5*V8I>cAFsP$8PfBxJf`ko|Mi;uM20TK z6Vw^`?=%CJu{OZcDyg71ZS}u#5fI z`&xCExr8lHx4Rws;ArfQlhGT$MV;Yo%!PkpZhVC~(Ra7$ryyz~!Ki-9qHcdxRJ(TQ zi#=Q-su+iPaV&Z+2dd+h==mO?CU6S9@Eq!A!ewjLJ?8FIwZ@@#V5Kz`b*Zo88~7)d zK)3u}GlBM~Up)GuJ}^nvC8!ziMXm4%Y9*&J62C|7NcMeZ0f87rx)f?5ov|1WLe-y* z>Tef%-v2{H@{(}{HNb7mh8d_GxQ`m(1!^nveq?TS5auIY7B%r&sCunY?fRkKh7qW{ z^AT#`?=T1cf_e4+-y@=qU!W@H*l#+DMtwjUTDxI&(j!mnD`yTb$J;iz$ zlxkMm9kmk!uqBQ}9qlpH?N3KHhR91I>M;6{>98^Cvb=@5d`YM?T!i|DQ+UhA-5a*yKvfk!@i0w$H9%lbFu=mGipb*rpt%=pJ8|qFhMBR~9sIA{> z(}yrW>9gp0Jy8?8ZT%DdNxwwBZuyRwx2Fh}B3=9l`>#j~GBm?P)Ict3^cc3Pk ziUshZ^)~8<)L*Ff`H%8z7>1%IJ`7c!glfMSHPNl8e)qUUbm@+wX8JSM!sl2PD;+au z+Y2?Zsi<4M2#ey^s4aekVd!<-e0ocvAL%xz9qEIrHym}@r=fPzoo^%SP&3?xh43_L zYtvC3{eha`|7^bB3G)Fej_RnhH5S82kHIpy0yW?%)XFcQcIq0kK-alVM7P@aq`B=e zSch~hR>Te1Pz|sM7CL2itP+NjehX*d1PsCdP`5ke6Z5m89_sCwftt`Z)CcM+Ch7fm zK4tbW2K8aMfSwN$>I`qA2F}1h%yHW6M5r|eb=w=GcCL+0$D$UNfNHngmhZ+O(r3`~ z_x~S=XltLM&M4<+W~L#it!suV?|_<6ENX%yurN+S4ZIw) znb3!NQIv(E9@guOAbg&c^WUQ!o~INRwiT34Cq9Zs3zgvcmUtTWC5)!*8Fs^Jn1(-K zaYA+C4XIlJ^_;d&B2Uj#gVT}x5SKqbAtS<8x`aDO&n8T?6?@Qdhb?bP-gktXgk$8@ zr%d0Ab2ti1kar4S5Q>vGi1bk6dcGmJB{IQlvAViKJVS_?1x9W)8sVv<<_als6&Xiuf+#S5Y5C^RVyll3ARf=M~|1f<8c* z&qm7LClsNRri2T`6DYihS4nThe1ur?!cpHGJu3}PE%Nki!|NvMd}tl0bN|#bR5n zqZL6v4Y%P>wo(u4HS)$0zfTAv{`xbIwAX7HWl3)&lpwDk;VAKTgz1@e81yXZ8l+1S z{B{0A$p|Ls8G)w>?TI(S6Evtx{4?Tvh?gQXBmNoce_UohV~DgO{Vn01&8$Q`(Z+uw zo=j*%_?!?<{#-n%H*_r>>Pg1@gjm8s!a6GGXMat613QwZrwCyQdEbyOM(9gC^I1pw z1c^uF{fz5L&mhbso|C-kuhq>%{w#g5x{~=ZuAq_+@lJ%t#M_WJjrbhmdS((&$2H_P zB%b+nc`ZIAGZPOH{HdFRkolzAh!=UE6L%A6yphOnxQ}p|_$}MvnAaN4A>M${h4Pm8 z4Rsa~Qf-~p#Q&izf{=~8qJ)dKj4aP{i9ZY3_+G31zr!ZgB{$HCH@b7iF)FR z`(l|@bab4cXA$8%@n0~J3Z=0ub|P=I{h8rDAoEW`DKhI)_%Y#E!v6@F&o9KoDbG&O zGZL#2nvvhj<|%&*@fYM3P=cqI!TFl}Pi(y$%V8~oV4jpq@xJFHa(yCr^FA??ju4nLC?Fk`~&?;^^%}x zsm<(el1?|$7YJ1d!>F^E{1jW~B04!+6Zb|EEIYYovVwu%zwtLt9UaK16(% z&Oek2^=yUS#2*t6#7}Hq1u7mOYEl=%=cpIXHINqkJ`Z0n52hoq0# zdXq>mB^{<;se_2Dv<1NwTp^4lv>;TcEQ-*YP}tVFLHswuzl46|#oPS9NWW#{vuL}H zbT-uUKKXO;BU|2<_&X|Ke9vEd6t7C<8H59bQMTcD8f3TenySRJmGBOE=WKo_(ru|1 zXggnS^Ush!k9a|w{{!(WCW%cXM&swO9Tl=R zq;L>nG3h6SpKX2?EKQ?Og0Icl5^ZklsXS zLOjoGTQM&g&i;dS9t8Qv$Hxu~8!{rSV^YF^VPVaaVu$a4zstCg5`+7V4(lHqJ0h%K ze0;)yeo3(d9Zwb22pcjwEIwg$(te+$nO?pF!{QPW!y1km5VM%m7I*BfZ#3m-~uethac+BgCD<7A+e)lM>ww+j*U%>Pe_a% jvES!dYj6K9v5DhiM+}Tj3X3~^jN$r?+27@KaF+iA\n" "Language-Team: C \n" @@ -83,7 +83,7 @@ msgstr "Datei zum Anhängen auswählen" msgid "File comment" msgstr "Datei-Kommentar" -#: InvenTree/models.py:68 templates/js/stock.html:669 +#: InvenTree/models.py:68 templates/js/stock.html:682 msgid "User" msgstr "Benutzer" @@ -97,24 +97,24 @@ msgstr "Name" msgid "Description (optional)" msgstr "Firmenbeschreibung" -#: InvenTree/settings.py:330 +#: InvenTree/settings.py:335 msgid "English" msgstr "Englisch" -#: InvenTree/settings.py:331 +#: InvenTree/settings.py:336 msgid "German" msgstr "Deutsch" -#: InvenTree/settings.py:332 +#: InvenTree/settings.py:337 msgid "French" msgstr "Französisch" -#: InvenTree/settings.py:333 +#: InvenTree/settings.py:338 msgid "Polish" msgstr "Polnisch" #: InvenTree/status_codes.py:94 InvenTree/status_codes.py:135 -#: InvenTree/status_codes.py:235 +#: InvenTree/status_codes.py:222 msgid "Pending" msgstr "Ausstehend" @@ -122,59 +122,50 @@ msgstr "Ausstehend" msgid "Placed" msgstr "Platziert" -#: InvenTree/status_codes.py:96 InvenTree/status_codes.py:238 +#: InvenTree/status_codes.py:96 InvenTree/status_codes.py:225 msgid "Complete" msgstr "Fertig" #: InvenTree/status_codes.py:97 InvenTree/status_codes.py:137 -#: InvenTree/status_codes.py:237 +#: InvenTree/status_codes.py:224 msgid "Cancelled" msgstr "Storniert" #: InvenTree/status_codes.py:98 InvenTree/status_codes.py:138 -#: InvenTree/status_codes.py:179 +#: InvenTree/status_codes.py:175 msgid "Lost" msgstr "Verloren" #: InvenTree/status_codes.py:99 InvenTree/status_codes.py:139 -#: InvenTree/status_codes.py:181 +#: InvenTree/status_codes.py:177 msgid "Returned" msgstr "Zurückgegeben" -#: InvenTree/status_codes.py:136 InvenTree/status_codes.py:182 -#: order/templates/order/sales_order_base.html:98 +#: InvenTree/status_codes.py:136 order/templates/order/sales_order_base.html:98 msgid "Shipped" msgstr "Versendet" -#: InvenTree/status_codes.py:175 +#: InvenTree/status_codes.py:171 msgid "OK" msgstr "OK" -#: InvenTree/status_codes.py:176 +#: InvenTree/status_codes.py:172 msgid "Attention needed" msgstr "erfordert Eingriff" -#: InvenTree/status_codes.py:177 +#: InvenTree/status_codes.py:173 msgid "Damaged" msgstr "Beschädigt" -#: InvenTree/status_codes.py:178 +#: InvenTree/status_codes.py:174 msgid "Destroyed" msgstr "Zerstört" -#: InvenTree/status_codes.py:180 +#: InvenTree/status_codes.py:176 msgid "Rejected" msgstr "" -#: InvenTree/status_codes.py:183 -msgid "Used for Build" -msgstr "Verwendet für Bau" - -#: InvenTree/status_codes.py:184 -msgid "Installed in Stock Item" -msgstr "In Lagerobjekt installiert" - -#: InvenTree/status_codes.py:236 build/templates/build/allocate.html:349 +#: InvenTree/status_codes.py:223 build/templates/build/allocate.html:349 #: order/templates/order/sales_order_detail.html:220 #: part/templates/part/tabs.html:23 templates/js/build.html:120 msgid "Allocated" @@ -265,7 +256,7 @@ msgstr "Standort-Details" msgid "Serial numbers" msgstr "Seriennummer" -#: build/forms.py:64 stock/forms.py:93 +#: build/forms.py:64 stock/forms.py:105 msgid "Enter unique serial numbers (or leave blank)" msgstr "Eindeutige Seriennummern eingeben (oder leer lassen)" @@ -306,7 +297,7 @@ msgstr "Eltern-Bau, dem dieser Bau zugewiesen ist" #: part/templates/part/set_category.html:13 templates/js/barcode.html:336 #: templates/js/bom.html:135 templates/js/build.html:41 #: templates/js/company.html:109 templates/js/part.html:120 -#: templates/js/stock.html:424 +#: templates/js/stock.html:425 msgid "Part" msgstr "Teil" @@ -350,7 +341,7 @@ msgstr "Bau-Status" msgid "Build status code" msgstr "Bau-Statuscode" -#: build/models.py:136 stock/models.py:374 +#: build/models.py:136 stock/models.py:376 msgid "Batch Code" msgstr "Losnummer" @@ -362,21 +353,21 @@ 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:86 -#: stock/models.py:368 stock/templates/stock/item_base.html:230 +#: stock/models.py:370 stock/templates/stock/item_base.html:237 msgid "External Link" msgstr "Externer Link" -#: build/models.py:156 stock/models.py:370 +#: build/models.py:156 stock/models.py:372 msgid "Link to external URL" msgstr "Link zu einer externen URL" -#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:307 +#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:310 #: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15 #: order/templates/order/purchase_order_detail.html:200 #: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:64 -#: stock/models.py:436 stock/models.py:1265 stock/templates/stock/tabs.html:26 +#: stock/models.py:438 stock/models.py:1287 stock/templates/stock/tabs.html:26 #: templates/js/barcode.html:391 templates/js/bom.html:229 -#: templates/js/stock.html:113 templates/js/stock.html:513 +#: templates/js/stock.html:114 templates/js/stock.html:526 msgid "Notes" msgstr "Notizen" @@ -407,15 +398,15 @@ msgstr "Anzahl muss größer null sein" msgid "Quantity must be 1 for serialized stock" msgstr "Anzahl muss 1 für Objekte mit Seriennummer sein" -#: build/models.py:512 +#: build/models.py:511 msgid "Build to allocate parts" msgstr "Bau starten um Teile zuzuweisen" -#: build/models.py:519 +#: build/models.py:518 msgid "Stock Item to allocate to build" msgstr "Lagerobjekt dem Bau zuweisen" -#: build/models.py:532 +#: build/models.py:531 msgid "Stock quantity to allocate to build" msgstr "Lagerobjekt-Anzahl dem Bau zuweisen" @@ -442,8 +433,8 @@ 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:146 +#: order/templates/order/sales_order_detail.html:150 stock/models.py:364 +#: stock/templates/stock/item_base.html:153 msgid "Serial Number" msgstr "Seriennummer" @@ -460,18 +451,18 @@ 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:152 +#: stock/templates/stock/item_base.html:159 #: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.html:338 #: templates/js/bom.html:172 templates/js/build.html:52 -#: templates/js/stock.html:660 +#: templates/js/stock.html:673 msgid "Quantity" msgstr "Anzahl" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:184 +#: stock/templates/stock/item_base.html:191 #: stock/templates/stock/stock_adjust.html:17 templates/js/barcode.html:337 -#: templates/js/stock.html:495 +#: templates/js/stock.html:508 msgid "Location" msgstr "Standort" @@ -497,7 +488,7 @@ msgstr "Keine BOM-Einträge gefunden" #: templates/js/bom.html:157 templates/js/company.html:60 #: templates/js/order.html:157 templates/js/order.html:230 #: templates/js/part.html:176 templates/js/part.html:355 -#: templates/js/stock.html:445 templates/js/stock.html:641 +#: templates/js/stock.html:440 templates/js/stock.html:654 msgid "Description" msgstr "Beschreibung" @@ -507,8 +498,8 @@ msgstr "Beschreibung" msgid "Reference" msgstr "Referenz" -#: build/templates/build/allocate.html:338 part/models.py:1269 -#: templates/js/part.html:359 templates/js/table_filters.html:90 +#: build/templates/build/allocate.html:338 part/models.py:1270 +#: templates/js/part.html:359 templates/js/table_filters.html:100 msgid "Required" msgstr "benötigt" @@ -552,7 +543,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:209 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:216 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "Bau" @@ -572,9 +563,9 @@ 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:262 templates/js/barcode.html:42 +#: stock/templates/stock/item_base.html:269 templates/js/barcode.html:42 #: templates/js/build.html:57 templates/js/order.html:162 -#: templates/js/order.html:235 templates/js/stock.html:482 +#: templates/js/order.html:235 templates/js/stock.html:495 msgid "Status" msgstr "Status" @@ -584,7 +575,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:172 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:179 templates/js/order.html:209 msgid "Sales Order" msgstr "Bestellung" @@ -653,7 +644,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:202 templates/js/stock.html:490 +#: stock/templates/stock/item_base.html:209 templates/js/stock.html:503 msgid "Batch" msgstr "Los" @@ -757,7 +748,7 @@ msgstr "Zuweisung aufheben" msgid "Confirm unallocation of build stock" msgstr "Zuweisungsaufhebung bestätigen" -#: build/views.py:162 stock/views.py:286 +#: build/views.py:162 stock/views.py:404 msgid "Check the confirmation box" msgstr "Bestätigungsbox bestätigen" @@ -765,7 +756,7 @@ msgstr "Bestätigungsbox bestätigen" msgid "Complete Build" msgstr "Bau fertigstellen" -#: build/views.py:201 stock/views.py:1118 stock/views.py:1232 +#: build/views.py:201 stock/views.py:1236 stock/views.py:1350 msgid "Next available serial number is" msgstr "" @@ -783,7 +774,7 @@ msgstr "Baufertigstellung bestätigen" msgid "Invalid location selected" msgstr "Ungültige Ortsauswahl" -#: build/views.py:300 stock/views.py:1268 +#: build/views.py:300 stock/views.py:1386 #, python-brace-format msgid "The following serial numbers already exist: ({sn})" msgstr "Die folgende Seriennummer existiert bereits: ({sn})" @@ -878,117 +869,117 @@ msgstr "Währung bearbeiten" msgid "Delete Currency" msgstr "Währung entfernen" -#: company/models.py:83 company/models.py:84 +#: company/models.py:86 company/models.py:87 msgid "Company name" msgstr "Firmenname" -#: company/models.py:86 +#: company/models.py:89 #, fuzzy #| msgid "Part description" msgid "Company description" msgstr "Beschreibung des Teils" -#: company/models.py:86 +#: company/models.py:89 msgid "Description of the company" msgstr "Firmenbeschreibung" -#: company/models.py:88 company/templates/company/company_base.html:48 +#: company/models.py:91 company/templates/company/company_base.html:48 #: templates/js/company.html:65 msgid "Website" msgstr "Website" -#: company/models.py:88 +#: company/models.py:91 msgid "Company website URL" msgstr "Firmenwebsite" -#: company/models.py:91 company/templates/company/company_base.html:55 +#: company/models.py:94 company/templates/company/company_base.html:55 msgid "Address" msgstr "Adresse" -#: company/models.py:92 +#: company/models.py:95 msgid "Company address" msgstr "Firmenadresse" -#: company/models.py:95 +#: company/models.py:98 #, fuzzy #| msgid "Contact phone number" msgid "Phone number" msgstr "Kontakt-Tel." -#: company/models.py:96 +#: company/models.py:99 msgid "Contact phone number" msgstr "Kontakt-Tel." -#: company/models.py:98 company/templates/company/company_base.html:69 +#: company/models.py:101 company/templates/company/company_base.html:69 msgid "Email" msgstr "Email" -#: company/models.py:98 +#: company/models.py:101 msgid "Contact email address" msgstr "Kontakt-Email" -#: company/models.py:101 company/templates/company/company_base.html:76 +#: company/models.py:104 company/templates/company/company_base.html:76 msgid "Contact" msgstr "Kontakt" -#: company/models.py:102 +#: company/models.py:105 msgid "Point of contact" msgstr "Anlaufstelle" -#: company/models.py:104 +#: company/models.py:107 msgid "Link to external company information" msgstr "Link auf externe Firmeninformation" -#: company/models.py:116 +#: company/models.py:119 msgid "Do you sell items to this company?" msgstr "Verkaufen Sie Teile an diese Firma?" -#: company/models.py:118 +#: company/models.py:121 msgid "Do you purchase items from this company?" msgstr "Kaufen Sie Teile von dieser Firma?" -#: company/models.py:120 +#: company/models.py:123 msgid "Does this company manufacture parts?" msgstr "Produziert diese Firma Teile?" -#: company/models.py:276 stock/models.py:322 -#: stock/templates/stock/item_base.html:138 +#: company/models.py:279 stock/models.py:324 +#: stock/templates/stock/item_base.html:145 msgid "Base Part" msgstr "Basisteil" -#: company/models.py:281 +#: company/models.py:284 msgid "Select part" msgstr "Teil auswählen" -#: company/models.py:287 +#: company/models.py:290 msgid "Select supplier" msgstr "Zulieferer auswählen" -#: company/models.py:290 +#: company/models.py:293 msgid "Supplier stock keeping unit" msgstr "Stock Keeping Units (SKU) des Zulieferers" -#: company/models.py:297 +#: company/models.py:300 msgid "Select manufacturer" msgstr "Hersteller auswählen" -#: company/models.py:301 +#: company/models.py:304 msgid "Manufacturer part number" msgstr "Hersteller-Teilenummer" -#: company/models.py:303 +#: company/models.py:306 msgid "URL for external supplier part link" msgstr "Teil-URL des Zulieferers" -#: company/models.py:305 +#: company/models.py:308 msgid "Supplier part description" msgstr "Zuliefererbeschreibung des Teils" -#: company/models.py:309 +#: company/models.py:312 msgid "Minimum charge (e.g. stocking fee)" msgstr "Mindestpreis" -#: company/models.py:311 +#: company/models.py:314 msgid "Part packaging" msgstr "Teile-Packaging" @@ -1025,14 +1016,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:237 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:244 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:159 +#: order/templates/order/sales_order_base.html:73 stock/models.py:359 +#: stock/models.py:360 stock/templates/stock/item_base.html:166 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "Kunde" @@ -1087,7 +1078,7 @@ msgstr "Neuen Hersteller anlegen" msgid "Supplier Stock" msgstr "Zuliefererbestand" -#: company/templates/company/detail_stock.html:34 +#: company/templates/company/detail_stock.html:35 #: company/templates/company/supplier_part_stock.html:33 #: part/templates/part/stock.html:53 templates/stock_table.html:5 msgid "Export" @@ -1146,8 +1137,8 @@ msgid "New Sales Order" 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:242 templates/js/company.html:150 +#: company/templates/company/supplier_part_base.html:19 stock/models.py:333 +#: stock/templates/stock/item_base.html:249 templates/js/company.html:150 msgid "Supplier Part" msgstr "Zulieferer-Teil" @@ -1246,7 +1237,7 @@ msgstr "Bepreisung" #: company/templates/company/supplier_part_tabs.html:8 #: company/templates/company/tabs.html:12 part/templates/part/tabs.html:18 #: stock/templates/stock/location.html:12 templates/js/part.html:203 -#: templates/js/stock.html:453 templates/navbar.html:11 +#: templates/js/stock.html:448 templates/navbar.html:11 msgid "Stock" msgstr "Lagerbestand" @@ -1346,6 +1337,26 @@ msgstr "Preisstaffel bearbeiten" msgid "Delete Price Break" msgstr "Preisstaffel löschen" +#: label/models.py:55 +#, fuzzy +#| msgid "Part name" +msgid "Label name" +msgstr "Name des Teils" + +#: label/models.py:58 +#, fuzzy +#| msgid "Part description" +msgid "Label description" +msgstr "Beschreibung des Teils" + +#: label/models.py:63 +msgid "Label template file" +msgstr "" + +#: label/models.py:69 +msgid "Query filters (comma-separated list of key=value pairs" +msgstr "" + #: order/forms.py:24 msgid "Place order" msgstr "Bestellung aufgeben" @@ -1400,7 +1411,7 @@ msgid "Supplier order reference code" msgstr "Bestellreferenz" #: order/models.py:185 order/models.py:259 part/views.py:1167 -#: stock/models.py:243 stock/models.py:665 stock/views.py:1243 +#: stock/models.py:238 stock/models.py:687 msgid "Quantity must be greater than zero" msgstr "Anzahl muss größer Null sein" @@ -1434,7 +1445,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:216 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:223 templates/js/order.html:136 msgid "Purchase Order" msgstr "Kaufvertrag" @@ -1718,7 +1729,7 @@ msgstr "Bestellungspositionen" msgid "Add Purchase Order Attachment" msgstr "Bestellanhang hinzufügen" -#: order/views.py:102 order/views.py:149 part/views.py:85 stock/views.py:166 +#: order/views.py:102 order/views.py:149 part/views.py:85 stock/views.py:167 msgid "Added attachment" msgstr "Anhang hinzugefügt" @@ -1738,7 +1749,7 @@ msgstr "Anhang aktualisiert" msgid "Delete Attachment" msgstr "Anhang löschen" -#: order/views.py:222 order/views.py:236 stock/views.py:222 +#: order/views.py:222 order/views.py:236 stock/views.py:223 msgid "Deleted attachment" msgstr "Anhang gelöscht" @@ -1871,11 +1882,11 @@ msgstr "Fehler beim Lesen der Stückliste (ungültige Daten)" msgid "Error reading BOM file (incorrect row size)" msgstr "Fehler beim Lesen der Stückliste (ungültige Zeilengröße)" -#: part/forms.py:55 stock/forms.py:200 +#: part/forms.py:55 stock/forms.py:243 msgid "File Format" msgstr "Dateiformat" -#: part/forms.py:55 stock/forms.py:200 +#: part/forms.py:55 stock/forms.py:243 msgid "Select output file format" msgstr "Ausgabe-Dateiformat auswählen" @@ -1925,105 +1936,114 @@ msgstr "Standard-Standort für Teile dieser Kategorie" msgid "Default keywords for parts in this category" msgstr "Standard-Stichworte für Teile dieser Kategorie" -#: part/models.py:427 +#: part/models.py:74 part/templates/part/part_app_base.html:9 +msgid "Part Category" +msgstr "Teilkategorie" + +#: part/models.py:75 part/templates/part/category.html:13 +#: part/templates/part/category.html:78 templates/stats.html:12 +msgid "Part Categories" +msgstr "Teile-Kategorien" + +#: part/models.py:428 msgid "Part must be unique for name, IPN and revision" msgstr "Namen, Teile- und Revisionsnummern müssen eindeutig sein" -#: part/models.py:442 part/templates/part/detail.html:19 +#: part/models.py:443 part/templates/part/detail.html:19 msgid "Part name" msgstr "Name des Teils" -#: part/models.py:446 +#: part/models.py:447 msgid "Is this part a template part?" msgstr "Ist dieses Teil eine Vorlage?" -#: part/models.py:455 +#: part/models.py:456 msgid "Is this part a variant of another part?" msgstr "Ist dieses Teil eine Variante eines anderen Teils?" -#: part/models.py:457 +#: part/models.py:458 msgid "Part description" msgstr "Beschreibung des Teils" -#: part/models.py:459 +#: part/models.py:460 msgid "Part keywords to improve visibility in search results" msgstr "Schlüsselworte um die Sichtbarkeit in Suchergebnissen zu verbessern" -#: part/models.py:464 +#: part/models.py:465 msgid "Part category" msgstr "Teile-Kategorie" -#: part/models.py:466 +#: part/models.py:467 msgid "Internal Part Number" msgstr "Interne Teilenummer" -#: part/models.py:468 +#: part/models.py:469 msgid "Part revision or version number" msgstr "Revisions- oder Versionsnummer" -#: part/models.py:470 +#: part/models.py:471 msgid "Link to extenal URL" msgstr "Link zu einer Externen URL" -#: part/models.py:482 +#: part/models.py:483 msgid "Where is this item normally stored?" msgstr "Wo wird dieses Teil normalerweise gelagert?" -#: part/models.py:526 +#: part/models.py:527 msgid "Default supplier part" msgstr "Standard-Zulieferer?" -#: part/models.py:529 +#: part/models.py:530 msgid "Minimum allowed stock level" msgstr "Minimal zulässiger Lagerbestand" -#: part/models.py:531 +#: part/models.py:532 msgid "Stock keeping units for this part" msgstr "Stock Keeping Units (SKU) für dieses Teil" -#: part/models.py:533 +#: part/models.py:534 msgid "Can this part be built from other parts?" msgstr "Kann dieses Teil aus anderen Teilen angefertigt werden?" -#: part/models.py:535 +#: part/models.py:536 msgid "Can this part be used to build other parts?" msgstr "Kann dieses Teil zum Bau von anderen genutzt werden?" -#: part/models.py:537 +#: part/models.py:538 msgid "Does this part have tracking for unique items?" msgstr "Hat dieses Teil Tracking für einzelne Objekte?" -#: part/models.py:539 +#: part/models.py:540 msgid "Can this part be purchased from external suppliers?" msgstr "Kann dieses Teil von externen Zulieferern gekauft werden?" -#: part/models.py:541 +#: part/models.py:542 msgid "Can this part be sold to customers?" msgstr "Kann dieses Teil an Kunden verkauft werden?" -#: part/models.py:543 +#: part/models.py:544 msgid "Is this part active?" msgstr "Ist dieses Teil aktiv?" -#: part/models.py:545 +#: part/models.py:546 msgid "Is this a virtual part, such as a software product or license?" msgstr "Ist dieses Teil virtuell, wie zum Beispiel eine Software oder Lizenz?" -#: part/models.py:547 +#: part/models.py:548 msgid "Part notes - supports Markdown formatting" msgstr "Bemerkungen - unterstüzt Markdown-Formatierung" -#: part/models.py:549 +#: part/models.py:550 msgid "Stored BOM checksum" msgstr "Prüfsumme der Stückliste gespeichert" -#: part/models.py:1221 +#: part/models.py:1222 #, fuzzy #| msgid "Stock item cannot be created for a template Part" msgid "Test templates can only be created for trackable parts" msgstr "Lagerobjekt kann nicht für Vorlagen-Teile angelegt werden" -#: part/models.py:1238 +#: part/models.py:1239 #, fuzzy #| msgid "" #| "A stock item with this serial number already exists for template part " @@ -2033,121 +2053,127 @@ msgstr "" "Ein Teil mit dieser Seriennummer existiert bereits für die Teilevorlage " "{part}" -#: part/models.py:1257 templates/js/part.html:350 templates/js/stock.html:89 +#: part/models.py:1258 templates/js/part.html:350 templates/js/stock.html:90 #, fuzzy #| msgid "Instance Name" msgid "Test Name" msgstr "Instanzname" -#: part/models.py:1258 +#: part/models.py:1259 #, fuzzy #| msgid "Serial number for this item" msgid "Enter a name for the test" msgstr "Seriennummer für dieses Teil" -#: part/models.py:1263 +#: part/models.py:1264 #, fuzzy #| msgid "Description" msgid "Test Description" msgstr "Beschreibung" -#: part/models.py:1264 +#: part/models.py:1265 #, fuzzy #| msgid "Brief description of the build" msgid "Enter description for this test" msgstr "Kurze Beschreibung des Baus" -#: part/models.py:1270 +#: part/models.py:1271 msgid "Is this test required to pass?" msgstr "" -#: part/models.py:1275 templates/js/part.html:367 +#: part/models.py:1276 templates/js/part.html:367 #, fuzzy #| msgid "Required Parts" msgid "Requires Value" msgstr "benötigte Teile" -#: part/models.py:1276 +#: part/models.py:1277 msgid "Does this test require a value when adding a test result?" msgstr "" -#: part/models.py:1281 templates/js/part.html:374 +#: part/models.py:1282 templates/js/part.html:374 #, fuzzy #| msgid "Delete Attachment" msgid "Requires Attachment" msgstr "Anhang löschen" -#: part/models.py:1282 +#: part/models.py:1283 msgid "Does this test require a file attachment when adding a test result?" msgstr "" -#: part/models.py:1315 +#: part/models.py:1316 msgid "Parameter template name must be unique" msgstr "Vorlagen-Name des Parameters muss eindeutig sein" -#: part/models.py:1320 +#: part/models.py:1321 msgid "Parameter Name" msgstr "Name des Parameters" -#: part/models.py:1322 +#: part/models.py:1323 msgid "Parameter Units" msgstr "Parameter Einheit" -#: part/models.py:1348 +#: part/models.py:1349 msgid "Parent Part" msgstr "Ausgangsteil" -#: part/models.py:1350 +#: part/models.py:1351 msgid "Parameter Template" msgstr "Parameter Vorlage" -#: part/models.py:1352 +#: part/models.py:1353 msgid "Parameter Value" msgstr "Parameter Wert" -#: part/models.py:1381 +#: part/models.py:1382 msgid "Select parent part" msgstr "Ausgangsteil auswählen" -#: part/models.py:1389 +#: part/models.py:1390 msgid "Select part to be used in BOM" msgstr "Teil für die Nutzung in der Stückliste auswählen" -#: part/models.py:1395 +#: part/models.py:1396 msgid "BOM quantity for this BOM item" msgstr "Stücklisten-Anzahl für dieses Stücklisten-Teil" -#: part/models.py:1398 +#: part/models.py:1399 msgid "Estimated build wastage quantity (absolute or percentage)" msgstr "Geschätzter Ausschuss (absolut oder prozentual)" -#: part/models.py:1401 +#: part/models.py:1402 msgid "BOM item reference" msgstr "Referenz des Objekts auf der Stückliste" -#: part/models.py:1404 +#: part/models.py:1405 msgid "BOM item notes" msgstr "Notizen zum Stücklisten-Objekt" -#: part/models.py:1406 +#: part/models.py:1407 msgid "BOM line checksum" msgstr "Prüfsumme der Stückliste" -#: part/models.py:1470 stock/models.py:233 +#: part/models.py:1471 stock/models.py:228 #, fuzzy #| msgid "Overage must be an integer value or a percentage" msgid "Quantity must be integer value for trackable parts" msgstr "Überschuss muss eine Ganzzahl oder ein Prozentwert sein" -#: part/models.py:1479 +#: part/models.py:1480 msgid "Part cannot be added to its own Bill of Materials" msgstr "Teil kann nicht zu seiner eigenen Stückliste hinzugefügt werden" -#: part/models.py:1486 +#: part/models.py:1487 #, python-brace-format msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)" msgstr "Teil '{p1}' wird in Stückliste für Teil '{p2}' benutzt (rekursiv)" +#: part/models.py:1494 +#, fuzzy +#| msgid "New BOM Item" +msgid "BOM Item" +msgstr "Neue Stücklistenposition" + #: part/templates/part/allocation.html:10 msgid "Part Stock Allocations" msgstr "Teilbestandszuordnungen" @@ -2163,14 +2189,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:224 +#: stock/templates/stock/item_base.html:231 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 -#: templates/js/stock.html:630 +#: templates/js/stock.html:643 msgid "Stock Item" msgstr "Lagerobjekt" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:178 +#: stock/templates/stock/item_base.html:185 msgid "Build Order" msgstr "Bauauftrag" @@ -2210,11 +2236,6 @@ msgstr "Stückliste validieren" msgid "Export Bill of Materials" msgstr "Stückliste exportieren" -#: part/templates/part/category.html:13 part/templates/part/category.html:78 -#: templates/stats.html:12 -msgid "Part Categories" -msgstr "Teile-Kategorien" - #: part/templates/part/category.html:14 msgid "All parts" msgstr "Alle Teile" @@ -2310,8 +2331,8 @@ msgstr "Teil ist virtuell (kein physisches Teil)" msgid "Part is not a virtual part" msgstr "Teil ist nicht virtuell" -#: part/templates/part/detail.html:139 stock/forms.py:194 -#: templates/js/table_filters.html:149 +#: part/templates/part/detail.html:139 stock/forms.py:237 +#: templates/js/table_filters.html:159 msgid "Template" msgstr "Vorlage" @@ -2327,7 +2348,7 @@ msgstr "Teil kann keine Vorlage sein wenn es Variante eines anderen Teils ist" msgid "Part is not a template part" msgstr "Teil ist nicht virtuell" -#: part/templates/part/detail.html:148 templates/js/table_filters.html:161 +#: part/templates/part/detail.html:148 templates/js/table_filters.html:171 msgid "Assembly" msgstr "Baugruppe" @@ -2339,7 +2360,7 @@ msgstr "Teil kann aus anderen Teilen angefertigt werden" msgid "Part cannot be assembled from other parts" msgstr "Teil kann nicht aus anderen Teilen angefertigt werden" -#: part/templates/part/detail.html:157 templates/js/table_filters.html:165 +#: part/templates/part/detail.html:157 templates/js/table_filters.html:175 msgid "Component" msgstr "Komponente" @@ -2351,7 +2372,7 @@ msgstr "Teil kann in Baugruppen benutzt werden" msgid "Part cannot be used in assemblies" msgstr "Teil kann nicht in Baugruppen benutzt werden" -#: part/templates/part/detail.html:166 templates/js/table_filters.html:177 +#: part/templates/part/detail.html:166 templates/js/table_filters.html:187 msgid "Trackable" msgstr "nachverfolgbar" @@ -2371,7 +2392,7 @@ msgstr "Kaufbar" msgid "Part can be purchased from external suppliers" msgstr "Teil kann von externen Zulieferern gekauft werden" -#: part/templates/part/detail.html:184 templates/js/table_filters.html:173 +#: part/templates/part/detail.html:184 templates/js/table_filters.html:183 msgid "Salable" msgstr "Verkäuflich" @@ -2383,7 +2404,7 @@ msgstr "Teil kann an Kunden verkauft werden" msgid "Part cannot be sold to customers" msgstr "Teil kann nicht an Kunden verkauft werden" -#: part/templates/part/detail.html:193 templates/js/table_filters.html:144 +#: part/templates/part/detail.html:193 templates/js/table_filters.html:154 msgid "Active" msgstr "Aktiv" @@ -2419,8 +2440,8 @@ msgstr "Parameter hinzufügen" msgid "New Parameter" msgstr "Neuer Parameter" -#: part/templates/part/params.html:21 stock/models.py:1252 -#: templates/js/stock.html:109 +#: part/templates/part/params.html:21 stock/models.py:1274 +#: templates/js/stock.html:110 msgid "Value" msgstr "Wert" @@ -2432,10 +2453,6 @@ msgstr "Bearbeiten" msgid "Delete" msgstr "Löschen" -#: part/templates/part/part_app_base.html:9 -msgid "Part Category" -msgstr "Teilkategorie" - #: part/templates/part/part_app_base.html:11 msgid "Part List" msgstr "Teileliste" @@ -2517,7 +2534,7 @@ msgstr "Teile löschen" msgid "Available Stock" msgstr "Verfügbarer Lagerbestand" -#: part/templates/part/part_base.html:108 templates/js/table_filters.html:37 +#: part/templates/part/part_base.html:108 templates/js/table_filters.html:57 msgid "In Stock" msgstr "Auf Lager" @@ -2630,7 +2647,7 @@ msgstr "Stückliste" msgid "Used In" msgstr "Benutzt in" -#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:268 +#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:275 msgid "Tests" msgstr "" @@ -2859,236 +2876,252 @@ msgstr "" msgid "Asset file description" msgstr "Einstellungs-Beschreibung" -#: stock/forms.py:194 +#: stock/forms.py:185 +msgid "Label" +msgstr "" + +#: stock/forms.py:186 stock/forms.py:237 #, fuzzy #| msgid "Select stock item to allocate" msgid "Select test report template" msgstr "Lagerobjekt für Zuordnung auswählen" -#: stock/forms.py:202 +#: stock/forms.py:245 msgid "Include stock items in sub locations" msgstr "Lagerobjekte in untergeordneten Lagerorten einschließen" -#: stock/forms.py:235 +#: stock/forms.py:278 msgid "Destination stock location" msgstr "Ziel-Lagerbestand" -#: stock/forms.py:241 +#: stock/forms.py:284 msgid "Confirm movement of stock items" msgstr "Bewegung der Lagerobjekte bestätigen" -#: stock/forms.py:243 +#: stock/forms.py:286 msgid "Set the destination as the default location for selected parts" msgstr "Setze das Ziel als Standard-Ziel für ausgewählte Teile" -#: stock/models.py:208 +#: stock/models.py:209 #, fuzzy #| msgid "A stock item with this serial number already exists" msgid "StockItem with this serial number already exists" msgstr "Ein Teil mit dieser Seriennummer existiert bereits" -#: stock/models.py:250 +#: stock/models.py:245 #, python-brace-format msgid "Part type ('{pf}') must be {pe}" msgstr "Teile-Typ ('{pf}') muss {pe} sein" -#: stock/models.py:260 stock/models.py:269 +#: stock/models.py:255 stock/models.py:264 msgid "Quantity must be 1 for item with a serial number" msgstr "Anzahl muss für Objekte mit Seriennummer \"1\" sein" -#: stock/models.py:261 +#: stock/models.py:256 msgid "Serial number cannot be set if quantity greater than 1" msgstr "" "Seriennummer kann nicht gesetzt werden wenn die Anzahl größer als \"1\" ist" -#: stock/models.py:282 +#: stock/models.py:277 msgid "Item cannot belong to itself" msgstr "Teil kann nicht zu sich selbst gehören" -#: stock/models.py:314 +#: stock/models.py:316 msgid "Parent Stock Item" msgstr "Eltern-Lagerobjekt" -#: stock/models.py:323 +#: stock/models.py:325 msgid "Base part" msgstr "Basis-Teil" -#: stock/models.py:332 +#: stock/models.py:334 msgid "Select a matching supplier part for this stock item" msgstr "Passenden Zulieferer für dieses Lagerobjekt auswählen" -#: stock/models.py:337 stock/templates/stock/stock_app_base.html:7 +#: stock/models.py:339 stock/templates/stock/stock_app_base.html:7 msgid "Stock Location" msgstr "Lagerort" -#: stock/models.py:340 +#: stock/models.py:342 msgid "Where is this stock item located?" msgstr "Wo wird dieses Teil normalerweise gelagert?" -#: stock/models.py:345 +#: stock/models.py:347 msgid "Installed In" msgstr "Installiert in" -#: stock/models.py:348 +#: stock/models.py:350 msgid "Is this item installed in another item?" msgstr "Ist dieses Teil in einem anderen verbaut?" -#: stock/models.py:364 +#: stock/models.py:366 msgid "Serial number for this item" msgstr "Seriennummer für dieses Teil" -#: stock/models.py:376 +#: stock/models.py:378 msgid "Batch code for this stock item" msgstr "Losnummer für dieses Lagerobjekt" -#: stock/models.py:380 +#: stock/models.py:382 msgid "Stock Quantity" msgstr "Bestand" -#: stock/models.py:389 +#: stock/models.py:391 msgid "Source Build" msgstr "Quellbau" -#: stock/models.py:391 +#: stock/models.py:393 msgid "Build for this stock item" msgstr "Bau für dieses Lagerobjekt" -#: stock/models.py:398 +#: stock/models.py:400 msgid "Source Purchase Order" msgstr "Quellbestellung" -#: stock/models.py:401 +#: stock/models.py:403 msgid "Purchase order for this stock item" msgstr "Bestellung für dieses Teil" -#: stock/models.py:407 +#: stock/models.py:409 msgid "Destination Sales Order" msgstr "Zielauftrag" -#: stock/models.py:414 +#: stock/models.py:416 msgid "Destination Build Order" msgstr "Zielbauauftrag" -#: stock/models.py:427 +#: stock/models.py:429 msgid "Delete this Stock Item when stock is depleted" msgstr "Objekt löschen wenn Lagerbestand aufgebraucht" -#: stock/models.py:437 stock/templates/stock/item_notes.html:14 +#: stock/models.py:439 stock/templates/stock/item_notes.html:14 #: stock/templates/stock/item_notes.html:30 msgid "Stock Item Notes" msgstr "Lagerobjekt-Notizen" -#: stock/models.py:489 +#: stock/models.py:490 #, fuzzy #| msgid "Item assigned to customer?" msgid "Assigned to Customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/models.py:491 +#: stock/models.py:492 #, fuzzy #| msgid "Item assigned to customer?" msgid "Manually assigned to customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/models.py:656 +#: stock/models.py:505 +#, fuzzy +#| msgid "Item assigned to customer?" +msgid "Returned from customer" +msgstr "Ist dieses Objekt einem Kunden zugeteilt?" + +#: stock/models.py:507 +#, fuzzy +#| msgid "Create new stock location" +msgid "Returned to location" +msgstr "Neuen Lagerort anlegen" + +#: stock/models.py:678 #, fuzzy #| msgid "Part is not a virtual part" msgid "Part is not set as trackable" msgstr "Teil ist nicht virtuell" -#: stock/models.py:662 +#: stock/models.py:684 msgid "Quantity must be integer" msgstr "Anzahl muss eine Ganzzahl sein" -#: stock/models.py:668 +#: stock/models.py:690 #, python-brace-format msgid "Quantity must not exceed available stock quantity ({n})" msgstr "Anzahl darf nicht die verfügbare Anzahl überschreiten ({n})" -#: stock/models.py:671 stock/models.py:674 +#: stock/models.py:693 stock/models.py:696 msgid "Serial numbers must be a list of integers" msgstr "Seriennummern muss eine Liste von Ganzzahlen sein" -#: stock/models.py:677 +#: stock/models.py:699 msgid "Quantity does not match serial numbers" msgstr "Anzahl stimmt nicht mit den Seriennummern überein" -#: stock/models.py:687 +#: stock/models.py:709 msgid "Serial numbers already exist: " msgstr "Seriennummern existieren bereits:" -#: stock/models.py:712 +#: stock/models.py:734 msgid "Add serial number" msgstr "Seriennummer hinzufügen" -#: stock/models.py:715 +#: stock/models.py:737 #, python-brace-format msgid "Serialized {n} items" msgstr "{n} Teile serialisiert" -#: stock/models.py:826 +#: stock/models.py:848 msgid "StockItem cannot be moved as it is not in stock" msgstr "Lagerobjekt kann nicht bewegt werden, da kein Bestand vorhanden ist" -#: stock/models.py:1153 +#: stock/models.py:1175 msgid "Tracking entry title" msgstr "Name des Eintrags-Trackings" -#: stock/models.py:1155 +#: stock/models.py:1177 msgid "Entry notes" msgstr "Eintrags-Notizen" -#: stock/models.py:1157 +#: stock/models.py:1179 msgid "Link to external page for further information" msgstr "Link auf externe Seite für weitere Informationen" -#: stock/models.py:1217 +#: stock/models.py:1239 #, fuzzy #| msgid "Serial number for this item" msgid "Value must be provided for this test" msgstr "Seriennummer für dieses Teil" -#: stock/models.py:1223 +#: stock/models.py:1245 msgid "Attachment must be uploaded for this test" msgstr "" -#: stock/models.py:1240 +#: stock/models.py:1262 msgid "Test" msgstr "" -#: stock/models.py:1241 +#: stock/models.py:1263 #, fuzzy #| msgid "Part name" msgid "Test name" msgstr "Name des Teils" -#: stock/models.py:1246 +#: stock/models.py:1268 #, fuzzy #| msgid "Search Results" msgid "Result" msgstr "Suchergebnisse" -#: stock/models.py:1247 templates/js/table_filters.html:80 +#: stock/models.py:1269 templates/js/table_filters.html:90 msgid "Test result" msgstr "" -#: stock/models.py:1253 +#: stock/models.py:1275 msgid "Test output value" msgstr "" -#: stock/models.py:1259 +#: stock/models.py:1281 #, fuzzy #| msgid "Attachments" msgid "Attachment" msgstr "Anhänge" -#: stock/models.py:1260 +#: stock/models.py:1282 #, fuzzy #| msgid "Delete attachment" msgid "Test result attachment" msgstr "Anhang löschen" -#: stock/models.py:1266 +#: stock/models.py:1288 #, fuzzy #| msgid "Edit notes" msgid "Test notes" @@ -3146,7 +3179,7 @@ msgstr "" msgid "Link Barcode" msgstr "" -#: stock/templates/stock/item_base.html:92 +#: stock/templates/stock/item_base.html:91 #, fuzzy #| msgid "Confirm stock adjustment" msgid "Stock adjustment actions" @@ -3171,84 +3204,94 @@ msgstr "Bestand entfernen" msgid "Transfer stock" msgstr "Bestand bestellen" +#: stock/templates/stock/item_base.html:101 +#, fuzzy +#| msgid "Serialize Stock" +msgid "Serialize stock" +msgstr "Lagerbestand erfassen" + #: stock/templates/stock/item_base.html:105 +#, fuzzy +#| msgid "Item assigned to customer?" +msgid "Assign to customer" +msgstr "Ist dieses Objekt einem Kunden zugeteilt?" + +#: stock/templates/stock/item_base.html:108 +#, fuzzy +#| msgid "Count stock" +msgid "Return to stock" +msgstr "Bestand zählen" + +#: stock/templates/stock/item_base.html:114 #: stock/templates/stock/location.html:30 #, fuzzy #| msgid "Stock Locations" msgid "Stock actions" msgstr "Lagerobjekt-Standorte" -#: stock/templates/stock/item_base.html:108 -#, fuzzy -#| msgid "Serialize Stock" -msgid "Serialize stock" -msgstr "Lagerbestand erfassen" - -#: 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:114 +#: stock/templates/stock/item_base.html:118 #, fuzzy #| msgid "Count stock items" msgid "Convert to variant" msgstr "Lagerobjekte zählen" -#: stock/templates/stock/item_base.html:116 +#: stock/templates/stock/item_base.html:120 #, fuzzy #| msgid "Count stock items" msgid "Duplicate stock item" msgstr "Lagerobjekte zählen" -#: stock/templates/stock/item_base.html:117 +#: stock/templates/stock/item_base.html:121 #, fuzzy #| msgid "Edit Stock Item" msgid "Edit stock item" msgstr "Lagerobjekt bearbeiten" -#: stock/templates/stock/item_base.html:119 +#: stock/templates/stock/item_base.html:123 #, 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:128 msgid "Generate test report" msgstr "" -#: stock/templates/stock/item_base.html:133 +#: stock/templates/stock/item_base.html:132 +msgid "Print labels" +msgstr "" + +#: stock/templates/stock/item_base.html:140 msgid "Stock Item Details" msgstr "Lagerbestands-Details" -#: stock/templates/stock/item_base.html:166 +#: stock/templates/stock/item_base.html:173 msgid "Belongs To" msgstr "Gehört zu" -#: stock/templates/stock/item_base.html:188 +#: stock/templates/stock/item_base.html:195 #, fuzzy #| msgid "No stock location set" msgid "No location set" msgstr "Kein Lagerort gesetzt" -#: stock/templates/stock/item_base.html:195 +#: stock/templates/stock/item_base.html:202 msgid "Unique Identifier" msgstr "Eindeutiger Bezeichner" -#: stock/templates/stock/item_base.html:223 +#: stock/templates/stock/item_base.html:230 msgid "Parent Item" msgstr "Elternposition" -#: stock/templates/stock/item_base.html:248 +#: stock/templates/stock/item_base.html:255 msgid "Last Updated" msgstr "Zuletzt aktualisiert" -#: stock/templates/stock/item_base.html:253 +#: stock/templates/stock/item_base.html:260 msgid "Last Stocktake" msgstr "Letzte Inventur" -#: stock/templates/stock/item_base.html:257 +#: stock/templates/stock/item_base.html:264 msgid "No stocktake performed" msgstr "Keine Inventur ausgeführt" @@ -3349,7 +3392,17 @@ msgstr "Lagerobjekt-Standorte" msgid "Are you sure you want to delete this stock location?" msgstr "Sind Sie sicher, dass Sie diesen Anhang löschen wollen?" -#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:934 +#: stock/templates/stock/stock_adjust.html:35 +#, fuzzy +#| msgid "" +#| "This stock item is serialized - it has a unique serial number and the " +#| "quantity cannot be adjusted." +msgid "Stock item is serialized and quantity cannot be adjusted" +msgstr "" +"Dieses Lagerobjekt ist serialisiert. Es hat eine eindeutige Seriennummer und " +"die Anzahl kann nicht angepasst werden." + +#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1052 #, fuzzy #| msgid "Count Stock Items" msgid "Convert Stock Item" @@ -3381,33 +3434,33 @@ msgstr "Baue" msgid "Children" msgstr "Kinder" -#: stock/views.py:113 +#: stock/views.py:114 msgid "Edit Stock Location" msgstr "Lagerobjekt-Standort bearbeiten" -#: stock/views.py:137 +#: stock/views.py:138 msgid "Stock Location QR code" msgstr "QR-Code für diesen Standort" -#: stock/views.py:155 +#: stock/views.py:156 #, fuzzy #| msgid "Add Attachment" msgid "Add Stock Item Attachment" msgstr "Anhang hinzufügen" -#: stock/views.py:200 +#: stock/views.py:201 #, fuzzy #| msgid "Edit Stock Item" msgid "Edit Stock Item Attachment" msgstr "Lagerobjekt bearbeiten" -#: stock/views.py:216 +#: stock/views.py:217 #, fuzzy #| msgid "Delete Part Attachment" msgid "Delete Stock Item Attachment" msgstr "Teilanhang löschen" -#: stock/views.py:232 +#: stock/views.py:233 #, fuzzy #| msgid "Item assigned to customer?" msgid "Assign to Customer" @@ -3415,178 +3468,212 @@ msgstr "Ist dieses Objekt einem Kunden zugeteilt?" #: stock/views.py:270 #, fuzzy +#| msgid "Part Stock" +msgid "Return to Stock" +msgstr "Teilbestand" + +#: stock/views.py:289 +#, fuzzy +#| msgid "Include sublocations" +msgid "Specify a valid location" +msgstr "Unterlagerorte einschließen" + +#: stock/views.py:293 +msgid "Stock item returned from customer" +msgstr "" + +#: stock/views.py:305 +#, fuzzy +#| msgid "Select valid part" +msgid "Select Label Template" +msgstr "Bitte ein gültiges Teil auswählen" + +#: stock/views.py:326 +#, fuzzy +#| msgid "Select valid part" +msgid "Select valid label" +msgstr "Bitte ein gültiges Teil auswählen" + +#: stock/views.py:388 +#, fuzzy #| msgid "Delete Template" msgid "Delete All Test Data" msgstr "Vorlage löschen" -#: stock/views.py:285 +#: stock/views.py:403 #, fuzzy #| msgid "Confirm Part Deletion" msgid "Confirm test data deletion" msgstr "Löschen des Teils bestätigen" -#: stock/views.py:305 +#: stock/views.py:423 msgid "Add Test Result" msgstr "" -#: stock/views.py:342 +#: stock/views.py:460 #, fuzzy #| msgid "Edit Template" msgid "Edit Test Result" msgstr "Vorlage bearbeiten" -#: stock/views.py:359 +#: stock/views.py:477 #, fuzzy #| msgid "Delete Template" msgid "Delete Test Result" msgstr "Vorlage löschen" -#: stock/views.py:370 +#: stock/views.py:488 #, fuzzy #| msgid "Delete Template" msgid "Select Test Report Template" msgstr "Vorlage löschen" -#: stock/views.py:384 +#: stock/views.py:502 #, fuzzy #| msgid "Select valid part" msgid "Select valid template" msgstr "Bitte ein gültiges Teil auswählen" -#: stock/views.py:436 +#: stock/views.py:554 msgid "Stock Export Options" msgstr "Lagerbestandsexportoptionen" -#: stock/views.py:556 +#: stock/views.py:674 msgid "Stock Item QR Code" msgstr "Lagerobjekt-QR-Code" -#: stock/views.py:579 +#: stock/views.py:697 msgid "Adjust Stock" msgstr "Lagerbestand anpassen" -#: stock/views.py:688 +#: stock/views.py:806 msgid "Move Stock Items" msgstr "Lagerobjekte bewegen" -#: stock/views.py:689 +#: stock/views.py:807 msgid "Count Stock Items" msgstr "Lagerobjekte zählen" -#: stock/views.py:690 +#: stock/views.py:808 msgid "Remove From Stock" msgstr "Aus Lagerbestand entfernen" -#: stock/views.py:691 +#: stock/views.py:809 msgid "Add Stock Items" msgstr "Lagerobjekte hinzufügen" -#: stock/views.py:692 +#: stock/views.py:810 msgid "Delete Stock Items" msgstr "Lagerobjekte löschen" -#: stock/views.py:720 +#: stock/views.py:838 msgid "Must enter integer value" msgstr "Nur Ganzzahl eingeben" -#: stock/views.py:725 +#: stock/views.py:843 msgid "Quantity must be positive" msgstr "Anzahl muss positiv sein" -#: stock/views.py:732 +#: stock/views.py:850 #, python-brace-format msgid "Quantity must not exceed {x}" msgstr "Anzahl darf {x} nicht überschreiten" -#: stock/views.py:740 +#: stock/views.py:858 msgid "Confirm stock adjustment" msgstr "Bestands-Anpassung bestätigen" -#: stock/views.py:811 +#: stock/views.py:929 #, python-brace-format msgid "Added stock to {n} items" msgstr "Vorrat zu {n} Lagerobjekten hinzugefügt" -#: stock/views.py:826 +#: stock/views.py:944 #, python-brace-format msgid "Removed stock from {n} items" msgstr "Vorrat von {n} Lagerobjekten entfernt" -#: stock/views.py:839 +#: stock/views.py:957 #, python-brace-format msgid "Counted stock for {n} items" msgstr "Bestand für {n} Objekte erfasst" -#: stock/views.py:867 +#: stock/views.py:985 msgid "No items were moved" msgstr "Keine Lagerobjekte wurden bewegt" -#: stock/views.py:870 +#: stock/views.py:988 #, python-brace-format msgid "Moved {n} items to {dest}" msgstr "{n} Teile nach {dest} bewegt" -#: stock/views.py:889 +#: stock/views.py:1007 #, python-brace-format msgid "Deleted {n} stock items" msgstr "{n} Teile im Lager gelöscht" -#: stock/views.py:901 +#: stock/views.py:1019 msgid "Edit Stock Item" msgstr "Lagerobjekt bearbeiten" -#: stock/views.py:961 +#: stock/views.py:1079 msgid "Create new Stock Location" msgstr "Neuen Lager-Standort erstellen" -#: stock/views.py:982 +#: stock/views.py:1100 msgid "Serialize Stock" msgstr "Lagerbestand erfassen" -#: stock/views.py:1074 +#: stock/views.py:1192 msgid "Create new Stock Item" msgstr "Neues Lagerobjekt hinzufügen" -#: stock/views.py:1167 +#: stock/views.py:1285 #, fuzzy #| msgid "Count stock items" msgid "Duplicate Stock Item" msgstr "Lagerobjekte zählen" -#: stock/views.py:1240 +#: stock/views.py:1358 msgid "Invalid quantity" msgstr "Ungültige Menge" -#: stock/views.py:1247 +#: stock/views.py:1361 +#, fuzzy +#| msgid "Quantity must be greater than zero" +msgid "Quantity cannot be less than zero" +msgstr "Anzahl muss größer Null sein" + +#: stock/views.py:1365 msgid "Invalid part selection" msgstr "Ungültige Teileauswahl" -#: stock/views.py:1296 +#: stock/views.py:1414 #, python-brace-format msgid "Created {n} new stock items" msgstr "{n} neue Lagerobjekte erstellt" -#: stock/views.py:1315 stock/views.py:1331 +#: stock/views.py:1433 stock/views.py:1449 msgid "Created new stock item" msgstr "Neues Lagerobjekt erstellt" -#: stock/views.py:1350 +#: stock/views.py:1468 msgid "Delete Stock Location" msgstr "Standort löschen" -#: stock/views.py:1363 +#: stock/views.py:1481 msgid "Delete Stock Item" msgstr "Lagerobjekt löschen" -#: stock/views.py:1374 +#: stock/views.py:1492 msgid "Delete Stock Tracking Entry" msgstr "Lagerbestands-Tracking-Eintrag löschen" -#: stock/views.py:1391 +#: stock/views.py:1509 msgid "Edit Stock Tracking Entry" msgstr "Lagerbestands-Tracking-Eintrag bearbeiten" -#: stock/views.py:1400 +#: stock/views.py:1518 msgid "Add Stock Tracking Entry" msgstr "Lagerbestands-Tracking-Eintrag hinzufügen" @@ -3839,7 +3926,7 @@ msgstr "Link" msgid "No purchase orders found" msgstr "Keine Bestellungen gefunden" -#: templates/js/order.html:170 templates/js/stock.html:612 +#: templates/js/order.html:170 templates/js/stock.html:625 msgid "Date" msgstr "Datum" @@ -3851,7 +3938,7 @@ msgstr "Keine Aufträge gefunden" msgid "Shipment Date" msgstr "Versanddatum" -#: templates/js/part.html:106 templates/js/stock.html:405 +#: templates/js/part.html:106 templates/js/stock.html:406 msgid "Select" msgstr "Auswählen" @@ -3867,7 +3954,7 @@ msgstr "Verkäufliches Teil" msgid "No category" msgstr "Keine Kategorie" -#: templates/js/part.html:214 templates/js/table_filters.html:157 +#: templates/js/part.html:214 templates/js/table_filters.html:167 msgid "Low stock" msgstr "Bestand niedrig" @@ -3893,13 +3980,13 @@ msgstr "" msgid "No test templates matching query" msgstr "Keine zur Anfrage passenden Lagerobjekte" -#: templates/js/part.html:387 templates/js/stock.html:62 +#: templates/js/part.html:387 templates/js/stock.html:63 #, fuzzy #| msgid "Edit Sales Order" msgid "Edit test result" msgstr "Auftrag bearbeiten" -#: templates/js/part.html:388 templates/js/stock.html:63 +#: templates/js/part.html:388 templates/js/stock.html:64 #, fuzzy #| msgid "Delete attachment" msgid "Delete test result" @@ -3909,165 +3996,215 @@ msgstr "Anhang löschen" msgid "This test is defined for a parent part" msgstr "" -#: templates/js/stock.html:25 +#: templates/js/stock.html:26 msgid "PASS" msgstr "" -#: templates/js/stock.html:27 +#: templates/js/stock.html:28 msgid "FAIL" msgstr "" -#: templates/js/stock.html:32 +#: templates/js/stock.html:33 msgid "NO RESULT" msgstr "" -#: templates/js/stock.html:58 +#: templates/js/stock.html:59 #, fuzzy #| msgid "Edit Sales Order" msgid "Add test result" msgstr "Auftrag bearbeiten" -#: templates/js/stock.html:76 +#: templates/js/stock.html:77 #, fuzzy #| msgid "No results found" msgid "No test results found" msgstr "Keine Ergebnisse gefunden" -#: templates/js/stock.html:117 +#: templates/js/stock.html:118 #, fuzzy #| msgid "Shipment Date" msgid "Test Date" msgstr "Versanddatum" -#: templates/js/stock.html:260 +#: templates/js/stock.html:261 msgid "No stock items matching query" msgstr "Keine zur Anfrage passenden Lagerobjekte" -#: templates/js/stock.html:357 templates/js/stock.html:372 +#: templates/js/stock.html:358 templates/js/stock.html:373 #, fuzzy #| msgid "Include sublocations" msgid "Undefined location" msgstr "Unterlagerorte einschließen" -#: templates/js/stock.html:469 -msgid "StockItem has been allocated" +#: templates/js/stock.html:464 +#, fuzzy +#| msgid "StockItem has been allocated" +msgid "Stock item has been allocated" msgstr "Lagerobjekt wurde zugewiesen" -#: templates/js/stock.html:474 -msgid "StockItem is lost" +#: templates/js/stock.html:468 +#, fuzzy +#| msgid "StockItem has been allocated" +msgid "Stock item has been assigned to customer" +msgstr "Lagerobjekt wurde zugewiesen" + +#: templates/js/stock.html:470 +#, fuzzy +#| msgid "This stock item is allocated to Sales Order" +msgid "Stock item was assigned to a build order" +msgstr "Dieses Lagerobjekt ist dem Auftrag zugewiesen" + +#: templates/js/stock.html:472 +#, fuzzy +#| msgid "This stock item is allocated to Sales Order" +msgid "Stock item was assigned to a sales order" +msgstr "Dieses Lagerobjekt ist dem Auftrag zugewiesen" + +#: templates/js/stock.html:479 +#, fuzzy +#| msgid "StockItem has been allocated" +msgid "Stock item has been rejected" +msgstr "Lagerobjekt wurde zugewiesen" + +#: templates/js/stock.html:483 +#, fuzzy +#| msgid "StockItem is lost" +msgid "Stock item is lost" msgstr "Lagerobjekt verloren" -#: templates/js/stock.html:503 +#: templates/js/stock.html:487 templates/js/table_filters.html:52 +#, fuzzy +#| msgid "Delete" +msgid "Depleted" +msgstr "Löschen" + +#: templates/js/stock.html:516 #, fuzzy #| msgid "Item assigned to customer?" msgid "Shipped to customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: templates/js/stock.html:506 +#: templates/js/stock.html:519 msgid "No stock location set" msgstr "Kein Lagerort gesetzt" -#: templates/js/stock.html:678 +#: templates/js/stock.html:691 msgid "No user information" msgstr "Keine Benutzerinformation" -#: templates/js/table_filters.html:19 templates/js/table_filters.html:62 +#: templates/js/table_filters.html:19 templates/js/table_filters.html:67 #, fuzzy #| msgid "Serialize Stock" msgid "Is Serialized" msgstr "Lagerbestand erfassen" -#: templates/js/table_filters.html:22 templates/js/table_filters.html:65 +#: templates/js/table_filters.html:22 templates/js/table_filters.html:70 #, fuzzy #| msgid "Serial Number" msgid "Serial number GTE" msgstr "Seriennummer" -#: templates/js/table_filters.html:23 templates/js/table_filters.html:66 +#: templates/js/table_filters.html:23 templates/js/table_filters.html:71 #, fuzzy #| msgid "Serial number for this item" msgid "Serial number greater than or equal to" msgstr "Seriennummer für dieses Teil" -#: templates/js/table_filters.html:26 templates/js/table_filters.html:69 +#: templates/js/table_filters.html:26 templates/js/table_filters.html:74 #, fuzzy #| msgid "Serial Number" msgid "Serial number LTE" msgstr "Seriennummer" -#: templates/js/table_filters.html:27 templates/js/table_filters.html:70 +#: templates/js/table_filters.html:27 templates/js/table_filters.html:75 #, fuzzy #| msgid "Serial numbers already exist: " msgid "Serial number less than or equal to" msgstr "Seriennummern existieren bereits:" -#: templates/js/table_filters.html:38 -msgid "Show items which are in stock" -msgstr "" - -#: templates/js/table_filters.html:42 -msgid "Include sublocations" -msgstr "Unterlagerorte einschließen" - -#: templates/js/table_filters.html:43 -msgid "Include stock in sublocations" -msgstr "Bestand in Unterlagerorten einschließen" - -#: templates/js/table_filters.html:47 +#: templates/js/table_filters.html:37 msgid "Active parts" msgstr "Aktive Teile" -#: templates/js/table_filters.html:48 +#: templates/js/table_filters.html:38 msgid "Show stock for active parts" msgstr "Bestand aktiver Teile anzeigen" -#: templates/js/table_filters.html:52 templates/js/table_filters.html:53 -msgid "Stock status" -msgstr "Bestandsstatus" - -#: templates/js/table_filters.html:57 +#: templates/js/table_filters.html:42 msgid "Is allocated" msgstr "Ist zugeordnet" -#: templates/js/table_filters.html:58 +#: templates/js/table_filters.html:43 msgid "Item has been alloacted" msgstr "Position wurde zugeordnet" -#: templates/js/table_filters.html:99 +#: templates/js/table_filters.html:47 +msgid "Include sublocations" +msgstr "Unterlagerorte einschließen" + +#: templates/js/table_filters.html:48 +msgid "Include stock in sublocations" +msgstr "Bestand in Unterlagerorten einschließen" + +#: templates/js/table_filters.html:53 +#, fuzzy +#| msgid "Delete this Stock Item when stock is depleted" +msgid "Show stock items which are depleted" +msgstr "Objekt löschen wenn Lagerbestand aufgebraucht" + +#: templates/js/table_filters.html:58 +msgid "Show items which are in stock" +msgstr "" + +#: templates/js/table_filters.html:62 +#, fuzzy +#| msgid "Item assigned to customer?" +msgid "Sent to customer" +msgstr "Ist dieses Objekt einem Kunden zugeteilt?" + +#: templates/js/table_filters.html:63 +msgid "Show items which have been assigned to a customer" +msgstr "" + +#: templates/js/table_filters.html:79 templates/js/table_filters.html:80 +msgid "Stock status" +msgstr "Bestandsstatus" + +#: templates/js/table_filters.html:109 msgid "Build status" msgstr "Bau-Status" -#: templates/js/table_filters.html:111 templates/js/table_filters.html:124 +#: templates/js/table_filters.html:121 templates/js/table_filters.html:134 msgid "Order status" msgstr "Bestellstatus" -#: templates/js/table_filters.html:116 templates/js/table_filters.html:129 +#: templates/js/table_filters.html:126 templates/js/table_filters.html:139 #, fuzzy #| msgid "Cascading" msgid "Outstanding" msgstr "Kaskadierend" -#: templates/js/table_filters.html:139 +#: templates/js/table_filters.html:149 msgid "Include subcategories" msgstr "Unterkategorien einschließen" -#: templates/js/table_filters.html:140 +#: templates/js/table_filters.html:150 msgid "Include parts in subcategories" msgstr "Teile in Unterkategorien einschließen" -#: templates/js/table_filters.html:145 +#: templates/js/table_filters.html:155 msgid "Show active parts" msgstr "Aktive Teile anzeigen" -#: templates/js/table_filters.html:153 +#: templates/js/table_filters.html:163 msgid "Stock available" msgstr "Bestand verfügbar" -#: templates/js/table_filters.html:169 +#: templates/js/table_filters.html:179 msgid "Starred" msgstr "Favorit" -#: templates/js/table_filters.html:181 +#: templates/js/table_filters.html:191 msgid "Purchasable" msgstr "Käuflich" @@ -4123,6 +4260,12 @@ msgstr "Bestand bestellen" msgid "Delete Stock" msgstr "Bestand löschen" +#~ msgid "Used for Build" +#~ msgstr "Verwendet für Bau" + +#~ msgid "Installed in Stock Item" +#~ msgstr "In Lagerobjekt installiert" + #~ msgid "Count stock items" #~ msgstr "Lagerobjekte zählen" diff --git a/InvenTree/locale/en/LC_MESSAGES/django.po b/InvenTree/locale/en/LC_MESSAGES/django.po index 972a9ee6af..dda559ee7e 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-07-30 10:24+0000\n" +"POT-Creation-Date: 2020-08-16 02:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -78,7 +78,7 @@ msgstr "" msgid "File comment" msgstr "" -#: InvenTree/models.py:68 templates/js/stock.html:669 +#: InvenTree/models.py:68 templates/js/stock.html:682 msgid "User" msgstr "" @@ -90,24 +90,24 @@ msgstr "" msgid "Description (optional)" msgstr "" -#: InvenTree/settings.py:330 +#: InvenTree/settings.py:335 msgid "English" msgstr "" -#: InvenTree/settings.py:331 +#: InvenTree/settings.py:336 msgid "German" msgstr "" -#: InvenTree/settings.py:332 +#: InvenTree/settings.py:337 msgid "French" msgstr "" -#: InvenTree/settings.py:333 +#: InvenTree/settings.py:338 msgid "Polish" msgstr "" #: InvenTree/status_codes.py:94 InvenTree/status_codes.py:135 -#: InvenTree/status_codes.py:235 +#: InvenTree/status_codes.py:222 msgid "Pending" msgstr "" @@ -115,59 +115,50 @@ msgstr "" msgid "Placed" msgstr "" -#: InvenTree/status_codes.py:96 InvenTree/status_codes.py:238 +#: InvenTree/status_codes.py:96 InvenTree/status_codes.py:225 msgid "Complete" msgstr "" #: InvenTree/status_codes.py:97 InvenTree/status_codes.py:137 -#: InvenTree/status_codes.py:237 +#: InvenTree/status_codes.py:224 msgid "Cancelled" msgstr "" #: InvenTree/status_codes.py:98 InvenTree/status_codes.py:138 -#: InvenTree/status_codes.py:179 +#: InvenTree/status_codes.py:175 msgid "Lost" msgstr "" #: InvenTree/status_codes.py:99 InvenTree/status_codes.py:139 -#: InvenTree/status_codes.py:181 +#: InvenTree/status_codes.py:177 msgid "Returned" msgstr "" -#: InvenTree/status_codes.py:136 InvenTree/status_codes.py:182 -#: order/templates/order/sales_order_base.html:98 +#: InvenTree/status_codes.py:136 order/templates/order/sales_order_base.html:98 msgid "Shipped" msgstr "" -#: InvenTree/status_codes.py:175 +#: InvenTree/status_codes.py:171 msgid "OK" msgstr "" -#: InvenTree/status_codes.py:176 +#: InvenTree/status_codes.py:172 msgid "Attention needed" msgstr "" -#: InvenTree/status_codes.py:177 +#: InvenTree/status_codes.py:173 msgid "Damaged" msgstr "" -#: InvenTree/status_codes.py:178 +#: InvenTree/status_codes.py:174 msgid "Destroyed" msgstr "" -#: InvenTree/status_codes.py:180 +#: InvenTree/status_codes.py:176 msgid "Rejected" msgstr "" -#: InvenTree/status_codes.py:183 -msgid "Used for Build" -msgstr "" - -#: InvenTree/status_codes.py:184 -msgid "Installed in Stock Item" -msgstr "" - -#: InvenTree/status_codes.py:236 build/templates/build/allocate.html:349 +#: InvenTree/status_codes.py:223 build/templates/build/allocate.html:349 #: order/templates/order/sales_order_detail.html:220 #: part/templates/part/tabs.html:23 templates/js/build.html:120 msgid "Allocated" @@ -250,7 +241,7 @@ msgstr "" msgid "Serial numbers" msgstr "" -#: build/forms.py:64 stock/forms.py:93 +#: build/forms.py:64 stock/forms.py:105 msgid "Enter unique serial numbers (or leave blank)" msgstr "" @@ -289,7 +280,7 @@ msgstr "" #: part/templates/part/set_category.html:13 templates/js/barcode.html:336 #: templates/js/bom.html:135 templates/js/build.html:41 #: templates/js/company.html:109 templates/js/part.html:120 -#: templates/js/stock.html:424 +#: templates/js/stock.html:425 msgid "Part" msgstr "" @@ -331,7 +322,7 @@ msgstr "" msgid "Build status code" msgstr "" -#: build/models.py:136 stock/models.py:374 +#: build/models.py:136 stock/models.py:376 msgid "Batch Code" msgstr "" @@ -343,21 +334,21 @@ 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:86 -#: stock/models.py:368 stock/templates/stock/item_base.html:230 +#: stock/models.py:370 stock/templates/stock/item_base.html:237 msgid "External Link" msgstr "" -#: build/models.py:156 stock/models.py:370 +#: build/models.py:156 stock/models.py:372 msgid "Link to external URL" msgstr "" -#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:307 +#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:310 #: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15 #: order/templates/order/purchase_order_detail.html:200 #: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:64 -#: stock/models.py:436 stock/models.py:1265 stock/templates/stock/tabs.html:26 +#: stock/models.py:438 stock/models.py:1287 stock/templates/stock/tabs.html:26 #: templates/js/barcode.html:391 templates/js/bom.html:229 -#: templates/js/stock.html:113 templates/js/stock.html:513 +#: templates/js/stock.html:114 templates/js/stock.html:526 msgid "Notes" msgstr "" @@ -387,15 +378,15 @@ msgstr "" msgid "Quantity must be 1 for serialized stock" msgstr "" -#: build/models.py:512 +#: build/models.py:511 msgid "Build to allocate parts" msgstr "" -#: build/models.py:519 +#: build/models.py:518 msgid "Stock Item to allocate to build" msgstr "" -#: build/models.py:532 +#: build/models.py:531 msgid "Stock quantity to allocate to build" msgstr "" @@ -422,8 +413,8 @@ 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:146 +#: order/templates/order/sales_order_detail.html:150 stock/models.py:364 +#: stock/templates/stock/item_base.html:153 msgid "Serial Number" msgstr "" @@ -440,18 +431,18 @@ 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:152 +#: stock/templates/stock/item_base.html:159 #: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.html:338 #: templates/js/bom.html:172 templates/js/build.html:52 -#: templates/js/stock.html:660 +#: templates/js/stock.html:673 msgid "Quantity" msgstr "" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:184 +#: stock/templates/stock/item_base.html:191 #: stock/templates/stock/stock_adjust.html:17 templates/js/barcode.html:337 -#: templates/js/stock.html:495 +#: templates/js/stock.html:508 msgid "Location" msgstr "" @@ -477,7 +468,7 @@ msgstr "" #: templates/js/bom.html:157 templates/js/company.html:60 #: templates/js/order.html:157 templates/js/order.html:230 #: templates/js/part.html:176 templates/js/part.html:355 -#: templates/js/stock.html:445 templates/js/stock.html:641 +#: templates/js/stock.html:440 templates/js/stock.html:654 msgid "Description" msgstr "" @@ -487,8 +478,8 @@ msgstr "" msgid "Reference" msgstr "" -#: build/templates/build/allocate.html:338 part/models.py:1269 -#: templates/js/part.html:359 templates/js/table_filters.html:90 +#: build/templates/build/allocate.html:338 part/models.py:1270 +#: templates/js/part.html:359 templates/js/table_filters.html:100 msgid "Required" msgstr "" @@ -531,7 +522,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:209 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:216 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "" @@ -551,9 +542,9 @@ 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:262 templates/js/barcode.html:42 +#: stock/templates/stock/item_base.html:269 templates/js/barcode.html:42 #: templates/js/build.html:57 templates/js/order.html:162 -#: templates/js/order.html:235 templates/js/stock.html:482 +#: templates/js/order.html:235 templates/js/stock.html:495 msgid "Status" msgstr "" @@ -563,7 +554,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:172 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:179 templates/js/order.html:209 msgid "Sales Order" msgstr "" @@ -630,7 +621,7 @@ msgid "Stock can be taken from any available location." msgstr "" #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:202 templates/js/stock.html:490 +#: stock/templates/stock/item_base.html:209 templates/js/stock.html:503 msgid "Batch" msgstr "" @@ -733,7 +724,7 @@ msgstr "" msgid "Confirm unallocation of build stock" msgstr "" -#: build/views.py:162 stock/views.py:286 +#: build/views.py:162 stock/views.py:404 msgid "Check the confirmation box" msgstr "" @@ -741,7 +732,7 @@ msgstr "" msgid "Complete Build" msgstr "" -#: build/views.py:201 stock/views.py:1118 stock/views.py:1232 +#: build/views.py:201 stock/views.py:1236 stock/views.py:1350 msgid "Next available serial number is" msgstr "" @@ -757,7 +748,7 @@ msgstr "" msgid "Invalid location selected" msgstr "" -#: build/views.py:300 stock/views.py:1268 +#: build/views.py:300 stock/views.py:1386 #, python-brace-format msgid "The following serial numbers already exist: ({sn})" msgstr "" @@ -850,113 +841,113 @@ msgstr "" msgid "Delete Currency" msgstr "" -#: company/models.py:83 company/models.py:84 +#: company/models.py:86 company/models.py:87 msgid "Company name" msgstr "" -#: company/models.py:86 +#: company/models.py:89 msgid "Company description" msgstr "" -#: company/models.py:86 +#: company/models.py:89 msgid "Description of the company" msgstr "" -#: company/models.py:88 company/templates/company/company_base.html:48 +#: company/models.py:91 company/templates/company/company_base.html:48 #: templates/js/company.html:65 msgid "Website" msgstr "" -#: company/models.py:88 +#: company/models.py:91 msgid "Company website URL" msgstr "" -#: company/models.py:91 company/templates/company/company_base.html:55 +#: company/models.py:94 company/templates/company/company_base.html:55 msgid "Address" msgstr "" -#: company/models.py:92 +#: company/models.py:95 msgid "Company address" msgstr "" -#: company/models.py:95 +#: company/models.py:98 msgid "Phone number" msgstr "" -#: company/models.py:96 +#: company/models.py:99 msgid "Contact phone number" msgstr "" -#: company/models.py:98 company/templates/company/company_base.html:69 +#: company/models.py:101 company/templates/company/company_base.html:69 msgid "Email" msgstr "" -#: company/models.py:98 +#: company/models.py:101 msgid "Contact email address" msgstr "" -#: company/models.py:101 company/templates/company/company_base.html:76 +#: company/models.py:104 company/templates/company/company_base.html:76 msgid "Contact" msgstr "" -#: company/models.py:102 +#: company/models.py:105 msgid "Point of contact" msgstr "" -#: company/models.py:104 +#: company/models.py:107 msgid "Link to external company information" msgstr "" -#: company/models.py:116 +#: company/models.py:119 msgid "Do you sell items to this company?" msgstr "" -#: company/models.py:118 +#: company/models.py:121 msgid "Do you purchase items from this company?" msgstr "" -#: company/models.py:120 +#: company/models.py:123 msgid "Does this company manufacture parts?" msgstr "" -#: company/models.py:276 stock/models.py:322 -#: stock/templates/stock/item_base.html:138 +#: company/models.py:279 stock/models.py:324 +#: stock/templates/stock/item_base.html:145 msgid "Base Part" msgstr "" -#: company/models.py:281 +#: company/models.py:284 msgid "Select part" msgstr "" -#: company/models.py:287 +#: company/models.py:290 msgid "Select supplier" msgstr "" -#: company/models.py:290 +#: company/models.py:293 msgid "Supplier stock keeping unit" msgstr "" -#: company/models.py:297 +#: company/models.py:300 msgid "Select manufacturer" msgstr "" -#: company/models.py:301 +#: company/models.py:304 msgid "Manufacturer part number" msgstr "" -#: company/models.py:303 +#: company/models.py:306 msgid "URL for external supplier part link" msgstr "" -#: company/models.py:305 +#: company/models.py:308 msgid "Supplier part description" msgstr "" -#: company/models.py:309 +#: company/models.py:312 msgid "Minimum charge (e.g. stocking fee)" msgstr "" -#: company/models.py:311 +#: company/models.py:314 msgid "Part packaging" msgstr "" @@ -991,14 +982,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:237 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:244 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:159 +#: order/templates/order/sales_order_base.html:73 stock/models.py:359 +#: stock/models.py:360 stock/templates/stock/item_base.html:166 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "" @@ -1053,7 +1044,7 @@ msgstr "" msgid "Supplier Stock" msgstr "" -#: company/templates/company/detail_stock.html:34 +#: company/templates/company/detail_stock.html:35 #: company/templates/company/supplier_part_stock.html:33 #: part/templates/part/stock.html:53 templates/stock_table.html:5 msgid "Export" @@ -1111,8 +1102,8 @@ msgid "New Sales Order" 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:242 templates/js/company.html:150 +#: company/templates/company/supplier_part_base.html:19 stock/models.py:333 +#: stock/templates/stock/item_base.html:249 templates/js/company.html:150 msgid "Supplier Part" msgstr "" @@ -1211,7 +1202,7 @@ msgstr "" #: company/templates/company/supplier_part_tabs.html:8 #: company/templates/company/tabs.html:12 part/templates/part/tabs.html:18 #: stock/templates/stock/location.html:12 templates/js/part.html:203 -#: templates/js/stock.html:453 templates/navbar.html:11 +#: templates/js/stock.html:448 templates/navbar.html:11 msgid "Stock" msgstr "" @@ -1311,6 +1302,22 @@ msgstr "" msgid "Delete Price Break" msgstr "" +#: label/models.py:55 +msgid "Label name" +msgstr "" + +#: label/models.py:58 +msgid "Label description" +msgstr "" + +#: label/models.py:63 +msgid "Label template file" +msgstr "" + +#: label/models.py:69 +msgid "Query filters (comma-separated list of key=value pairs" +msgstr "" + #: order/forms.py:24 msgid "Place order" msgstr "" @@ -1361,7 +1368,7 @@ msgid "Supplier order reference code" msgstr "" #: order/models.py:185 order/models.py:259 part/views.py:1167 -#: stock/models.py:243 stock/models.py:665 stock/views.py:1243 +#: stock/models.py:238 stock/models.py:687 msgid "Quantity must be greater than zero" msgstr "" @@ -1395,7 +1402,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:216 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:223 templates/js/order.html:136 msgid "Purchase Order" msgstr "" @@ -1672,7 +1679,7 @@ msgstr "" msgid "Add Purchase Order Attachment" msgstr "" -#: order/views.py:102 order/views.py:149 part/views.py:85 stock/views.py:166 +#: order/views.py:102 order/views.py:149 part/views.py:85 stock/views.py:167 msgid "Added attachment" msgstr "" @@ -1692,7 +1699,7 @@ msgstr "" msgid "Delete Attachment" msgstr "" -#: order/views.py:222 order/views.py:236 stock/views.py:222 +#: order/views.py:222 order/views.py:236 stock/views.py:223 msgid "Deleted attachment" msgstr "" @@ -1825,11 +1832,11 @@ msgstr "" msgid "Error reading BOM file (incorrect row size)" msgstr "" -#: part/forms.py:55 stock/forms.py:200 +#: part/forms.py:55 stock/forms.py:243 msgid "File Format" msgstr "" -#: part/forms.py:55 stock/forms.py:200 +#: part/forms.py:55 stock/forms.py:243 msgid "Select output file format" msgstr "" @@ -1877,207 +1884,220 @@ msgstr "" msgid "Default keywords for parts in this category" msgstr "" -#: part/models.py:427 +#: part/models.py:74 part/templates/part/part_app_base.html:9 +msgid "Part Category" +msgstr "" + +#: part/models.py:75 part/templates/part/category.html:13 +#: part/templates/part/category.html:78 templates/stats.html:12 +msgid "Part Categories" +msgstr "" + +#: part/models.py:428 msgid "Part must be unique for name, IPN and revision" msgstr "" -#: part/models.py:442 part/templates/part/detail.html:19 +#: part/models.py:443 part/templates/part/detail.html:19 msgid "Part name" msgstr "" -#: part/models.py:446 +#: part/models.py:447 msgid "Is this part a template part?" msgstr "" -#: part/models.py:455 +#: part/models.py:456 msgid "Is this part a variant of another part?" msgstr "" -#: part/models.py:457 +#: part/models.py:458 msgid "Part description" msgstr "" -#: part/models.py:459 +#: part/models.py:460 msgid "Part keywords to improve visibility in search results" msgstr "" -#: part/models.py:464 +#: part/models.py:465 msgid "Part category" msgstr "" -#: part/models.py:466 +#: part/models.py:467 msgid "Internal Part Number" msgstr "" -#: part/models.py:468 +#: part/models.py:469 msgid "Part revision or version number" msgstr "" -#: part/models.py:470 +#: part/models.py:471 msgid "Link to extenal URL" msgstr "" -#: part/models.py:482 +#: part/models.py:483 msgid "Where is this item normally stored?" msgstr "" -#: part/models.py:526 +#: part/models.py:527 msgid "Default supplier part" msgstr "" -#: part/models.py:529 +#: part/models.py:530 msgid "Minimum allowed stock level" msgstr "" -#: part/models.py:531 +#: part/models.py:532 msgid "Stock keeping units for this part" msgstr "" -#: part/models.py:533 +#: part/models.py:534 msgid "Can this part be built from other parts?" msgstr "" -#: part/models.py:535 +#: part/models.py:536 msgid "Can this part be used to build other parts?" msgstr "" -#: part/models.py:537 +#: part/models.py:538 msgid "Does this part have tracking for unique items?" msgstr "" -#: part/models.py:539 +#: part/models.py:540 msgid "Can this part be purchased from external suppliers?" msgstr "" -#: part/models.py:541 +#: part/models.py:542 msgid "Can this part be sold to customers?" msgstr "" -#: part/models.py:543 +#: part/models.py:544 msgid "Is this part active?" msgstr "" -#: part/models.py:545 +#: part/models.py:546 msgid "Is this a virtual part, such as a software product or license?" msgstr "" -#: part/models.py:547 +#: part/models.py:548 msgid "Part notes - supports Markdown formatting" msgstr "" -#: part/models.py:549 +#: part/models.py:550 msgid "Stored BOM checksum" msgstr "" -#: part/models.py:1221 +#: part/models.py:1222 msgid "Test templates can only be created for trackable parts" msgstr "" -#: part/models.py:1238 +#: part/models.py:1239 msgid "Test with this name already exists for this part" msgstr "" -#: part/models.py:1257 templates/js/part.html:350 templates/js/stock.html:89 +#: part/models.py:1258 templates/js/part.html:350 templates/js/stock.html:90 msgid "Test Name" msgstr "" -#: part/models.py:1258 +#: part/models.py:1259 msgid "Enter a name for the test" msgstr "" -#: part/models.py:1263 +#: part/models.py:1264 msgid "Test Description" msgstr "" -#: part/models.py:1264 +#: part/models.py:1265 msgid "Enter description for this test" msgstr "" -#: part/models.py:1270 +#: part/models.py:1271 msgid "Is this test required to pass?" msgstr "" -#: part/models.py:1275 templates/js/part.html:367 +#: part/models.py:1276 templates/js/part.html:367 msgid "Requires Value" msgstr "" -#: part/models.py:1276 +#: part/models.py:1277 msgid "Does this test require a value when adding a test result?" msgstr "" -#: part/models.py:1281 templates/js/part.html:374 +#: part/models.py:1282 templates/js/part.html:374 msgid "Requires Attachment" msgstr "" -#: part/models.py:1282 +#: part/models.py:1283 msgid "Does this test require a file attachment when adding a test result?" msgstr "" -#: part/models.py:1315 +#: part/models.py:1316 msgid "Parameter template name must be unique" msgstr "" -#: part/models.py:1320 +#: part/models.py:1321 msgid "Parameter Name" msgstr "" -#: part/models.py:1322 +#: part/models.py:1323 msgid "Parameter Units" msgstr "" -#: part/models.py:1348 +#: part/models.py:1349 msgid "Parent Part" msgstr "" -#: part/models.py:1350 +#: part/models.py:1351 msgid "Parameter Template" msgstr "" -#: part/models.py:1352 +#: part/models.py:1353 msgid "Parameter Value" msgstr "" -#: part/models.py:1381 +#: part/models.py:1382 msgid "Select parent part" msgstr "" -#: part/models.py:1389 +#: part/models.py:1390 msgid "Select part to be used in BOM" msgstr "" -#: part/models.py:1395 +#: part/models.py:1396 msgid "BOM quantity for this BOM item" msgstr "" -#: part/models.py:1398 +#: part/models.py:1399 msgid "Estimated build wastage quantity (absolute or percentage)" msgstr "" -#: part/models.py:1401 +#: part/models.py:1402 msgid "BOM item reference" msgstr "" -#: part/models.py:1404 +#: part/models.py:1405 msgid "BOM item notes" msgstr "" -#: part/models.py:1406 +#: part/models.py:1407 msgid "BOM line checksum" msgstr "" -#: part/models.py:1470 stock/models.py:233 +#: part/models.py:1471 stock/models.py:228 msgid "Quantity must be integer value for trackable parts" msgstr "" -#: part/models.py:1479 +#: part/models.py:1480 msgid "Part cannot be added to its own Bill of Materials" msgstr "" -#: part/models.py:1486 +#: part/models.py:1487 #, python-brace-format msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)" msgstr "" +#: part/models.py:1494 +msgid "BOM Item" +msgstr "" + #: part/templates/part/allocation.html:10 msgid "Part Stock Allocations" msgstr "" @@ -2093,14 +2113,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:224 +#: stock/templates/stock/item_base.html:231 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 -#: templates/js/stock.html:630 +#: templates/js/stock.html:643 msgid "Stock Item" msgstr "" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:178 +#: stock/templates/stock/item_base.html:185 msgid "Build Order" msgstr "" @@ -2140,11 +2160,6 @@ msgstr "" msgid "Export Bill of Materials" msgstr "" -#: part/templates/part/category.html:13 part/templates/part/category.html:78 -#: templates/stats.html:12 -msgid "Part Categories" -msgstr "" - #: part/templates/part/category.html:14 msgid "All parts" msgstr "" @@ -2238,8 +2253,8 @@ msgstr "" msgid "Part is not a virtual part" msgstr "" -#: part/templates/part/detail.html:139 stock/forms.py:194 -#: templates/js/table_filters.html:149 +#: part/templates/part/detail.html:139 stock/forms.py:237 +#: templates/js/table_filters.html:159 msgid "Template" msgstr "" @@ -2251,7 +2266,7 @@ msgstr "" msgid "Part is not a template part" msgstr "" -#: part/templates/part/detail.html:148 templates/js/table_filters.html:161 +#: part/templates/part/detail.html:148 templates/js/table_filters.html:171 msgid "Assembly" msgstr "" @@ -2263,7 +2278,7 @@ msgstr "" msgid "Part cannot be assembled from other parts" msgstr "" -#: part/templates/part/detail.html:157 templates/js/table_filters.html:165 +#: part/templates/part/detail.html:157 templates/js/table_filters.html:175 msgid "Component" msgstr "" @@ -2275,7 +2290,7 @@ msgstr "" msgid "Part cannot be used in assemblies" msgstr "" -#: part/templates/part/detail.html:166 templates/js/table_filters.html:177 +#: part/templates/part/detail.html:166 templates/js/table_filters.html:187 msgid "Trackable" msgstr "" @@ -2295,7 +2310,7 @@ msgstr "" msgid "Part can be purchased from external suppliers" msgstr "" -#: part/templates/part/detail.html:184 templates/js/table_filters.html:173 +#: part/templates/part/detail.html:184 templates/js/table_filters.html:183 msgid "Salable" msgstr "" @@ -2307,7 +2322,7 @@ msgstr "" msgid "Part cannot be sold to customers" msgstr "" -#: part/templates/part/detail.html:193 templates/js/table_filters.html:144 +#: part/templates/part/detail.html:193 templates/js/table_filters.html:154 msgid "Active" msgstr "" @@ -2339,8 +2354,8 @@ msgstr "" msgid "New Parameter" msgstr "" -#: part/templates/part/params.html:21 stock/models.py:1252 -#: templates/js/stock.html:109 +#: part/templates/part/params.html:21 stock/models.py:1274 +#: templates/js/stock.html:110 msgid "Value" msgstr "" @@ -2352,10 +2367,6 @@ msgstr "" msgid "Delete" msgstr "" -#: part/templates/part/part_app_base.html:9 -msgid "Part Category" -msgstr "" - #: part/templates/part/part_app_base.html:11 msgid "Part List" msgstr "" @@ -2423,7 +2434,7 @@ msgstr "" msgid "Available Stock" msgstr "" -#: part/templates/part/part_base.html:108 templates/js/table_filters.html:37 +#: part/templates/part/part_base.html:108 templates/js/table_filters.html:57 msgid "In Stock" msgstr "" @@ -2524,7 +2535,7 @@ msgstr "" msgid "Used In" msgstr "" -#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:268 +#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:275 msgid "Tests" msgstr "" @@ -2741,215 +2752,227 @@ msgstr "" msgid "Asset file description" msgstr "" -#: stock/forms.py:194 +#: stock/forms.py:185 +msgid "Label" +msgstr "" + +#: stock/forms.py:186 stock/forms.py:237 msgid "Select test report template" msgstr "" -#: stock/forms.py:202 +#: stock/forms.py:245 msgid "Include stock items in sub locations" msgstr "" -#: stock/forms.py:235 +#: stock/forms.py:278 msgid "Destination stock location" msgstr "" -#: stock/forms.py:241 +#: stock/forms.py:284 msgid "Confirm movement of stock items" msgstr "" -#: stock/forms.py:243 +#: stock/forms.py:286 msgid "Set the destination as the default location for selected parts" msgstr "" -#: stock/models.py:208 +#: stock/models.py:209 msgid "StockItem with this serial number already exists" msgstr "" -#: stock/models.py:250 +#: stock/models.py:245 #, python-brace-format msgid "Part type ('{pf}') must be {pe}" msgstr "" -#: stock/models.py:260 stock/models.py:269 +#: stock/models.py:255 stock/models.py:264 msgid "Quantity must be 1 for item with a serial number" msgstr "" -#: stock/models.py:261 +#: stock/models.py:256 msgid "Serial number cannot be set if quantity greater than 1" msgstr "" -#: stock/models.py:282 +#: stock/models.py:277 msgid "Item cannot belong to itself" msgstr "" -#: stock/models.py:314 +#: stock/models.py:316 msgid "Parent Stock Item" msgstr "" -#: stock/models.py:323 +#: stock/models.py:325 msgid "Base part" msgstr "" -#: stock/models.py:332 +#: stock/models.py:334 msgid "Select a matching supplier part for this stock item" msgstr "" -#: stock/models.py:337 stock/templates/stock/stock_app_base.html:7 +#: stock/models.py:339 stock/templates/stock/stock_app_base.html:7 msgid "Stock Location" msgstr "" -#: stock/models.py:340 +#: stock/models.py:342 msgid "Where is this stock item located?" msgstr "" -#: stock/models.py:345 +#: stock/models.py:347 msgid "Installed In" msgstr "" -#: stock/models.py:348 +#: stock/models.py:350 msgid "Is this item installed in another item?" msgstr "" -#: stock/models.py:364 +#: stock/models.py:366 msgid "Serial number for this item" msgstr "" -#: stock/models.py:376 +#: stock/models.py:378 msgid "Batch code for this stock item" msgstr "" -#: stock/models.py:380 +#: stock/models.py:382 msgid "Stock Quantity" msgstr "" -#: stock/models.py:389 +#: stock/models.py:391 msgid "Source Build" msgstr "" -#: stock/models.py:391 +#: stock/models.py:393 msgid "Build for this stock item" msgstr "" -#: stock/models.py:398 +#: stock/models.py:400 msgid "Source Purchase Order" msgstr "" -#: stock/models.py:401 +#: stock/models.py:403 msgid "Purchase order for this stock item" msgstr "" -#: stock/models.py:407 +#: stock/models.py:409 msgid "Destination Sales Order" msgstr "" -#: stock/models.py:414 +#: stock/models.py:416 msgid "Destination Build Order" msgstr "" -#: stock/models.py:427 +#: stock/models.py:429 msgid "Delete this Stock Item when stock is depleted" msgstr "" -#: stock/models.py:437 stock/templates/stock/item_notes.html:14 +#: stock/models.py:439 stock/templates/stock/item_notes.html:14 #: stock/templates/stock/item_notes.html:30 msgid "Stock Item Notes" msgstr "" -#: stock/models.py:489 +#: stock/models.py:490 msgid "Assigned to Customer" msgstr "" -#: stock/models.py:491 +#: stock/models.py:492 msgid "Manually assigned to customer" msgstr "" -#: stock/models.py:656 +#: stock/models.py:505 +msgid "Returned from customer" +msgstr "" + +#: stock/models.py:507 +msgid "Returned to location" +msgstr "" + +#: stock/models.py:678 msgid "Part is not set as trackable" msgstr "" -#: stock/models.py:662 +#: stock/models.py:684 msgid "Quantity must be integer" msgstr "" -#: stock/models.py:668 +#: stock/models.py:690 #, python-brace-format msgid "Quantity must not exceed available stock quantity ({n})" msgstr "" -#: stock/models.py:671 stock/models.py:674 +#: stock/models.py:693 stock/models.py:696 msgid "Serial numbers must be a list of integers" msgstr "" -#: stock/models.py:677 +#: stock/models.py:699 msgid "Quantity does not match serial numbers" msgstr "" -#: stock/models.py:687 +#: stock/models.py:709 msgid "Serial numbers already exist: " msgstr "" -#: stock/models.py:712 +#: stock/models.py:734 msgid "Add serial number" msgstr "" -#: stock/models.py:715 +#: stock/models.py:737 #, python-brace-format msgid "Serialized {n} items" msgstr "" -#: stock/models.py:826 +#: stock/models.py:848 msgid "StockItem cannot be moved as it is not in stock" msgstr "" -#: stock/models.py:1153 +#: stock/models.py:1175 msgid "Tracking entry title" msgstr "" -#: stock/models.py:1155 +#: stock/models.py:1177 msgid "Entry notes" msgstr "" -#: stock/models.py:1157 +#: stock/models.py:1179 msgid "Link to external page for further information" msgstr "" -#: stock/models.py:1217 +#: stock/models.py:1239 msgid "Value must be provided for this test" msgstr "" -#: stock/models.py:1223 +#: stock/models.py:1245 msgid "Attachment must be uploaded for this test" msgstr "" -#: stock/models.py:1240 +#: stock/models.py:1262 msgid "Test" msgstr "" -#: stock/models.py:1241 +#: stock/models.py:1263 msgid "Test name" msgstr "" -#: stock/models.py:1246 +#: stock/models.py:1268 msgid "Result" msgstr "" -#: stock/models.py:1247 templates/js/table_filters.html:80 +#: stock/models.py:1269 templates/js/table_filters.html:90 msgid "Test result" msgstr "" -#: stock/models.py:1253 +#: stock/models.py:1275 msgid "Test output value" msgstr "" -#: stock/models.py:1259 +#: stock/models.py:1281 msgid "Attachment" msgstr "" -#: stock/models.py:1260 +#: stock/models.py:1282 msgid "Test result attachment" msgstr "" -#: stock/models.py:1266 +#: stock/models.py:1288 msgid "Test notes" msgstr "" @@ -2997,7 +3020,7 @@ msgstr "" msgid "Link Barcode" msgstr "" -#: stock/templates/stock/item_base.html:92 +#: stock/templates/stock/item_base.html:91 msgid "Stock adjustment actions" msgstr "" @@ -3018,68 +3041,76 @@ msgstr "" msgid "Transfer stock" msgstr "" +#: stock/templates/stock/item_base.html:101 +msgid "Serialize stock" +msgstr "" + #: stock/templates/stock/item_base.html:105 +msgid "Assign to customer" +msgstr "" + +#: stock/templates/stock/item_base.html:108 +msgid "Return to stock" +msgstr "" + +#: stock/templates/stock/item_base.html:114 #: stock/templates/stock/location.html:30 msgid "Stock actions" msgstr "" -#: stock/templates/stock/item_base.html:108 -msgid "Serialize stock" -msgstr "" - -#: stock/templates/stock/item_base.html:111 -msgid "Assign to customer" -msgstr "" - -#: stock/templates/stock/item_base.html:114 +#: stock/templates/stock/item_base.html:118 msgid "Convert to variant" msgstr "" -#: stock/templates/stock/item_base.html:116 +#: stock/templates/stock/item_base.html:120 msgid "Duplicate stock item" msgstr "" -#: stock/templates/stock/item_base.html:117 +#: stock/templates/stock/item_base.html:121 msgid "Edit stock item" msgstr "" -#: stock/templates/stock/item_base.html:119 +#: stock/templates/stock/item_base.html:123 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:124 +#: stock/templates/stock/item_base.html:128 msgid "Generate test report" msgstr "" -#: stock/templates/stock/item_base.html:133 +#: stock/templates/stock/item_base.html:132 +msgid "Print labels" +msgstr "" + +#: stock/templates/stock/item_base.html:140 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:166 +#: stock/templates/stock/item_base.html:173 msgid "Belongs To" msgstr "" -#: stock/templates/stock/item_base.html:188 +#: stock/templates/stock/item_base.html:195 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:195 +#: stock/templates/stock/item_base.html:202 msgid "Unique Identifier" msgstr "" -#: stock/templates/stock/item_base.html:223 +#: stock/templates/stock/item_base.html:230 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:248 +#: stock/templates/stock/item_base.html:255 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:253 +#: stock/templates/stock/item_base.html:260 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:257 +#: stock/templates/stock/item_base.html:264 msgid "No stocktake performed" msgstr "" @@ -3166,7 +3197,11 @@ msgstr "" msgid "Are you sure you want to delete this stock location?" msgstr "" -#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:934 +#: stock/templates/stock/stock_adjust.html:35 +msgid "Stock item is serialized and quantity cannot be adjusted" +msgstr "" + +#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1052 msgid "Convert Stock Item" msgstr "" @@ -3194,190 +3229,214 @@ msgstr "" msgid "Children" msgstr "" -#: stock/views.py:113 +#: stock/views.py:114 msgid "Edit Stock Location" msgstr "" -#: stock/views.py:137 +#: stock/views.py:138 msgid "Stock Location QR code" msgstr "" -#: stock/views.py:155 +#: stock/views.py:156 msgid "Add Stock Item Attachment" msgstr "" -#: stock/views.py:200 +#: stock/views.py:201 msgid "Edit Stock Item Attachment" msgstr "" -#: stock/views.py:216 +#: stock/views.py:217 msgid "Delete Stock Item Attachment" msgstr "" -#: stock/views.py:232 +#: stock/views.py:233 msgid "Assign to Customer" msgstr "" #: stock/views.py:270 -msgid "Delete All Test Data" +msgid "Return to Stock" msgstr "" -#: stock/views.py:285 -msgid "Confirm test data deletion" +#: stock/views.py:289 +msgid "Specify a valid location" +msgstr "" + +#: stock/views.py:293 +msgid "Stock item returned from customer" msgstr "" #: stock/views.py:305 +msgid "Select Label Template" +msgstr "" + +#: stock/views.py:326 +msgid "Select valid label" +msgstr "" + +#: stock/views.py:388 +msgid "Delete All Test Data" +msgstr "" + +#: stock/views.py:403 +msgid "Confirm test data deletion" +msgstr "" + +#: stock/views.py:423 msgid "Add Test Result" msgstr "" -#: stock/views.py:342 +#: stock/views.py:460 msgid "Edit Test Result" msgstr "" -#: stock/views.py:359 +#: stock/views.py:477 msgid "Delete Test Result" msgstr "" -#: stock/views.py:370 +#: stock/views.py:488 msgid "Select Test Report Template" msgstr "" -#: stock/views.py:384 +#: stock/views.py:502 msgid "Select valid template" msgstr "" -#: stock/views.py:436 +#: stock/views.py:554 msgid "Stock Export Options" msgstr "" -#: stock/views.py:556 +#: stock/views.py:674 msgid "Stock Item QR Code" msgstr "" -#: stock/views.py:579 +#: stock/views.py:697 msgid "Adjust Stock" msgstr "" -#: stock/views.py:688 +#: stock/views.py:806 msgid "Move Stock Items" msgstr "" -#: stock/views.py:689 +#: stock/views.py:807 msgid "Count Stock Items" msgstr "" -#: stock/views.py:690 +#: stock/views.py:808 msgid "Remove From Stock" msgstr "" -#: stock/views.py:691 +#: stock/views.py:809 msgid "Add Stock Items" msgstr "" -#: stock/views.py:692 +#: stock/views.py:810 msgid "Delete Stock Items" msgstr "" -#: stock/views.py:720 +#: stock/views.py:838 msgid "Must enter integer value" msgstr "" -#: stock/views.py:725 +#: stock/views.py:843 msgid "Quantity must be positive" msgstr "" -#: stock/views.py:732 +#: stock/views.py:850 #, python-brace-format msgid "Quantity must not exceed {x}" msgstr "" -#: stock/views.py:740 +#: stock/views.py:858 msgid "Confirm stock adjustment" msgstr "" -#: stock/views.py:811 +#: stock/views.py:929 #, python-brace-format msgid "Added stock to {n} items" msgstr "" -#: stock/views.py:826 +#: stock/views.py:944 #, python-brace-format msgid "Removed stock from {n} items" msgstr "" -#: stock/views.py:839 +#: stock/views.py:957 #, python-brace-format msgid "Counted stock for {n} items" msgstr "" -#: stock/views.py:867 +#: stock/views.py:985 msgid "No items were moved" msgstr "" -#: stock/views.py:870 +#: stock/views.py:988 #, python-brace-format msgid "Moved {n} items to {dest}" msgstr "" -#: stock/views.py:889 +#: stock/views.py:1007 #, python-brace-format msgid "Deleted {n} stock items" msgstr "" -#: stock/views.py:901 +#: stock/views.py:1019 msgid "Edit Stock Item" msgstr "" -#: stock/views.py:961 +#: stock/views.py:1079 msgid "Create new Stock Location" msgstr "" -#: stock/views.py:982 +#: stock/views.py:1100 msgid "Serialize Stock" msgstr "" -#: stock/views.py:1074 +#: stock/views.py:1192 msgid "Create new Stock Item" msgstr "" -#: stock/views.py:1167 +#: stock/views.py:1285 msgid "Duplicate Stock Item" msgstr "" -#: stock/views.py:1240 +#: stock/views.py:1358 msgid "Invalid quantity" msgstr "" -#: stock/views.py:1247 +#: stock/views.py:1361 +msgid "Quantity cannot be less than zero" +msgstr "" + +#: stock/views.py:1365 msgid "Invalid part selection" msgstr "" -#: stock/views.py:1296 +#: stock/views.py:1414 #, python-brace-format msgid "Created {n} new stock items" msgstr "" -#: stock/views.py:1315 stock/views.py:1331 +#: stock/views.py:1433 stock/views.py:1449 msgid "Created new stock item" msgstr "" -#: stock/views.py:1350 +#: stock/views.py:1468 msgid "Delete Stock Location" msgstr "" -#: stock/views.py:1363 +#: stock/views.py:1481 msgid "Delete Stock Item" msgstr "" -#: stock/views.py:1374 +#: stock/views.py:1492 msgid "Delete Stock Tracking Entry" msgstr "" -#: stock/views.py:1391 +#: stock/views.py:1509 msgid "Edit Stock Tracking Entry" msgstr "" -#: stock/views.py:1400 +#: stock/views.py:1518 msgid "Add Stock Tracking Entry" msgstr "" @@ -3602,7 +3661,7 @@ msgstr "" msgid "No purchase orders found" msgstr "" -#: templates/js/order.html:170 templates/js/stock.html:612 +#: templates/js/order.html:170 templates/js/stock.html:625 msgid "Date" msgstr "" @@ -3614,7 +3673,7 @@ msgstr "" msgid "Shipment Date" msgstr "" -#: templates/js/part.html:106 templates/js/stock.html:405 +#: templates/js/part.html:106 templates/js/stock.html:406 msgid "Select" msgstr "" @@ -3630,7 +3689,7 @@ msgstr "" msgid "No category" msgstr "" -#: templates/js/part.html:214 templates/js/table_filters.html:157 +#: templates/js/part.html:214 templates/js/table_filters.html:167 msgid "Low stock" msgstr "" @@ -3654,11 +3713,11 @@ msgstr "" msgid "No test templates matching query" msgstr "" -#: templates/js/part.html:387 templates/js/stock.html:62 +#: templates/js/part.html:387 templates/js/stock.html:63 msgid "Edit test result" msgstr "" -#: templates/js/part.html:388 templates/js/stock.html:63 +#: templates/js/part.html:388 templates/js/stock.html:64 msgid "Delete test result" msgstr "" @@ -3666,143 +3725,175 @@ msgstr "" msgid "This test is defined for a parent part" msgstr "" -#: templates/js/stock.html:25 +#: templates/js/stock.html:26 msgid "PASS" msgstr "" -#: templates/js/stock.html:27 +#: templates/js/stock.html:28 msgid "FAIL" msgstr "" -#: templates/js/stock.html:32 +#: templates/js/stock.html:33 msgid "NO RESULT" msgstr "" -#: templates/js/stock.html:58 +#: templates/js/stock.html:59 msgid "Add test result" msgstr "" -#: templates/js/stock.html:76 +#: templates/js/stock.html:77 msgid "No test results found" msgstr "" -#: templates/js/stock.html:117 +#: templates/js/stock.html:118 msgid "Test Date" msgstr "" -#: templates/js/stock.html:260 +#: templates/js/stock.html:261 msgid "No stock items matching query" msgstr "" -#: templates/js/stock.html:357 templates/js/stock.html:372 +#: templates/js/stock.html:358 templates/js/stock.html:373 msgid "Undefined location" msgstr "" -#: templates/js/stock.html:469 -msgid "StockItem has been allocated" +#: templates/js/stock.html:464 +msgid "Stock item has been allocated" msgstr "" -#: templates/js/stock.html:474 -msgid "StockItem is lost" +#: templates/js/stock.html:468 +msgid "Stock item has been assigned to customer" msgstr "" -#: templates/js/stock.html:503 +#: templates/js/stock.html:470 +msgid "Stock item was assigned to a build order" +msgstr "" + +#: templates/js/stock.html:472 +msgid "Stock item was assigned to a sales order" +msgstr "" + +#: templates/js/stock.html:479 +msgid "Stock item has been rejected" +msgstr "" + +#: templates/js/stock.html:483 +msgid "Stock item is lost" +msgstr "" + +#: templates/js/stock.html:487 templates/js/table_filters.html:52 +msgid "Depleted" +msgstr "" + +#: templates/js/stock.html:516 msgid "Shipped to customer" msgstr "" -#: templates/js/stock.html:506 +#: templates/js/stock.html:519 msgid "No stock location set" msgstr "" -#: templates/js/stock.html:678 +#: templates/js/stock.html:691 msgid "No user information" msgstr "" -#: templates/js/table_filters.html:19 templates/js/table_filters.html:62 +#: templates/js/table_filters.html:19 templates/js/table_filters.html:67 msgid "Is Serialized" msgstr "" -#: templates/js/table_filters.html:22 templates/js/table_filters.html:65 +#: templates/js/table_filters.html:22 templates/js/table_filters.html:70 msgid "Serial number GTE" msgstr "" -#: templates/js/table_filters.html:23 templates/js/table_filters.html:66 +#: templates/js/table_filters.html:23 templates/js/table_filters.html:71 msgid "Serial number greater than or equal to" msgstr "" -#: templates/js/table_filters.html:26 templates/js/table_filters.html:69 +#: templates/js/table_filters.html:26 templates/js/table_filters.html:74 msgid "Serial number LTE" msgstr "" -#: templates/js/table_filters.html:27 templates/js/table_filters.html:70 +#: templates/js/table_filters.html:27 templates/js/table_filters.html:75 msgid "Serial number less than or equal to" msgstr "" -#: templates/js/table_filters.html:38 -msgid "Show items which are in stock" -msgstr "" - -#: templates/js/table_filters.html:42 -msgid "Include sublocations" -msgstr "" - -#: templates/js/table_filters.html:43 -msgid "Include stock in sublocations" -msgstr "" - -#: templates/js/table_filters.html:47 +#: templates/js/table_filters.html:37 msgid "Active parts" msgstr "" -#: templates/js/table_filters.html:48 +#: templates/js/table_filters.html:38 msgid "Show stock for active parts" msgstr "" -#: templates/js/table_filters.html:52 templates/js/table_filters.html:53 -msgid "Stock status" -msgstr "" - -#: templates/js/table_filters.html:57 +#: templates/js/table_filters.html:42 msgid "Is allocated" msgstr "" -#: templates/js/table_filters.html:58 +#: templates/js/table_filters.html:43 msgid "Item has been alloacted" msgstr "" -#: templates/js/table_filters.html:99 +#: templates/js/table_filters.html:47 +msgid "Include sublocations" +msgstr "" + +#: templates/js/table_filters.html:48 +msgid "Include stock in sublocations" +msgstr "" + +#: templates/js/table_filters.html:53 +msgid "Show stock items which are depleted" +msgstr "" + +#: templates/js/table_filters.html:58 +msgid "Show items which are in stock" +msgstr "" + +#: templates/js/table_filters.html:62 +msgid "Sent to customer" +msgstr "" + +#: templates/js/table_filters.html:63 +msgid "Show items which have been assigned to a customer" +msgstr "" + +#: templates/js/table_filters.html:79 templates/js/table_filters.html:80 +msgid "Stock status" +msgstr "" + +#: templates/js/table_filters.html:109 msgid "Build status" msgstr "" -#: templates/js/table_filters.html:111 templates/js/table_filters.html:124 +#: templates/js/table_filters.html:121 templates/js/table_filters.html:134 msgid "Order status" msgstr "" -#: templates/js/table_filters.html:116 templates/js/table_filters.html:129 +#: templates/js/table_filters.html:126 templates/js/table_filters.html:139 msgid "Outstanding" msgstr "" -#: templates/js/table_filters.html:139 +#: templates/js/table_filters.html:149 msgid "Include subcategories" msgstr "" -#: templates/js/table_filters.html:140 +#: templates/js/table_filters.html:150 msgid "Include parts in subcategories" msgstr "" -#: templates/js/table_filters.html:145 +#: templates/js/table_filters.html:155 msgid "Show active parts" msgstr "" -#: templates/js/table_filters.html:153 +#: templates/js/table_filters.html:163 msgid "Stock available" msgstr "" -#: templates/js/table_filters.html:169 +#: templates/js/table_filters.html:179 msgid "Starred" msgstr "" -#: templates/js/table_filters.html:181 +#: templates/js/table_filters.html:191 msgid "Purchasable" msgstr "" diff --git a/InvenTree/locale/es/LC_MESSAGES/django.po b/InvenTree/locale/es/LC_MESSAGES/django.po index 972a9ee6af..dda559ee7e 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-07-30 10:24+0000\n" +"POT-Creation-Date: 2020-08-16 02:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -78,7 +78,7 @@ msgstr "" msgid "File comment" msgstr "" -#: InvenTree/models.py:68 templates/js/stock.html:669 +#: InvenTree/models.py:68 templates/js/stock.html:682 msgid "User" msgstr "" @@ -90,24 +90,24 @@ msgstr "" msgid "Description (optional)" msgstr "" -#: InvenTree/settings.py:330 +#: InvenTree/settings.py:335 msgid "English" msgstr "" -#: InvenTree/settings.py:331 +#: InvenTree/settings.py:336 msgid "German" msgstr "" -#: InvenTree/settings.py:332 +#: InvenTree/settings.py:337 msgid "French" msgstr "" -#: InvenTree/settings.py:333 +#: InvenTree/settings.py:338 msgid "Polish" msgstr "" #: InvenTree/status_codes.py:94 InvenTree/status_codes.py:135 -#: InvenTree/status_codes.py:235 +#: InvenTree/status_codes.py:222 msgid "Pending" msgstr "" @@ -115,59 +115,50 @@ msgstr "" msgid "Placed" msgstr "" -#: InvenTree/status_codes.py:96 InvenTree/status_codes.py:238 +#: InvenTree/status_codes.py:96 InvenTree/status_codes.py:225 msgid "Complete" msgstr "" #: InvenTree/status_codes.py:97 InvenTree/status_codes.py:137 -#: InvenTree/status_codes.py:237 +#: InvenTree/status_codes.py:224 msgid "Cancelled" msgstr "" #: InvenTree/status_codes.py:98 InvenTree/status_codes.py:138 -#: InvenTree/status_codes.py:179 +#: InvenTree/status_codes.py:175 msgid "Lost" msgstr "" #: InvenTree/status_codes.py:99 InvenTree/status_codes.py:139 -#: InvenTree/status_codes.py:181 +#: InvenTree/status_codes.py:177 msgid "Returned" msgstr "" -#: InvenTree/status_codes.py:136 InvenTree/status_codes.py:182 -#: order/templates/order/sales_order_base.html:98 +#: InvenTree/status_codes.py:136 order/templates/order/sales_order_base.html:98 msgid "Shipped" msgstr "" -#: InvenTree/status_codes.py:175 +#: InvenTree/status_codes.py:171 msgid "OK" msgstr "" -#: InvenTree/status_codes.py:176 +#: InvenTree/status_codes.py:172 msgid "Attention needed" msgstr "" -#: InvenTree/status_codes.py:177 +#: InvenTree/status_codes.py:173 msgid "Damaged" msgstr "" -#: InvenTree/status_codes.py:178 +#: InvenTree/status_codes.py:174 msgid "Destroyed" msgstr "" -#: InvenTree/status_codes.py:180 +#: InvenTree/status_codes.py:176 msgid "Rejected" msgstr "" -#: InvenTree/status_codes.py:183 -msgid "Used for Build" -msgstr "" - -#: InvenTree/status_codes.py:184 -msgid "Installed in Stock Item" -msgstr "" - -#: InvenTree/status_codes.py:236 build/templates/build/allocate.html:349 +#: InvenTree/status_codes.py:223 build/templates/build/allocate.html:349 #: order/templates/order/sales_order_detail.html:220 #: part/templates/part/tabs.html:23 templates/js/build.html:120 msgid "Allocated" @@ -250,7 +241,7 @@ msgstr "" msgid "Serial numbers" msgstr "" -#: build/forms.py:64 stock/forms.py:93 +#: build/forms.py:64 stock/forms.py:105 msgid "Enter unique serial numbers (or leave blank)" msgstr "" @@ -289,7 +280,7 @@ msgstr "" #: part/templates/part/set_category.html:13 templates/js/barcode.html:336 #: templates/js/bom.html:135 templates/js/build.html:41 #: templates/js/company.html:109 templates/js/part.html:120 -#: templates/js/stock.html:424 +#: templates/js/stock.html:425 msgid "Part" msgstr "" @@ -331,7 +322,7 @@ msgstr "" msgid "Build status code" msgstr "" -#: build/models.py:136 stock/models.py:374 +#: build/models.py:136 stock/models.py:376 msgid "Batch Code" msgstr "" @@ -343,21 +334,21 @@ 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:86 -#: stock/models.py:368 stock/templates/stock/item_base.html:230 +#: stock/models.py:370 stock/templates/stock/item_base.html:237 msgid "External Link" msgstr "" -#: build/models.py:156 stock/models.py:370 +#: build/models.py:156 stock/models.py:372 msgid "Link to external URL" msgstr "" -#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:307 +#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:310 #: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15 #: order/templates/order/purchase_order_detail.html:200 #: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:64 -#: stock/models.py:436 stock/models.py:1265 stock/templates/stock/tabs.html:26 +#: stock/models.py:438 stock/models.py:1287 stock/templates/stock/tabs.html:26 #: templates/js/barcode.html:391 templates/js/bom.html:229 -#: templates/js/stock.html:113 templates/js/stock.html:513 +#: templates/js/stock.html:114 templates/js/stock.html:526 msgid "Notes" msgstr "" @@ -387,15 +378,15 @@ msgstr "" msgid "Quantity must be 1 for serialized stock" msgstr "" -#: build/models.py:512 +#: build/models.py:511 msgid "Build to allocate parts" msgstr "" -#: build/models.py:519 +#: build/models.py:518 msgid "Stock Item to allocate to build" msgstr "" -#: build/models.py:532 +#: build/models.py:531 msgid "Stock quantity to allocate to build" msgstr "" @@ -422,8 +413,8 @@ 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:146 +#: order/templates/order/sales_order_detail.html:150 stock/models.py:364 +#: stock/templates/stock/item_base.html:153 msgid "Serial Number" msgstr "" @@ -440,18 +431,18 @@ 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:152 +#: stock/templates/stock/item_base.html:159 #: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.html:338 #: templates/js/bom.html:172 templates/js/build.html:52 -#: templates/js/stock.html:660 +#: templates/js/stock.html:673 msgid "Quantity" msgstr "" #: build/templates/build/allocate.html:177 #: build/templates/build/auto_allocate.html:20 -#: stock/templates/stock/item_base.html:184 +#: stock/templates/stock/item_base.html:191 #: stock/templates/stock/stock_adjust.html:17 templates/js/barcode.html:337 -#: templates/js/stock.html:495 +#: templates/js/stock.html:508 msgid "Location" msgstr "" @@ -477,7 +468,7 @@ msgstr "" #: templates/js/bom.html:157 templates/js/company.html:60 #: templates/js/order.html:157 templates/js/order.html:230 #: templates/js/part.html:176 templates/js/part.html:355 -#: templates/js/stock.html:445 templates/js/stock.html:641 +#: templates/js/stock.html:440 templates/js/stock.html:654 msgid "Description" msgstr "" @@ -487,8 +478,8 @@ msgstr "" msgid "Reference" msgstr "" -#: build/templates/build/allocate.html:338 part/models.py:1269 -#: templates/js/part.html:359 templates/js/table_filters.html:90 +#: build/templates/build/allocate.html:338 part/models.py:1270 +#: templates/js/part.html:359 templates/js/table_filters.html:100 msgid "Required" msgstr "" @@ -531,7 +522,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:209 templates/js/build.html:33 +#: stock/templates/stock/item_base.html:216 templates/js/build.html:33 #: templates/navbar.html:12 msgid "Build" msgstr "" @@ -551,9 +542,9 @@ 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:262 templates/js/barcode.html:42 +#: stock/templates/stock/item_base.html:269 templates/js/barcode.html:42 #: templates/js/build.html:57 templates/js/order.html:162 -#: templates/js/order.html:235 templates/js/stock.html:482 +#: templates/js/order.html:235 templates/js/stock.html:495 msgid "Status" msgstr "" @@ -563,7 +554,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:172 templates/js/order.html:209 +#: stock/templates/stock/item_base.html:179 templates/js/order.html:209 msgid "Sales Order" msgstr "" @@ -630,7 +621,7 @@ msgid "Stock can be taken from any available location." msgstr "" #: build/templates/build/detail.html:48 -#: stock/templates/stock/item_base.html:202 templates/js/stock.html:490 +#: stock/templates/stock/item_base.html:209 templates/js/stock.html:503 msgid "Batch" msgstr "" @@ -733,7 +724,7 @@ msgstr "" msgid "Confirm unallocation of build stock" msgstr "" -#: build/views.py:162 stock/views.py:286 +#: build/views.py:162 stock/views.py:404 msgid "Check the confirmation box" msgstr "" @@ -741,7 +732,7 @@ msgstr "" msgid "Complete Build" msgstr "" -#: build/views.py:201 stock/views.py:1118 stock/views.py:1232 +#: build/views.py:201 stock/views.py:1236 stock/views.py:1350 msgid "Next available serial number is" msgstr "" @@ -757,7 +748,7 @@ msgstr "" msgid "Invalid location selected" msgstr "" -#: build/views.py:300 stock/views.py:1268 +#: build/views.py:300 stock/views.py:1386 #, python-brace-format msgid "The following serial numbers already exist: ({sn})" msgstr "" @@ -850,113 +841,113 @@ msgstr "" msgid "Delete Currency" msgstr "" -#: company/models.py:83 company/models.py:84 +#: company/models.py:86 company/models.py:87 msgid "Company name" msgstr "" -#: company/models.py:86 +#: company/models.py:89 msgid "Company description" msgstr "" -#: company/models.py:86 +#: company/models.py:89 msgid "Description of the company" msgstr "" -#: company/models.py:88 company/templates/company/company_base.html:48 +#: company/models.py:91 company/templates/company/company_base.html:48 #: templates/js/company.html:65 msgid "Website" msgstr "" -#: company/models.py:88 +#: company/models.py:91 msgid "Company website URL" msgstr "" -#: company/models.py:91 company/templates/company/company_base.html:55 +#: company/models.py:94 company/templates/company/company_base.html:55 msgid "Address" msgstr "" -#: company/models.py:92 +#: company/models.py:95 msgid "Company address" msgstr "" -#: company/models.py:95 +#: company/models.py:98 msgid "Phone number" msgstr "" -#: company/models.py:96 +#: company/models.py:99 msgid "Contact phone number" msgstr "" -#: company/models.py:98 company/templates/company/company_base.html:69 +#: company/models.py:101 company/templates/company/company_base.html:69 msgid "Email" msgstr "" -#: company/models.py:98 +#: company/models.py:101 msgid "Contact email address" msgstr "" -#: company/models.py:101 company/templates/company/company_base.html:76 +#: company/models.py:104 company/templates/company/company_base.html:76 msgid "Contact" msgstr "" -#: company/models.py:102 +#: company/models.py:105 msgid "Point of contact" msgstr "" -#: company/models.py:104 +#: company/models.py:107 msgid "Link to external company information" msgstr "" -#: company/models.py:116 +#: company/models.py:119 msgid "Do you sell items to this company?" msgstr "" -#: company/models.py:118 +#: company/models.py:121 msgid "Do you purchase items from this company?" msgstr "" -#: company/models.py:120 +#: company/models.py:123 msgid "Does this company manufacture parts?" msgstr "" -#: company/models.py:276 stock/models.py:322 -#: stock/templates/stock/item_base.html:138 +#: company/models.py:279 stock/models.py:324 +#: stock/templates/stock/item_base.html:145 msgid "Base Part" msgstr "" -#: company/models.py:281 +#: company/models.py:284 msgid "Select part" msgstr "" -#: company/models.py:287 +#: company/models.py:290 msgid "Select supplier" msgstr "" -#: company/models.py:290 +#: company/models.py:293 msgid "Supplier stock keeping unit" msgstr "" -#: company/models.py:297 +#: company/models.py:300 msgid "Select manufacturer" msgstr "" -#: company/models.py:301 +#: company/models.py:304 msgid "Manufacturer part number" msgstr "" -#: company/models.py:303 +#: company/models.py:306 msgid "URL for external supplier part link" msgstr "" -#: company/models.py:305 +#: company/models.py:308 msgid "Supplier part description" msgstr "" -#: company/models.py:309 +#: company/models.py:312 msgid "Minimum charge (e.g. stocking fee)" msgstr "" -#: company/models.py:311 +#: company/models.py:314 msgid "Part packaging" msgstr "" @@ -991,14 +982,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:237 templates/js/company.html:52 +#: stock/templates/stock/item_base.html:244 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:159 +#: order/templates/order/sales_order_base.html:73 stock/models.py:359 +#: stock/models.py:360 stock/templates/stock/item_base.html:166 #: templates/js/company.html:44 templates/js/order.html:217 msgid "Customer" msgstr "" @@ -1053,7 +1044,7 @@ msgstr "" msgid "Supplier Stock" msgstr "" -#: company/templates/company/detail_stock.html:34 +#: company/templates/company/detail_stock.html:35 #: company/templates/company/supplier_part_stock.html:33 #: part/templates/part/stock.html:53 templates/stock_table.html:5 msgid "Export" @@ -1111,8 +1102,8 @@ msgid "New Sales Order" 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:242 templates/js/company.html:150 +#: company/templates/company/supplier_part_base.html:19 stock/models.py:333 +#: stock/templates/stock/item_base.html:249 templates/js/company.html:150 msgid "Supplier Part" msgstr "" @@ -1211,7 +1202,7 @@ msgstr "" #: company/templates/company/supplier_part_tabs.html:8 #: company/templates/company/tabs.html:12 part/templates/part/tabs.html:18 #: stock/templates/stock/location.html:12 templates/js/part.html:203 -#: templates/js/stock.html:453 templates/navbar.html:11 +#: templates/js/stock.html:448 templates/navbar.html:11 msgid "Stock" msgstr "" @@ -1311,6 +1302,22 @@ msgstr "" msgid "Delete Price Break" msgstr "" +#: label/models.py:55 +msgid "Label name" +msgstr "" + +#: label/models.py:58 +msgid "Label description" +msgstr "" + +#: label/models.py:63 +msgid "Label template file" +msgstr "" + +#: label/models.py:69 +msgid "Query filters (comma-separated list of key=value pairs" +msgstr "" + #: order/forms.py:24 msgid "Place order" msgstr "" @@ -1361,7 +1368,7 @@ msgid "Supplier order reference code" msgstr "" #: order/models.py:185 order/models.py:259 part/views.py:1167 -#: stock/models.py:243 stock/models.py:665 stock/views.py:1243 +#: stock/models.py:238 stock/models.py:687 msgid "Quantity must be greater than zero" msgstr "" @@ -1395,7 +1402,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:216 templates/js/order.html:136 +#: stock/templates/stock/item_base.html:223 templates/js/order.html:136 msgid "Purchase Order" msgstr "" @@ -1672,7 +1679,7 @@ msgstr "" msgid "Add Purchase Order Attachment" msgstr "" -#: order/views.py:102 order/views.py:149 part/views.py:85 stock/views.py:166 +#: order/views.py:102 order/views.py:149 part/views.py:85 stock/views.py:167 msgid "Added attachment" msgstr "" @@ -1692,7 +1699,7 @@ msgstr "" msgid "Delete Attachment" msgstr "" -#: order/views.py:222 order/views.py:236 stock/views.py:222 +#: order/views.py:222 order/views.py:236 stock/views.py:223 msgid "Deleted attachment" msgstr "" @@ -1825,11 +1832,11 @@ msgstr "" msgid "Error reading BOM file (incorrect row size)" msgstr "" -#: part/forms.py:55 stock/forms.py:200 +#: part/forms.py:55 stock/forms.py:243 msgid "File Format" msgstr "" -#: part/forms.py:55 stock/forms.py:200 +#: part/forms.py:55 stock/forms.py:243 msgid "Select output file format" msgstr "" @@ -1877,207 +1884,220 @@ msgstr "" msgid "Default keywords for parts in this category" msgstr "" -#: part/models.py:427 +#: part/models.py:74 part/templates/part/part_app_base.html:9 +msgid "Part Category" +msgstr "" + +#: part/models.py:75 part/templates/part/category.html:13 +#: part/templates/part/category.html:78 templates/stats.html:12 +msgid "Part Categories" +msgstr "" + +#: part/models.py:428 msgid "Part must be unique for name, IPN and revision" msgstr "" -#: part/models.py:442 part/templates/part/detail.html:19 +#: part/models.py:443 part/templates/part/detail.html:19 msgid "Part name" msgstr "" -#: part/models.py:446 +#: part/models.py:447 msgid "Is this part a template part?" msgstr "" -#: part/models.py:455 +#: part/models.py:456 msgid "Is this part a variant of another part?" msgstr "" -#: part/models.py:457 +#: part/models.py:458 msgid "Part description" msgstr "" -#: part/models.py:459 +#: part/models.py:460 msgid "Part keywords to improve visibility in search results" msgstr "" -#: part/models.py:464 +#: part/models.py:465 msgid "Part category" msgstr "" -#: part/models.py:466 +#: part/models.py:467 msgid "Internal Part Number" msgstr "" -#: part/models.py:468 +#: part/models.py:469 msgid "Part revision or version number" msgstr "" -#: part/models.py:470 +#: part/models.py:471 msgid "Link to extenal URL" msgstr "" -#: part/models.py:482 +#: part/models.py:483 msgid "Where is this item normally stored?" msgstr "" -#: part/models.py:526 +#: part/models.py:527 msgid "Default supplier part" msgstr "" -#: part/models.py:529 +#: part/models.py:530 msgid "Minimum allowed stock level" msgstr "" -#: part/models.py:531 +#: part/models.py:532 msgid "Stock keeping units for this part" msgstr "" -#: part/models.py:533 +#: part/models.py:534 msgid "Can this part be built from other parts?" msgstr "" -#: part/models.py:535 +#: part/models.py:536 msgid "Can this part be used to build other parts?" msgstr "" -#: part/models.py:537 +#: part/models.py:538 msgid "Does this part have tracking for unique items?" msgstr "" -#: part/models.py:539 +#: part/models.py:540 msgid "Can this part be purchased from external suppliers?" msgstr "" -#: part/models.py:541 +#: part/models.py:542 msgid "Can this part be sold to customers?" msgstr "" -#: part/models.py:543 +#: part/models.py:544 msgid "Is this part active?" msgstr "" -#: part/models.py:545 +#: part/models.py:546 msgid "Is this a virtual part, such as a software product or license?" msgstr "" -#: part/models.py:547 +#: part/models.py:548 msgid "Part notes - supports Markdown formatting" msgstr "" -#: part/models.py:549 +#: part/models.py:550 msgid "Stored BOM checksum" msgstr "" -#: part/models.py:1221 +#: part/models.py:1222 msgid "Test templates can only be created for trackable parts" msgstr "" -#: part/models.py:1238 +#: part/models.py:1239 msgid "Test with this name already exists for this part" msgstr "" -#: part/models.py:1257 templates/js/part.html:350 templates/js/stock.html:89 +#: part/models.py:1258 templates/js/part.html:350 templates/js/stock.html:90 msgid "Test Name" msgstr "" -#: part/models.py:1258 +#: part/models.py:1259 msgid "Enter a name for the test" msgstr "" -#: part/models.py:1263 +#: part/models.py:1264 msgid "Test Description" msgstr "" -#: part/models.py:1264 +#: part/models.py:1265 msgid "Enter description for this test" msgstr "" -#: part/models.py:1270 +#: part/models.py:1271 msgid "Is this test required to pass?" msgstr "" -#: part/models.py:1275 templates/js/part.html:367 +#: part/models.py:1276 templates/js/part.html:367 msgid "Requires Value" msgstr "" -#: part/models.py:1276 +#: part/models.py:1277 msgid "Does this test require a value when adding a test result?" msgstr "" -#: part/models.py:1281 templates/js/part.html:374 +#: part/models.py:1282 templates/js/part.html:374 msgid "Requires Attachment" msgstr "" -#: part/models.py:1282 +#: part/models.py:1283 msgid "Does this test require a file attachment when adding a test result?" msgstr "" -#: part/models.py:1315 +#: part/models.py:1316 msgid "Parameter template name must be unique" msgstr "" -#: part/models.py:1320 +#: part/models.py:1321 msgid "Parameter Name" msgstr "" -#: part/models.py:1322 +#: part/models.py:1323 msgid "Parameter Units" msgstr "" -#: part/models.py:1348 +#: part/models.py:1349 msgid "Parent Part" msgstr "" -#: part/models.py:1350 +#: part/models.py:1351 msgid "Parameter Template" msgstr "" -#: part/models.py:1352 +#: part/models.py:1353 msgid "Parameter Value" msgstr "" -#: part/models.py:1381 +#: part/models.py:1382 msgid "Select parent part" msgstr "" -#: part/models.py:1389 +#: part/models.py:1390 msgid "Select part to be used in BOM" msgstr "" -#: part/models.py:1395 +#: part/models.py:1396 msgid "BOM quantity for this BOM item" msgstr "" -#: part/models.py:1398 +#: part/models.py:1399 msgid "Estimated build wastage quantity (absolute or percentage)" msgstr "" -#: part/models.py:1401 +#: part/models.py:1402 msgid "BOM item reference" msgstr "" -#: part/models.py:1404 +#: part/models.py:1405 msgid "BOM item notes" msgstr "" -#: part/models.py:1406 +#: part/models.py:1407 msgid "BOM line checksum" msgstr "" -#: part/models.py:1470 stock/models.py:233 +#: part/models.py:1471 stock/models.py:228 msgid "Quantity must be integer value for trackable parts" msgstr "" -#: part/models.py:1479 +#: part/models.py:1480 msgid "Part cannot be added to its own Bill of Materials" msgstr "" -#: part/models.py:1486 +#: part/models.py:1487 #, python-brace-format msgid "Part '{p1}' is used in BOM for '{p2}' (recursive)" msgstr "" +#: part/models.py:1494 +msgid "BOM Item" +msgstr "" + #: part/templates/part/allocation.html:10 msgid "Part Stock Allocations" msgstr "" @@ -2093,14 +2113,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:224 +#: stock/templates/stock/item_base.html:231 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.html:106 -#: templates/js/stock.html:630 +#: templates/js/stock.html:643 msgid "Stock Item" msgstr "" #: part/templates/part/allocation.html:20 -#: stock/templates/stock/item_base.html:178 +#: stock/templates/stock/item_base.html:185 msgid "Build Order" msgstr "" @@ -2140,11 +2160,6 @@ msgstr "" msgid "Export Bill of Materials" msgstr "" -#: part/templates/part/category.html:13 part/templates/part/category.html:78 -#: templates/stats.html:12 -msgid "Part Categories" -msgstr "" - #: part/templates/part/category.html:14 msgid "All parts" msgstr "" @@ -2238,8 +2253,8 @@ msgstr "" msgid "Part is not a virtual part" msgstr "" -#: part/templates/part/detail.html:139 stock/forms.py:194 -#: templates/js/table_filters.html:149 +#: part/templates/part/detail.html:139 stock/forms.py:237 +#: templates/js/table_filters.html:159 msgid "Template" msgstr "" @@ -2251,7 +2266,7 @@ msgstr "" msgid "Part is not a template part" msgstr "" -#: part/templates/part/detail.html:148 templates/js/table_filters.html:161 +#: part/templates/part/detail.html:148 templates/js/table_filters.html:171 msgid "Assembly" msgstr "" @@ -2263,7 +2278,7 @@ msgstr "" msgid "Part cannot be assembled from other parts" msgstr "" -#: part/templates/part/detail.html:157 templates/js/table_filters.html:165 +#: part/templates/part/detail.html:157 templates/js/table_filters.html:175 msgid "Component" msgstr "" @@ -2275,7 +2290,7 @@ msgstr "" msgid "Part cannot be used in assemblies" msgstr "" -#: part/templates/part/detail.html:166 templates/js/table_filters.html:177 +#: part/templates/part/detail.html:166 templates/js/table_filters.html:187 msgid "Trackable" msgstr "" @@ -2295,7 +2310,7 @@ msgstr "" msgid "Part can be purchased from external suppliers" msgstr "" -#: part/templates/part/detail.html:184 templates/js/table_filters.html:173 +#: part/templates/part/detail.html:184 templates/js/table_filters.html:183 msgid "Salable" msgstr "" @@ -2307,7 +2322,7 @@ msgstr "" msgid "Part cannot be sold to customers" msgstr "" -#: part/templates/part/detail.html:193 templates/js/table_filters.html:144 +#: part/templates/part/detail.html:193 templates/js/table_filters.html:154 msgid "Active" msgstr "" @@ -2339,8 +2354,8 @@ msgstr "" msgid "New Parameter" msgstr "" -#: part/templates/part/params.html:21 stock/models.py:1252 -#: templates/js/stock.html:109 +#: part/templates/part/params.html:21 stock/models.py:1274 +#: templates/js/stock.html:110 msgid "Value" msgstr "" @@ -2352,10 +2367,6 @@ msgstr "" msgid "Delete" msgstr "" -#: part/templates/part/part_app_base.html:9 -msgid "Part Category" -msgstr "" - #: part/templates/part/part_app_base.html:11 msgid "Part List" msgstr "" @@ -2423,7 +2434,7 @@ msgstr "" msgid "Available Stock" msgstr "" -#: part/templates/part/part_base.html:108 templates/js/table_filters.html:37 +#: part/templates/part/part_base.html:108 templates/js/table_filters.html:57 msgid "In Stock" msgstr "" @@ -2524,7 +2535,7 @@ msgstr "" msgid "Used In" msgstr "" -#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:268 +#: part/templates/part/tabs.html:55 stock/templates/stock/item_base.html:275 msgid "Tests" msgstr "" @@ -2741,215 +2752,227 @@ msgstr "" msgid "Asset file description" msgstr "" -#: stock/forms.py:194 +#: stock/forms.py:185 +msgid "Label" +msgstr "" + +#: stock/forms.py:186 stock/forms.py:237 msgid "Select test report template" msgstr "" -#: stock/forms.py:202 +#: stock/forms.py:245 msgid "Include stock items in sub locations" msgstr "" -#: stock/forms.py:235 +#: stock/forms.py:278 msgid "Destination stock location" msgstr "" -#: stock/forms.py:241 +#: stock/forms.py:284 msgid "Confirm movement of stock items" msgstr "" -#: stock/forms.py:243 +#: stock/forms.py:286 msgid "Set the destination as the default location for selected parts" msgstr "" -#: stock/models.py:208 +#: stock/models.py:209 msgid "StockItem with this serial number already exists" msgstr "" -#: stock/models.py:250 +#: stock/models.py:245 #, python-brace-format msgid "Part type ('{pf}') must be {pe}" msgstr "" -#: stock/models.py:260 stock/models.py:269 +#: stock/models.py:255 stock/models.py:264 msgid "Quantity must be 1 for item with a serial number" msgstr "" -#: stock/models.py:261 +#: stock/models.py:256 msgid "Serial number cannot be set if quantity greater than 1" msgstr "" -#: stock/models.py:282 +#: stock/models.py:277 msgid "Item cannot belong to itself" msgstr "" -#: stock/models.py:314 +#: stock/models.py:316 msgid "Parent Stock Item" msgstr "" -#: stock/models.py:323 +#: stock/models.py:325 msgid "Base part" msgstr "" -#: stock/models.py:332 +#: stock/models.py:334 msgid "Select a matching supplier part for this stock item" msgstr "" -#: stock/models.py:337 stock/templates/stock/stock_app_base.html:7 +#: stock/models.py:339 stock/templates/stock/stock_app_base.html:7 msgid "Stock Location" msgstr "" -#: stock/models.py:340 +#: stock/models.py:342 msgid "Where is this stock item located?" msgstr "" -#: stock/models.py:345 +#: stock/models.py:347 msgid "Installed In" msgstr "" -#: stock/models.py:348 +#: stock/models.py:350 msgid "Is this item installed in another item?" msgstr "" -#: stock/models.py:364 +#: stock/models.py:366 msgid "Serial number for this item" msgstr "" -#: stock/models.py:376 +#: stock/models.py:378 msgid "Batch code for this stock item" msgstr "" -#: stock/models.py:380 +#: stock/models.py:382 msgid "Stock Quantity" msgstr "" -#: stock/models.py:389 +#: stock/models.py:391 msgid "Source Build" msgstr "" -#: stock/models.py:391 +#: stock/models.py:393 msgid "Build for this stock item" msgstr "" -#: stock/models.py:398 +#: stock/models.py:400 msgid "Source Purchase Order" msgstr "" -#: stock/models.py:401 +#: stock/models.py:403 msgid "Purchase order for this stock item" msgstr "" -#: stock/models.py:407 +#: stock/models.py:409 msgid "Destination Sales Order" msgstr "" -#: stock/models.py:414 +#: stock/models.py:416 msgid "Destination Build Order" msgstr "" -#: stock/models.py:427 +#: stock/models.py:429 msgid "Delete this Stock Item when stock is depleted" msgstr "" -#: stock/models.py:437 stock/templates/stock/item_notes.html:14 +#: stock/models.py:439 stock/templates/stock/item_notes.html:14 #: stock/templates/stock/item_notes.html:30 msgid "Stock Item Notes" msgstr "" -#: stock/models.py:489 +#: stock/models.py:490 msgid "Assigned to Customer" msgstr "" -#: stock/models.py:491 +#: stock/models.py:492 msgid "Manually assigned to customer" msgstr "" -#: stock/models.py:656 +#: stock/models.py:505 +msgid "Returned from customer" +msgstr "" + +#: stock/models.py:507 +msgid "Returned to location" +msgstr "" + +#: stock/models.py:678 msgid "Part is not set as trackable" msgstr "" -#: stock/models.py:662 +#: stock/models.py:684 msgid "Quantity must be integer" msgstr "" -#: stock/models.py:668 +#: stock/models.py:690 #, python-brace-format msgid "Quantity must not exceed available stock quantity ({n})" msgstr "" -#: stock/models.py:671 stock/models.py:674 +#: stock/models.py:693 stock/models.py:696 msgid "Serial numbers must be a list of integers" msgstr "" -#: stock/models.py:677 +#: stock/models.py:699 msgid "Quantity does not match serial numbers" msgstr "" -#: stock/models.py:687 +#: stock/models.py:709 msgid "Serial numbers already exist: " msgstr "" -#: stock/models.py:712 +#: stock/models.py:734 msgid "Add serial number" msgstr "" -#: stock/models.py:715 +#: stock/models.py:737 #, python-brace-format msgid "Serialized {n} items" msgstr "" -#: stock/models.py:826 +#: stock/models.py:848 msgid "StockItem cannot be moved as it is not in stock" msgstr "" -#: stock/models.py:1153 +#: stock/models.py:1175 msgid "Tracking entry title" msgstr "" -#: stock/models.py:1155 +#: stock/models.py:1177 msgid "Entry notes" msgstr "" -#: stock/models.py:1157 +#: stock/models.py:1179 msgid "Link to external page for further information" msgstr "" -#: stock/models.py:1217 +#: stock/models.py:1239 msgid "Value must be provided for this test" msgstr "" -#: stock/models.py:1223 +#: stock/models.py:1245 msgid "Attachment must be uploaded for this test" msgstr "" -#: stock/models.py:1240 +#: stock/models.py:1262 msgid "Test" msgstr "" -#: stock/models.py:1241 +#: stock/models.py:1263 msgid "Test name" msgstr "" -#: stock/models.py:1246 +#: stock/models.py:1268 msgid "Result" msgstr "" -#: stock/models.py:1247 templates/js/table_filters.html:80 +#: stock/models.py:1269 templates/js/table_filters.html:90 msgid "Test result" msgstr "" -#: stock/models.py:1253 +#: stock/models.py:1275 msgid "Test output value" msgstr "" -#: stock/models.py:1259 +#: stock/models.py:1281 msgid "Attachment" msgstr "" -#: stock/models.py:1260 +#: stock/models.py:1282 msgid "Test result attachment" msgstr "" -#: stock/models.py:1266 +#: stock/models.py:1288 msgid "Test notes" msgstr "" @@ -2997,7 +3020,7 @@ msgstr "" msgid "Link Barcode" msgstr "" -#: stock/templates/stock/item_base.html:92 +#: stock/templates/stock/item_base.html:91 msgid "Stock adjustment actions" msgstr "" @@ -3018,68 +3041,76 @@ msgstr "" msgid "Transfer stock" msgstr "" +#: stock/templates/stock/item_base.html:101 +msgid "Serialize stock" +msgstr "" + #: stock/templates/stock/item_base.html:105 +msgid "Assign to customer" +msgstr "" + +#: stock/templates/stock/item_base.html:108 +msgid "Return to stock" +msgstr "" + +#: stock/templates/stock/item_base.html:114 #: stock/templates/stock/location.html:30 msgid "Stock actions" msgstr "" -#: stock/templates/stock/item_base.html:108 -msgid "Serialize stock" -msgstr "" - -#: stock/templates/stock/item_base.html:111 -msgid "Assign to customer" -msgstr "" - -#: stock/templates/stock/item_base.html:114 +#: stock/templates/stock/item_base.html:118 msgid "Convert to variant" msgstr "" -#: stock/templates/stock/item_base.html:116 +#: stock/templates/stock/item_base.html:120 msgid "Duplicate stock item" msgstr "" -#: stock/templates/stock/item_base.html:117 +#: stock/templates/stock/item_base.html:121 msgid "Edit stock item" msgstr "" -#: stock/templates/stock/item_base.html:119 +#: stock/templates/stock/item_base.html:123 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:124 +#: stock/templates/stock/item_base.html:128 msgid "Generate test report" msgstr "" -#: stock/templates/stock/item_base.html:133 +#: stock/templates/stock/item_base.html:132 +msgid "Print labels" +msgstr "" + +#: stock/templates/stock/item_base.html:140 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:166 +#: stock/templates/stock/item_base.html:173 msgid "Belongs To" msgstr "" -#: stock/templates/stock/item_base.html:188 +#: stock/templates/stock/item_base.html:195 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:195 +#: stock/templates/stock/item_base.html:202 msgid "Unique Identifier" msgstr "" -#: stock/templates/stock/item_base.html:223 +#: stock/templates/stock/item_base.html:230 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:248 +#: stock/templates/stock/item_base.html:255 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:253 +#: stock/templates/stock/item_base.html:260 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:257 +#: stock/templates/stock/item_base.html:264 msgid "No stocktake performed" msgstr "" @@ -3166,7 +3197,11 @@ msgstr "" msgid "Are you sure you want to delete this stock location?" msgstr "" -#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:934 +#: stock/templates/stock/stock_adjust.html:35 +msgid "Stock item is serialized and quantity cannot be adjusted" +msgstr "" + +#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1052 msgid "Convert Stock Item" msgstr "" @@ -3194,190 +3229,214 @@ msgstr "" msgid "Children" msgstr "" -#: stock/views.py:113 +#: stock/views.py:114 msgid "Edit Stock Location" msgstr "" -#: stock/views.py:137 +#: stock/views.py:138 msgid "Stock Location QR code" msgstr "" -#: stock/views.py:155 +#: stock/views.py:156 msgid "Add Stock Item Attachment" msgstr "" -#: stock/views.py:200 +#: stock/views.py:201 msgid "Edit Stock Item Attachment" msgstr "" -#: stock/views.py:216 +#: stock/views.py:217 msgid "Delete Stock Item Attachment" msgstr "" -#: stock/views.py:232 +#: stock/views.py:233 msgid "Assign to Customer" msgstr "" #: stock/views.py:270 -msgid "Delete All Test Data" +msgid "Return to Stock" msgstr "" -#: stock/views.py:285 -msgid "Confirm test data deletion" +#: stock/views.py:289 +msgid "Specify a valid location" +msgstr "" + +#: stock/views.py:293 +msgid "Stock item returned from customer" msgstr "" #: stock/views.py:305 +msgid "Select Label Template" +msgstr "" + +#: stock/views.py:326 +msgid "Select valid label" +msgstr "" + +#: stock/views.py:388 +msgid "Delete All Test Data" +msgstr "" + +#: stock/views.py:403 +msgid "Confirm test data deletion" +msgstr "" + +#: stock/views.py:423 msgid "Add Test Result" msgstr "" -#: stock/views.py:342 +#: stock/views.py:460 msgid "Edit Test Result" msgstr "" -#: stock/views.py:359 +#: stock/views.py:477 msgid "Delete Test Result" msgstr "" -#: stock/views.py:370 +#: stock/views.py:488 msgid "Select Test Report Template" msgstr "" -#: stock/views.py:384 +#: stock/views.py:502 msgid "Select valid template" msgstr "" -#: stock/views.py:436 +#: stock/views.py:554 msgid "Stock Export Options" msgstr "" -#: stock/views.py:556 +#: stock/views.py:674 msgid "Stock Item QR Code" msgstr "" -#: stock/views.py:579 +#: stock/views.py:697 msgid "Adjust Stock" msgstr "" -#: stock/views.py:688 +#: stock/views.py:806 msgid "Move Stock Items" msgstr "" -#: stock/views.py:689 +#: stock/views.py:807 msgid "Count Stock Items" msgstr "" -#: stock/views.py:690 +#: stock/views.py:808 msgid "Remove From Stock" msgstr "" -#: stock/views.py:691 +#: stock/views.py:809 msgid "Add Stock Items" msgstr "" -#: stock/views.py:692 +#: stock/views.py:810 msgid "Delete Stock Items" msgstr "" -#: stock/views.py:720 +#: stock/views.py:838 msgid "Must enter integer value" msgstr "" -#: stock/views.py:725 +#: stock/views.py:843 msgid "Quantity must be positive" msgstr "" -#: stock/views.py:732 +#: stock/views.py:850 #, python-brace-format msgid "Quantity must not exceed {x}" msgstr "" -#: stock/views.py:740 +#: stock/views.py:858 msgid "Confirm stock adjustment" msgstr "" -#: stock/views.py:811 +#: stock/views.py:929 #, python-brace-format msgid "Added stock to {n} items" msgstr "" -#: stock/views.py:826 +#: stock/views.py:944 #, python-brace-format msgid "Removed stock from {n} items" msgstr "" -#: stock/views.py:839 +#: stock/views.py:957 #, python-brace-format msgid "Counted stock for {n} items" msgstr "" -#: stock/views.py:867 +#: stock/views.py:985 msgid "No items were moved" msgstr "" -#: stock/views.py:870 +#: stock/views.py:988 #, python-brace-format msgid "Moved {n} items to {dest}" msgstr "" -#: stock/views.py:889 +#: stock/views.py:1007 #, python-brace-format msgid "Deleted {n} stock items" msgstr "" -#: stock/views.py:901 +#: stock/views.py:1019 msgid "Edit Stock Item" msgstr "" -#: stock/views.py:961 +#: stock/views.py:1079 msgid "Create new Stock Location" msgstr "" -#: stock/views.py:982 +#: stock/views.py:1100 msgid "Serialize Stock" msgstr "" -#: stock/views.py:1074 +#: stock/views.py:1192 msgid "Create new Stock Item" msgstr "" -#: stock/views.py:1167 +#: stock/views.py:1285 msgid "Duplicate Stock Item" msgstr "" -#: stock/views.py:1240 +#: stock/views.py:1358 msgid "Invalid quantity" msgstr "" -#: stock/views.py:1247 +#: stock/views.py:1361 +msgid "Quantity cannot be less than zero" +msgstr "" + +#: stock/views.py:1365 msgid "Invalid part selection" msgstr "" -#: stock/views.py:1296 +#: stock/views.py:1414 #, python-brace-format msgid "Created {n} new stock items" msgstr "" -#: stock/views.py:1315 stock/views.py:1331 +#: stock/views.py:1433 stock/views.py:1449 msgid "Created new stock item" msgstr "" -#: stock/views.py:1350 +#: stock/views.py:1468 msgid "Delete Stock Location" msgstr "" -#: stock/views.py:1363 +#: stock/views.py:1481 msgid "Delete Stock Item" msgstr "" -#: stock/views.py:1374 +#: stock/views.py:1492 msgid "Delete Stock Tracking Entry" msgstr "" -#: stock/views.py:1391 +#: stock/views.py:1509 msgid "Edit Stock Tracking Entry" msgstr "" -#: stock/views.py:1400 +#: stock/views.py:1518 msgid "Add Stock Tracking Entry" msgstr "" @@ -3602,7 +3661,7 @@ msgstr "" msgid "No purchase orders found" msgstr "" -#: templates/js/order.html:170 templates/js/stock.html:612 +#: templates/js/order.html:170 templates/js/stock.html:625 msgid "Date" msgstr "" @@ -3614,7 +3673,7 @@ msgstr "" msgid "Shipment Date" msgstr "" -#: templates/js/part.html:106 templates/js/stock.html:405 +#: templates/js/part.html:106 templates/js/stock.html:406 msgid "Select" msgstr "" @@ -3630,7 +3689,7 @@ msgstr "" msgid "No category" msgstr "" -#: templates/js/part.html:214 templates/js/table_filters.html:157 +#: templates/js/part.html:214 templates/js/table_filters.html:167 msgid "Low stock" msgstr "" @@ -3654,11 +3713,11 @@ msgstr "" msgid "No test templates matching query" msgstr "" -#: templates/js/part.html:387 templates/js/stock.html:62 +#: templates/js/part.html:387 templates/js/stock.html:63 msgid "Edit test result" msgstr "" -#: templates/js/part.html:388 templates/js/stock.html:63 +#: templates/js/part.html:388 templates/js/stock.html:64 msgid "Delete test result" msgstr "" @@ -3666,143 +3725,175 @@ msgstr "" msgid "This test is defined for a parent part" msgstr "" -#: templates/js/stock.html:25 +#: templates/js/stock.html:26 msgid "PASS" msgstr "" -#: templates/js/stock.html:27 +#: templates/js/stock.html:28 msgid "FAIL" msgstr "" -#: templates/js/stock.html:32 +#: templates/js/stock.html:33 msgid "NO RESULT" msgstr "" -#: templates/js/stock.html:58 +#: templates/js/stock.html:59 msgid "Add test result" msgstr "" -#: templates/js/stock.html:76 +#: templates/js/stock.html:77 msgid "No test results found" msgstr "" -#: templates/js/stock.html:117 +#: templates/js/stock.html:118 msgid "Test Date" msgstr "" -#: templates/js/stock.html:260 +#: templates/js/stock.html:261 msgid "No stock items matching query" msgstr "" -#: templates/js/stock.html:357 templates/js/stock.html:372 +#: templates/js/stock.html:358 templates/js/stock.html:373 msgid "Undefined location" msgstr "" -#: templates/js/stock.html:469 -msgid "StockItem has been allocated" +#: templates/js/stock.html:464 +msgid "Stock item has been allocated" msgstr "" -#: templates/js/stock.html:474 -msgid "StockItem is lost" +#: templates/js/stock.html:468 +msgid "Stock item has been assigned to customer" msgstr "" -#: templates/js/stock.html:503 +#: templates/js/stock.html:470 +msgid "Stock item was assigned to a build order" +msgstr "" + +#: templates/js/stock.html:472 +msgid "Stock item was assigned to a sales order" +msgstr "" + +#: templates/js/stock.html:479 +msgid "Stock item has been rejected" +msgstr "" + +#: templates/js/stock.html:483 +msgid "Stock item is lost" +msgstr "" + +#: templates/js/stock.html:487 templates/js/table_filters.html:52 +msgid "Depleted" +msgstr "" + +#: templates/js/stock.html:516 msgid "Shipped to customer" msgstr "" -#: templates/js/stock.html:506 +#: templates/js/stock.html:519 msgid "No stock location set" msgstr "" -#: templates/js/stock.html:678 +#: templates/js/stock.html:691 msgid "No user information" msgstr "" -#: templates/js/table_filters.html:19 templates/js/table_filters.html:62 +#: templates/js/table_filters.html:19 templates/js/table_filters.html:67 msgid "Is Serialized" msgstr "" -#: templates/js/table_filters.html:22 templates/js/table_filters.html:65 +#: templates/js/table_filters.html:22 templates/js/table_filters.html:70 msgid "Serial number GTE" msgstr "" -#: templates/js/table_filters.html:23 templates/js/table_filters.html:66 +#: templates/js/table_filters.html:23 templates/js/table_filters.html:71 msgid "Serial number greater than or equal to" msgstr "" -#: templates/js/table_filters.html:26 templates/js/table_filters.html:69 +#: templates/js/table_filters.html:26 templates/js/table_filters.html:74 msgid "Serial number LTE" msgstr "" -#: templates/js/table_filters.html:27 templates/js/table_filters.html:70 +#: templates/js/table_filters.html:27 templates/js/table_filters.html:75 msgid "Serial number less than or equal to" msgstr "" -#: templates/js/table_filters.html:38 -msgid "Show items which are in stock" -msgstr "" - -#: templates/js/table_filters.html:42 -msgid "Include sublocations" -msgstr "" - -#: templates/js/table_filters.html:43 -msgid "Include stock in sublocations" -msgstr "" - -#: templates/js/table_filters.html:47 +#: templates/js/table_filters.html:37 msgid "Active parts" msgstr "" -#: templates/js/table_filters.html:48 +#: templates/js/table_filters.html:38 msgid "Show stock for active parts" msgstr "" -#: templates/js/table_filters.html:52 templates/js/table_filters.html:53 -msgid "Stock status" -msgstr "" - -#: templates/js/table_filters.html:57 +#: templates/js/table_filters.html:42 msgid "Is allocated" msgstr "" -#: templates/js/table_filters.html:58 +#: templates/js/table_filters.html:43 msgid "Item has been alloacted" msgstr "" -#: templates/js/table_filters.html:99 +#: templates/js/table_filters.html:47 +msgid "Include sublocations" +msgstr "" + +#: templates/js/table_filters.html:48 +msgid "Include stock in sublocations" +msgstr "" + +#: templates/js/table_filters.html:53 +msgid "Show stock items which are depleted" +msgstr "" + +#: templates/js/table_filters.html:58 +msgid "Show items which are in stock" +msgstr "" + +#: templates/js/table_filters.html:62 +msgid "Sent to customer" +msgstr "" + +#: templates/js/table_filters.html:63 +msgid "Show items which have been assigned to a customer" +msgstr "" + +#: templates/js/table_filters.html:79 templates/js/table_filters.html:80 +msgid "Stock status" +msgstr "" + +#: templates/js/table_filters.html:109 msgid "Build status" msgstr "" -#: templates/js/table_filters.html:111 templates/js/table_filters.html:124 +#: templates/js/table_filters.html:121 templates/js/table_filters.html:134 msgid "Order status" msgstr "" -#: templates/js/table_filters.html:116 templates/js/table_filters.html:129 +#: templates/js/table_filters.html:126 templates/js/table_filters.html:139 msgid "Outstanding" msgstr "" -#: templates/js/table_filters.html:139 +#: templates/js/table_filters.html:149 msgid "Include subcategories" msgstr "" -#: templates/js/table_filters.html:140 +#: templates/js/table_filters.html:150 msgid "Include parts in subcategories" msgstr "" -#: templates/js/table_filters.html:145 +#: templates/js/table_filters.html:155 msgid "Show active parts" msgstr "" -#: templates/js/table_filters.html:153 +#: templates/js/table_filters.html:163 msgid "Stock available" msgstr "" -#: templates/js/table_filters.html:169 +#: templates/js/table_filters.html:179 msgid "Starred" msgstr "" -#: templates/js/table_filters.html:181 +#: templates/js/table_filters.html:191 msgid "Purchasable" msgstr "" From 2bbc65cc5933ea4b3e937ffbd25644dacd4e8118 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 13:29:38 +1000 Subject: [PATCH 20/27] Add "brief" version of QR codes - Use this to render to labels (as it contains much less information) --- InvenTree/InvenTree/helpers.py | 19 ++++++++----- .../barcode/plugins/inventree_barcode.py | 28 +++++++++++++------ InvenTree/label/models.py | 2 +- InvenTree/stock/models.py | 13 +++------ 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index bf7e9083b5..fb54bc4c6f 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -242,7 +242,7 @@ def WrapWithQuotes(text, quote='"'): return text -def MakeBarcode(object_name, object_data): +def MakeBarcode(object_name, object_pk, object_data, **kwargs): """ Generate a string for a barcode. Adds some global InvenTree parameters. Args: @@ -255,12 +255,17 @@ def MakeBarcode(object_name, object_data): json string of the supplied data plus some other data """ - data = { - 'tool': 'InvenTree', - 'version': inventreeVersion(), - 'instance': inventreeInstanceName(), - object_name: object_data - } + brief = kwargs.get('brief', False) + + data = {} + + if brief: + data[object_name] = object_pk + else: + data['tool'] = 'InvenTree' + data['version'] = inventreeVersion() + data['instance'] = inventreeInstanceName() + data[object_name] = object_data return json.dumps(data, sort_keys=True) diff --git a/InvenTree/barcode/plugins/inventree_barcode.py b/InvenTree/barcode/plugins/inventree_barcode.py index 821cdc9c88..780b7e888a 100644 --- a/InvenTree/barcode/plugins/inventree_barcode.py +++ b/InvenTree/barcode/plugins/inventree_barcode.py @@ -47,12 +47,12 @@ class InvenTreeBarcodePlugin(BarcodePlugin): else: return False - for key in ['tool', 'version']: - if key not in self.data.keys(): - return False + # If any of the following keys are in the JSON data, + # let's go ahead and assume that the code is a valid InvenTree one... - if not self.data['tool'] == 'InvenTree': - return False + for key in ['tool', 'version', 'InvenTree', 'stockitem', 'location', 'part']: + if key in self.data.keys(): + return True return True @@ -60,10 +60,22 @@ class InvenTreeBarcodePlugin(BarcodePlugin): for k in self.data.keys(): if k.lower() == 'stockitem': + + data = self.data[k] + + pk = None + + # Initially try casting to an integer try: - pk = self.data[k]['id'] - except (AttributeError, KeyError): - raise ValidationError({k: "id parameter not supplied"}) + pk = int(data) + except (ValueError): + pk = None + + if pk is None: + try: + pk = self.data[k]['id'] + except (AttributeError, KeyError): + raise ValidationError({k: "id parameter not supplied"}) try: item = StockItem.objects.get(pk=pk) diff --git a/InvenTree/label/models.py b/InvenTree/label/models.py index 6faf966d7e..7d481327a9 100644 --- a/InvenTree/label/models.py +++ b/InvenTree/label/models.py @@ -142,7 +142,7 @@ class StockItemLabel(LabelTemplate): 'serial': item.serial, 'uid': item.uid, 'pk': item.pk, - 'qr_data': item.format_short_barcode(), + 'qr_data': item.format_barcode(brief=True), 'tests': item.testResultMap() }) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 664593e600..e523ed70d3 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -283,7 +283,7 @@ class StockItem(MPTTModel): def get_part_name(self): return self.part.full_name - def format_barcode(self): + def format_barcode(self, **kwargs): """ Return a JSON string for formatting a barcode for this StockItem. Can be used to perform lookup of a stockitem using barcode @@ -296,19 +296,14 @@ class StockItem(MPTTModel): return helpers.MakeBarcode( "stockitem", + self.id, { "id": self.id, "url": reverse('api-stock-detail', kwargs={'pk': self.id}), - } + }, + **kwargs ) - def format_short_barcode(self): - """ - Return a short barcode - """ - - return "stockid={pk}".format(pk=self.pk) - uid = models.CharField(blank=True, max_length=128, help_text=("Unique identifier field")) parent = TreeForeignKey( From a4267f76e86432c3a99ee9fc299797254d56e3fe Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 13:39:05 +1000 Subject: [PATCH 21/27] Fixes for barcode data rendering --- InvenTree/InvenTree/helpers.py | 3 +++ InvenTree/InvenTree/tests.py | 1 + InvenTree/part/models.py | 7 ++++--- InvenTree/stock/models.py | 8 ++++---- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index fb54bc4c6f..28cebbcd3d 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -265,6 +265,9 @@ def MakeBarcode(object_name, object_pk, object_data, **kwargs): data['tool'] = 'InvenTree' data['version'] = inventreeVersion() data['instance'] = inventreeInstanceName() + + # Ensure PK is included + object_data['id'] = object_pk data[object_name] = object_data return json.dumps(data, sort_keys=True) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index 877adab919..c46a059c8d 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -138,6 +138,7 @@ class TestMakeBarcode(TestCase): bc = helpers.MakeBarcode( "part", + 3, { "id": 3, "url": "www.google.com", diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 308808fbdd..c074d197b9 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -560,16 +560,17 @@ class Part(MPTTModel): responsible = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True, related_name='parts_responible') - def format_barcode(self): + def format_barcode(self, **kwargs): """ Return a JSON string for formatting a barcode for this Part object """ return helpers.MakeBarcode( "part", + self.id, { - "id": self.id, "name": self.full_name, "url": reverse('api-part-detail', kwargs={'pk': self.id}), - } + }, + **kwargs ) @property diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index e523ed70d3..788bf845df 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -45,16 +45,17 @@ class StockLocation(InvenTreeTree): def get_absolute_url(self): return reverse('stock-location-detail', kwargs={'pk': self.id}) - def format_barcode(self): + def format_barcode(self, **kwargs): """ Return a JSON string for formatting a barcode for this StockLocation object """ return helpers.MakeBarcode( 'stocklocation', + self.pk, { - "id": self.id, "name": self.name, "url": reverse('api-location-detail', kwargs={'pk': self.id}), - } + }, + **kwargs ) def get_stock_items(self, cascade=True): @@ -298,7 +299,6 @@ class StockItem(MPTTModel): "stockitem", self.id, { - "id": self.id, "url": reverse('api-stock-detail', kwargs={'pk': self.id}), }, **kwargs From ccda637e3c67f462b35f9efee3b5813f90dc17e0 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 13:42:27 +1000 Subject: [PATCH 22/27] Fixes for barcode decoding --- .../barcode/plugins/inventree_barcode.py | 35 +++++++++++++++---- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/InvenTree/barcode/plugins/inventree_barcode.py b/InvenTree/barcode/plugins/inventree_barcode.py index 780b7e888a..6e4e6937a0 100644 --- a/InvenTree/barcode/plugins/inventree_barcode.py +++ b/InvenTree/barcode/plugins/inventree_barcode.py @@ -68,7 +68,7 @@ class InvenTreeBarcodePlugin(BarcodePlugin): # Initially try casting to an integer try: pk = int(data) - except (ValueError): + except (TypeError, ValueError): pk = None if pk is None: @@ -89,10 +89,21 @@ class InvenTreeBarcodePlugin(BarcodePlugin): for k in self.data.keys(): if k.lower() == 'stocklocation': + + pk = None + + # First try simple integer lookup try: - pk = self.data[k]['id'] - except (AttributeError, KeyError): - raise ValidationError({k: "id parameter not supplied"}) + pk = int(self.data[k]) + except (TypeError, ValueError): + pk = None + + if pk is None: + # Lookup by 'id' field + try: + pk = self.data[k]['id'] + except (AttributeError, KeyError): + raise ValidationError({k: "id parameter not supplied"}) try: loc = StockLocation.objects.get(pk=pk) @@ -106,10 +117,20 @@ class InvenTreeBarcodePlugin(BarcodePlugin): for k in self.data.keys(): if k.lower() == 'part': + + pk = None + + # Try integer lookup first try: - pk = self.data[k]['id'] - except (AttributeError, KeyError): - raise ValidationError({k, 'id parameter not supplied'}) + pk = int(self.data[k]) + except (TypeError, ValueError): + pk = None + + if pk is None: + try: + pk = self.data[k]['id'] + except (AttributeError, KeyError): + raise ValidationError({k, 'id parameter not supplied'}) try: part = Part.objects.get(pk=pk) From da079b23dcba7d4ae8414d22a5413595aa8dd14d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 16 Aug 2020 13:43:59 +1000 Subject: [PATCH 23/27] Fix duplicate label printing menu action --- InvenTree/stock/templates/stock/item_base.html | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index a7bc263bbf..e65f8e0b33 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 }}