diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 42f4ba4660..107cc7ff16 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -22,7 +22,28 @@ from django.utils.translation import gettext_lazy as _ def _is_true(x): - return x in [True, "True", "true", "Y", "y", "1"] + # Shortcut function to determine if a value "looks" like a boolean + return str(x).lower() in ['1', 'y', 'yes', 't', 'true'] + + +def get_setting(environment_var, backup_val, default_value=None): + """ + Helper function for retrieving a configuration setting value + + - First preference is to look for the environment variable + - Second preference is to look for the value of the settings file + - Third preference is the default value + """ + + val = os.getenv(environment_var) + + if val is not None: + return val + + if backup_val is not None: + return backup_val + + return default_value # Build paths inside the project like this: os.path.join(BASE_DIR, ...) @@ -39,10 +60,17 @@ with open(cfg_filename, 'r') as cfg: # Default action is to run the system in Debug mode # SECURITY WARNING: don't run with debug turned on in production! -DEBUG = _is_true(os.getenv("INVENTREE_DEBUG", CONFIG.get("debug", True))) +DEBUG = _is_true(get_setting( + 'INVENTREE_DEBUG', + CONFIG.get('debug', True) +)) # Configure logging settings -log_level = CONFIG.get('log_level', 'DEBUG').upper() +log_level = get_setting( + 'INVENTREE_LOG_LEVEL', + CONFIG.get('log_level', 'DEBUG') +) + logging.basicConfig( level=log_level, format="%(asctime)s %(levelname)s %(message)s", @@ -75,6 +103,7 @@ if os.getenv("INVENTREE_SECRET_KEY"): else: # Secret key passed in by file location key_file = os.getenv("INVENTREE_SECRET_KEY_FILE") + if key_file: if os.path.isfile(key_file): logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY_FILE") @@ -112,7 +141,12 @@ if cors_opt: STATIC_URL = '/static/' # The filesystem location for served static files -STATIC_ROOT = os.path.abspath(CONFIG.get('static_root', os.path.join(BASE_DIR, 'static'))) +STATIC_ROOT = os.path.abspath( + get_setting( + 'INVENTREE_STATIC_ROOT', + CONFIG.get('static_root', os.path.join(BASE_DIR, 'static')) + ) +) STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'InvenTree', 'static'), @@ -125,7 +159,12 @@ STATIC_COLOR_THEMES_DIR = os.path.join(STATIC_ROOT, 'css', 'color-themes') MEDIA_URL = '/media/' # The filesystem location for served static files -MEDIA_ROOT = os.path.abspath(CONFIG.get('media_root', os.path.join(BASE_DIR, 'media'))) +MEDIA_ROOT = os.path.abspath( + get_setting( + 'INVENTREE_MEDIA_ROOT', + CONFIG.get('media_root', os.path.join(BASE_DIR, 'media')) + ) +) if DEBUG: logger.info("InvenTree running in DEBUG mode") @@ -133,30 +172,6 @@ if DEBUG: logger.info(f"MEDIA_ROOT: '{MEDIA_ROOT}'") logger.info(f"STATIC_ROOT: '{STATIC_ROOT}'") -# Does the user wish to use the sentry.io integration? -sentry_opts = CONFIG.get('sentry', {}) - -if sentry_opts.get('enabled', False): - - logger.info("Configuring sentry.io integration") - - dsn = sentry_opts.get('dsn', None) - - if dsn is not None: - # Try to import required modules (exit if not installed) - try: - import sentry_sdk - from sentry_sdk.integrations.django import DjangoIntegration - - sentry_sdk.init(dsn=dsn, integrations=[DjangoIntegration()], send_default_pii=True) - - except ModuleNotFoundError: - logger.error("sentry_sdk module not found. Install using 'pip install sentry-sdk'") - sys.exit(-1) - - else: - logger.warning("Sentry.io DSN not specified in config file") - # Application definition INSTALLED_APPS = [ @@ -430,16 +445,17 @@ if not type(EXTRA_URL_SCHEMES) in [list]: EXTRA_URL_SCHEMES = [] # Internationalization -# https://docs.djangoproject.com/en/1.10/topics/i18n/ +# https://docs.djangoproject.com/en/dev/topics/i18n/ LANGUAGE_CODE = CONFIG.get('language', 'en-us') # If a new language translation is supported, it must be added here LANGUAGES = [ ('en', _('English')), - ('de', _('German')), ('fr', _('French')), + ('de', _('German')), ('pk', _('Polish')), + ('tr', _('Turkish')), ] # Currencies available for use @@ -491,10 +507,15 @@ CRISPY_TEMPLATE_PACK = 'bootstrap3' # Use database transactions when importing / exporting data IMPORT_EXPORT_USE_TRANSACTIONS = True +BACKUP_DIR = get_setting( + 'INVENTREE_BACKUP_DIR', + CONFIG.get('backup_dir', tempfile.gettempdir()), +) + # Settings for dbbsettings app DBBACKUP_STORAGE = 'django.core.files.storage.FileSystemStorage' DBBACKUP_STORAGE_OPTIONS = { - 'location': CONFIG.get('backup_dir', tempfile.gettempdir()), + 'location': BACKUP_DIR, } # Internal IP addresses allowed to see the debug toolbar diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 4a6bde8bfe..0fd924a77a 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -196,6 +196,13 @@ class InvenTreeSetting(models.Model): 'validator': bool, }, + 'STOCK_OWNERSHIP_CONTROL': { + 'name': _('Stock Ownership Control'), + 'description': _('Enable ownership control over stock locations and items'), + 'default': False, + 'validator': bool, + }, + 'BUILDORDER_REFERENCE_PREFIX': { 'name': _('Build Order Reference Prefix'), 'description': _('Prefix value for build order reference'), diff --git a/InvenTree/config_template.yaml b/InvenTree/config_template.yaml index bb1e20fceb..c655ffdc5c 100644 --- a/InvenTree/config_template.yaml +++ b/InvenTree/config_template.yaml @@ -107,13 +107,6 @@ static_root: '../inventree_static' # If unspecified, the local user's temp directory will be used #backup_dir: '/home/inventree/backup/' -# Sentry.io integration -# If you have a sentry.io account, it can be used to log server errors -# Ensure sentry_sdk is installed by running 'pip install sentry-sdk' -sentry: - enabled: False - # dsn: add-your-sentry-dsn-here - # LaTeX report rendering # InvenTree uses the django-tex plugin to enable LaTeX report rendering # Ref: https://pypi.org/project/django-tex/ diff --git a/InvenTree/locale/de/LC_MESSAGES/django.po b/InvenTree/locale/de/LC_MESSAGES/django.po index 76931db8fa..aa858ebd81 100644 --- a/InvenTree/locale/de/LC_MESSAGES/django.po +++ b/InvenTree/locale/de/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2021-01-14 23:57+1100\n" +"POT-Creation-Date: 2021-01-17 18:11+0000\n" "PO-Revision-Date: 2020-05-03 11:32+0200\n" "Last-Translator: Christian Schlüter \n" "Language-Team: C \n" @@ -62,7 +62,7 @@ msgid "Select Category" msgstr "Teilkategorie auswählen" #: InvenTree/helpers.py:361 order/models.py:232 order/models.py:330 -#: stock/views.py:1573 +#: stock/views.py:1865 msgid "Invalid quantity provided" msgstr "Keine gültige Menge" @@ -105,7 +105,7 @@ msgstr "Datei zum Anhängen auswählen" msgid "File comment" msgstr "Datei-Kommentar" -#: InvenTree/models.py:68 templates/js/stock.js:873 +#: InvenTree/models.py:68 templates/js/stock.js:878 msgid "User" msgstr "Benutzer" @@ -338,7 +338,7 @@ msgstr "" #: build/forms.py:78 build/templates/build/auto_allocate.html:17 #: build/templates/build/build_base.html:83 -#: build/templates/build/detail.html:29 common/models.py:596 +#: build/templates/build/detail.html:29 common/models.py:603 #: company/forms.py:112 company/templates/company/supplier_part_pricing.html:75 #: order/templates/order/order_wizard/select_parts.html:32 #: order/templates/order/purchase_order_detail.html:179 @@ -346,13 +346,13 @@ msgstr "" #: order/templates/order/sales_order_detail.html:156 #: part/templates/part/allocation.html:16 #: part/templates/part/allocation.html:49 -#: part/templates/part/sale_prices.html:82 stock/forms.py:304 -#: stock/templates/stock/item_base.html:40 -#: stock/templates/stock/item_base.html:46 -#: stock/templates/stock/item_base.html:214 +#: part/templates/part/sale_prices.html:82 stock/forms.py:306 +#: stock/templates/stock/item_base.html:51 +#: stock/templates/stock/item_base.html:57 +#: stock/templates/stock/item_base.html:234 #: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.js:338 -#: templates/js/bom.js:195 templates/js/build.js:420 templates/js/stock.js:864 -#: templates/js/stock.js:1103 +#: templates/js/bom.js:195 templates/js/build.js:420 templates/js/stock.js:869 +#: templates/js/stock.js:1108 msgid "Quantity" msgstr "Anzahl" @@ -362,7 +362,7 @@ msgstr "Anzahl" msgid "Enter quantity for build output" msgstr "Seriennummer für dieses Teil" -#: build/forms.py:83 stock/forms.py:116 +#: build/forms.py:83 stock/forms.py:117 #, fuzzy #| msgid "Serial Number" msgid "Serial numbers" @@ -437,7 +437,7 @@ msgstr "Bauauftrag" #: build/models.py:62 build/templates/build/index.html:8 #: build/templates/build/index.html:15 order/templates/order/so_builds.html:11 #: order/templates/order/so_tabs.html:9 part/templates/part/tabs.html:31 -#: templates/InvenTree/settings/tabs.html:28 users/models.py:32 +#: templates/InvenTree/settings/tabs.html:28 users/models.py:36 msgid "Build Orders" msgstr "Bauaufträge" @@ -463,7 +463,7 @@ msgstr "Referenz" #: templates/js/bom.js:549 templates/js/build.js:664 templates/js/company.js:56 #: templates/js/order.js:180 templates/js/order.js:274 templates/js/part.js:188 #: templates/js/part.js:271 templates/js/part.js:391 templates/js/part.js:572 -#: templates/js/stock.js:511 templates/js/stock.js:845 +#: templates/js/stock.js:511 templates/js/stock.js:850 msgid "Description" msgstr "Beschreibung" @@ -493,7 +493,7 @@ msgstr "Bestellung, die diesem Bau zugwiesen ist" #: templates/js/barcode.js:336 templates/js/bom.js:153 templates/js/bom.js:534 #: templates/js/build.js:669 templates/js/company.js:138 #: templates/js/part.js:252 templates/js/part.js:357 templates/js/stock.js:485 -#: templates/js/stock.js:1175 +#: templates/js/stock.js:1180 msgid "Part" msgstr "Teil" @@ -561,7 +561,7 @@ msgstr "Bau-Status" msgid "Build status code" msgstr "Bau-Statuscode" -#: build/models.py:194 stock/models.py:412 +#: build/models.py:194 stock/models.py:418 msgid "Batch Code" msgstr "Losnummer" @@ -577,11 +577,11 @@ msgstr "" #: company/templates/company/supplier_part_base.html:68 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:80 part/templates/part/part_base.html:102 -#: stock/models.py:406 stock/templates/stock/item_base.html:297 +#: stock/models.py:412 stock/templates/stock/item_base.html:317 msgid "External Link" msgstr "Externer Link" -#: build/models.py:220 part/models.py:705 stock/models.py:408 +#: build/models.py:220 part/models.py:705 stock/models.py:414 msgid "Link to external URL" msgstr "Link zu einer externen URL" @@ -589,10 +589,10 @@ msgstr "Link zu einer externen URL" #: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:18 #: order/templates/order/purchase_order_detail.html:213 #: order/templates/order/so_tabs.html:23 part/models.py:831 -#: part/templates/part/tabs.html:73 stock/forms.py:313 stock/forms.py:345 -#: stock/forms.py:373 stock/models.py:478 stock/models.py:1544 +#: part/templates/part/tabs.html:73 stock/forms.py:315 stock/forms.py:347 +#: stock/forms.py:375 stock/models.py:484 stock/models.py:1554 #: stock/templates/stock/tabs.html:26 templates/js/barcode.js:391 -#: templates/js/bom.js:295 templates/js/stock.js:127 templates/js/stock.js:618 +#: templates/js/bom.js:295 templates/js/stock.js:127 templates/js/stock.js:623 msgid "Notes" msgstr "Notizen" @@ -754,8 +754,8 @@ msgid "" "The following stock items will be allocated to the specified build output" msgstr "Lagerobjekt dem Bau zuweisen" -#: build/templates/build/auto_allocate.html:18 stock/forms.py:343 -#: stock/templates/stock/item_base.html:244 +#: build/templates/build/auto_allocate.html:18 stock/forms.py:345 +#: stock/templates/stock/item_base.html:264 #: stock/templates/stock/stock_adjust.html:17 #: templates/InvenTree/search.html:183 templates/js/barcode.js:337 #: templates/js/build.js:434 templates/js/stock.js:597 @@ -791,8 +791,8 @@ msgstr "Dieser Bau ist Kind von Bau" #: order/templates/order/order_base.html:26 #: order/templates/order/sales_order_base.html:35 #: part/templates/part/category.html:13 part/templates/part/part_base.html:32 -#: stock/templates/stock/item_base.html:97 -#: stock/templates/stock/location.html:12 +#: stock/templates/stock/item_base.html:114 +#: stock/templates/stock/location.html:24 #, fuzzy #| msgid "Admin" msgid "Admin view" @@ -834,10 +834,10 @@ msgstr "Bau-Status" #: build/templates/build/build_base.html:88 #: build/templates/build/detail.html:57 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:343 templates/InvenTree/search.html:175 +#: stock/templates/stock/item_base.html:363 templates/InvenTree/search.html:175 #: templates/js/barcode.js:42 templates/js/build.js:697 #: templates/js/order.js:185 templates/js/order.js:279 -#: templates/js/stock.js:584 templates/js/stock.js:1111 +#: templates/js/stock.js:584 templates/js/stock.js:1116 msgid "Status" msgstr "Status" @@ -867,7 +867,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:238 templates/js/order.js:240 +#: stock/templates/stock/item_base.html:258 templates/js/order.js:240 msgid "Sales Order" msgstr "Bestellung" @@ -988,7 +988,7 @@ msgstr "Lagerobjekt" msgid "Stock can be taken from any available location." msgstr "Bestand kann jedem verfügbaren Lagerort entnommen werden." -#: build/templates/build/detail.html:44 stock/forms.py:371 +#: build/templates/build/detail.html:44 stock/forms.py:373 #, fuzzy #| msgid "Description" msgid "Destination" @@ -1001,8 +1001,8 @@ msgid "Destination location not specified" msgstr "Hat dieses Teil Tracking für einzelne Objekte?" #: build/templates/build/detail.html:68 -#: stock/templates/stock/item_base.html:262 templates/js/stock.js:592 -#: templates/js/stock.js:1118 templates/js/table_filters.js:80 +#: stock/templates/stock/item_base.html:282 templates/js/stock.js:592 +#: templates/js/stock.js:1123 templates/js/table_filters.js:80 #: templates/js/table_filters.js:161 msgid "Batch" msgstr "Los" @@ -1118,7 +1118,7 @@ msgstr "Lagerbestand dem Bau zuweisen" msgid "Create Build Output" msgstr "Bau-Ausgabe" -#: build/views.py:207 stock/models.py:887 stock/views.py:1594 +#: build/views.py:207 stock/models.py:897 stock/views.py:1886 #, fuzzy #| msgid "Serial numbers already exist: " msgid "Serial numbers already exist" @@ -1140,7 +1140,7 @@ msgstr "Bau entfernt" msgid "Confirm unallocation of build stock" msgstr "Zuweisungsaufhebung bestätigen" -#: build/views.py:303 build/views.py:388 stock/views.py:330 +#: build/views.py:303 build/views.py:388 stock/views.py:432 msgid "Check the confirmation box" msgstr "Bestätigungsbox bestätigen" @@ -1266,7 +1266,7 @@ msgid "Add Build Order Attachment" msgstr "Auftragsanhang hinzufügen" #: build/views.py:1060 order/views.py:113 order/views.py:166 part/views.py:170 -#: stock/views.py:179 +#: stock/views.py:281 msgid "Added attachment" msgstr "Anhang hinzugefügt" @@ -1282,7 +1282,7 @@ msgstr "Anhang aktualisiert" msgid "Delete Attachment" msgstr "Anhang löschen" -#: build/views.py:1123 order/views.py:242 order/views.py:257 stock/views.py:237 +#: build/views.py:1123 order/views.py:242 order/views.py:257 stock/views.py:339 msgid "Deleted attachment" msgstr "Anhang gelöscht" @@ -1378,7 +1378,7 @@ msgstr "Teilparametervorlage bearbeiten" msgid "Copy category parameter templates when creating a part" msgstr "" -#: common/models.py:115 part/templates/part/detail.html:155 stock/forms.py:255 +#: common/models.py:115 part/templates/part/detail.html:155 stock/forms.py:257 #: templates/js/table_filters.js:23 templates/js/table_filters.js:270 msgid "Template" msgstr "Vorlage" @@ -1503,93 +1503,101 @@ msgid "Allow building with expired stock" msgstr "" #: common/models.py:200 +msgid "Stock Ownership Control" +msgstr "" + +#: common/models.py:201 +msgid "Enable ownership control over stock locations and items" +msgstr "" + +#: common/models.py:207 #, fuzzy #| msgid "Order Reference" msgid "Build Order Reference Prefix" msgstr "Bestellreferenz" -#: common/models.py:201 +#: common/models.py:208 #, fuzzy #| msgid "Order reference" msgid "Prefix value for build order reference" msgstr "Bestell-Referenz" -#: common/models.py:206 +#: common/models.py:213 #, fuzzy #| msgid "Order Reference" msgid "Build Order Reference Regex" msgstr "Bestellreferenz" -#: common/models.py:207 +#: common/models.py:214 msgid "Regular expression pattern for matching build order reference" msgstr "" -#: common/models.py:211 +#: common/models.py:218 #, fuzzy #| msgid "Sales Order Reference" msgid "Sales Order Reference Prefix" msgstr "Bestellungsreferenz" -#: common/models.py:212 +#: common/models.py:219 #, fuzzy #| msgid "Order reference" msgid "Prefix value for sales order reference" msgstr "Bestell-Referenz" -#: common/models.py:217 +#: common/models.py:224 #, fuzzy #| msgid "Order reference" msgid "Purchase Order Reference Prefix" msgstr "Bestell-Referenz" -#: common/models.py:218 +#: common/models.py:225 #, fuzzy #| msgid "Order reference" msgid "Prefix value for purchase order reference" msgstr "Bestell-Referenz" -#: common/models.py:441 +#: common/models.py:448 msgid "Settings key (must be unique - case insensitive" msgstr "" "Einstellungs-Schlüssel (muss einzigartig sein, Groß-/ Kleinschreibung wird " "nicht beachtet)" -#: common/models.py:443 +#: common/models.py:450 msgid "Settings value" msgstr "Einstellungs-Wert" -#: common/models.py:500 +#: common/models.py:507 msgid "Value must be a boolean value" msgstr "" -#: common/models.py:510 +#: common/models.py:517 #, fuzzy #| msgid "Must enter integer value" msgid "Value must be an integer value" msgstr "Nur Ganzzahl eingeben" -#: common/models.py:524 +#: common/models.py:531 msgid "Key string must be unique" msgstr "Schlüsseltext muss eindeutig sein" -#: common/models.py:597 company/forms.py:113 +#: common/models.py:604 company/forms.py:113 #, fuzzy #| msgid "Price Breaks" msgid "Price break quantity" msgstr "Preisstaffelung" -#: common/models.py:605 company/templates/company/supplier_part_pricing.html:80 +#: common/models.py:612 company/templates/company/supplier_part_pricing.html:80 #: part/templates/part/sale_prices.html:87 templates/js/bom.js:246 msgid "Price" msgstr "Preis" -#: common/models.py:606 +#: common/models.py:613 #, fuzzy #| msgid "Enter a valid quantity" msgid "Unit price at specified quantity" msgstr "Bitte eine gültige Anzahl eingeben" -#: common/models.py:629 +#: common/models.py:636 #, fuzzy #| msgid "Default Location" msgid "Default" @@ -1710,8 +1718,8 @@ msgstr "Produziert diese Firma Teile?" msgid "Currency" msgstr "Währung bearbeiten" -#: company/models.py:313 stock/models.py:360 -#: stock/templates/stock/item_base.html:194 +#: company/models.py:313 stock/models.py:366 +#: stock/templates/stock/item_base.html:214 msgid "Base Part" msgstr "Basisteil" @@ -1724,7 +1732,7 @@ msgstr "Teil auswählen" #: company/templates/company/supplier_part_detail.html:21 #: order/templates/order/order_base.html:89 #: order/templates/order/order_wizard/select_pos.html:30 part/bom.py:170 -#: stock/templates/stock/item_base.html:304 templates/js/company.js:48 +#: stock/templates/stock/item_base.html:324 templates/js/company.js:48 #: templates/js/company.js:164 templates/js/order.js:167 msgid "Supplier" msgstr "Zulieferer" @@ -1828,8 +1836,8 @@ msgid "Uses default currency" msgstr "Währung entfernen" #: company/templates/company/detail.html:62 -#: order/templates/order/sales_order_base.html:89 stock/models.py:395 -#: stock/models.py:396 stock/templates/stock/item_base.html:221 +#: order/templates/order/sales_order_base.html:89 stock/models.py:401 +#: stock/models.py:402 stock/templates/stock/item_base.html:241 #: templates/js/company.js:40 templates/js/order.js:261 msgid "Customer" msgstr "Kunde" @@ -1845,13 +1853,13 @@ msgstr "Neues Zuliefererteil anlegen" #: company/templates/company/detail_part.html:18 #: order/templates/order/purchase_order_detail.html:68 -#: part/templates/part/supplier.html:14 templates/js/stock.js:995 +#: part/templates/part/supplier.html:14 templates/js/stock.js:1000 msgid "New Supplier Part" msgstr "Neues Zulieferer-Teil" #: company/templates/company/detail_part.html:23 #: part/templates/part/category.html:120 part/templates/part/supplier.html:17 -#: templates/stock_table.html:18 +#: templates/stock_table.html:27 msgid "Options" msgstr "Optionen" @@ -1873,7 +1881,7 @@ msgid "Delete Parts" msgstr "Teile löschen" #: company/templates/company/detail_part.html:63 -#: part/templates/part/category.html:116 templates/js/stock.js:989 +#: part/templates/part/category.html:116 templates/js/stock.js:994 msgid "New Part" msgstr "Neues Teil" @@ -1907,7 +1915,7 @@ msgstr "Zuliefererbestand" #: company/templates/company/supplier_part_stock.html:33 #: part/templates/part/bom.html:63 part/templates/part/category.html:112 #: part/templates/part/category.html:126 part/templates/part/stock.html:51 -#: templates/stock_table.html:7 +#: templates/stock_table.html:13 msgid "Export" msgstr "Exportieren" @@ -1931,7 +1939,7 @@ msgstr "" #: order/templates/order/purchase_orders.html:13 #: part/templates/part/orders.html:9 part/templates/part/tabs.html:48 #: templates/InvenTree/settings/tabs.html:31 templates/navbar.html:33 -#: users/models.py:33 +#: users/models.py:37 msgid "Purchase Orders" msgstr "Bestellungen" @@ -1951,7 +1959,7 @@ msgstr "Neue Bestellung" #: order/templates/order/sales_orders.html:13 #: part/templates/part/sales_orders.html:9 part/templates/part/tabs.html:56 #: templates/InvenTree/settings/tabs.html:34 templates/navbar.html:42 -#: users/models.py:34 +#: users/models.py:38 msgid "Sales Orders" msgstr "Bestellungen" @@ -1966,8 +1974,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:369 -#: stock/templates/stock/item_base.html:309 templates/js/company.js:180 +#: company/templates/company/supplier_part_base.html:19 stock/models.py:375 +#: stock/templates/stock/item_base.html:329 templates/js/company.js:180 msgid "Supplier Part" msgstr "Zulieferer-Teil" @@ -2008,7 +2016,7 @@ msgid "Pricing Information" msgstr "Preisinformationen ansehen" #: company/templates/company/supplier_part_pricing.html:17 company/views.py:486 -#: part/templates/part/sale_prices.html:14 part/views.py:2565 +#: part/templates/part/sale_prices.html:14 part/views.py:2567 msgid "Add Price Break" msgstr "Preisstaffel hinzufügen" @@ -2043,7 +2051,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:17 templates/InvenTree/search.html:155 +#: stock/templates/stock/location.html:29 templates/InvenTree/search.html:155 #: templates/InvenTree/settings/tabs.html:25 templates/js/part.js:192 #: templates/js/part.js:418 templates/js/stock.js:519 templates/navbar.html:22 msgid "Stock" @@ -2058,7 +2066,7 @@ msgstr "Bestellungen" #: part/templates/part/cat_link.html:7 part/templates/part/category.html:94 #: part/templates/part/category_tabs.html:6 #: templates/InvenTree/settings/tabs.html:22 templates/navbar.html:19 -#: templates/stats.html:35 templates/stats.html:44 users/models.py:29 +#: templates/stats.html:35 templates/stats.html:44 users/models.py:33 msgid "Parts" msgstr "Teile" @@ -2127,7 +2135,7 @@ msgstr "Firma gelöscht" msgid "Edit Supplier Part" msgstr "Zuliefererteil bearbeiten" -#: company/views.py:295 templates/js/stock.js:996 +#: company/views.py:295 templates/js/stock.js:1001 msgid "Create new Supplier Part" msgstr "Neues Zuliefererteil anlegen" @@ -2135,17 +2143,17 @@ msgstr "Neues Zuliefererteil anlegen" msgid "Delete Supplier Part" msgstr "Zuliefererteil entfernen" -#: company/views.py:492 part/views.py:2571 +#: company/views.py:492 part/views.py:2573 #, fuzzy #| msgid "Add Price Break" msgid "Added new price break" msgstr "Preisstaffel hinzufügen" -#: company/views.py:548 part/views.py:2615 +#: company/views.py:548 part/views.py:2617 msgid "Edit Price Break" msgstr "Preisstaffel bearbeiten" -#: company/views.py:564 part/views.py:2631 +#: company/views.py:564 part/views.py:2633 msgid "Delete Price Break" msgstr "Preisstaffel löschen" @@ -2175,7 +2183,7 @@ msgstr "Name des Teils" msgid "Label description" msgstr "Beschreibung des Teils" -#: label/models.py:83 stock/forms.py:198 +#: label/models.py:83 stock/forms.py:200 msgid "Label" msgstr "" @@ -2304,8 +2312,8 @@ msgstr "Erstelldatum" msgid "Date order was completed" msgstr "Bestellung als vollständig markieren" -#: order/models.py:230 order/models.py:328 part/views.py:1504 -#: stock/models.py:259 stock/models.py:871 +#: order/models.py:230 order/models.py:328 part/views.py:1506 +#: stock/models.py:265 stock/models.py:881 msgid "Quantity must be greater than zero" msgstr "Anzahl muss größer Null sein" @@ -2343,7 +2351,7 @@ msgstr "Position - Notizen" #: order/models.py:607 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:24 -#: stock/templates/stock/item_base.html:276 templates/js/order.js:145 +#: stock/templates/stock/item_base.html:296 templates/js/order.js:145 msgid "Purchase Order" msgstr "Kaufvertrag" @@ -2355,8 +2363,8 @@ msgstr "Zulieferer-Teil" msgid "Number of items received" msgstr "Empfangene Objekt-Anzahl" -#: order/models.py:630 stock/models.py:488 -#: stock/templates/stock/item_base.html:283 +#: order/models.py:630 stock/models.py:494 +#: stock/templates/stock/item_base.html:303 #, fuzzy #| msgid "Purchase Order" msgid "Purchase Price" @@ -2531,13 +2539,13 @@ msgstr "Bestellpositionen" #: order/templates/order/purchase_order_detail.html:39 #: order/templates/order/purchase_order_detail.html:119 #: part/templates/part/category.html:173 part/templates/part/category.html:215 -#: templates/js/stock.js:642 templates/js/stock.js:1001 +#: templates/js/stock.js:647 templates/js/stock.js:1006 msgid "New Location" msgstr "Neuer Standort" #: order/templates/order/purchase_order_detail.html:40 #: order/templates/order/purchase_order_detail.html:120 -#: stock/templates/stock/location.html:22 +#: stock/templates/stock/location.html:35 msgid "Create new stock location" msgstr "Neuen Lagerort anlegen" @@ -2620,8 +2628,8 @@ msgid "Sales Order Items" msgstr "Auftragspositionen" #: order/templates/order/sales_order_detail.html:72 -#: order/templates/order/sales_order_detail.html:154 stock/models.py:400 -#: stock/templates/stock/item_base.html:208 templates/js/build.js:418 +#: order/templates/order/sales_order_detail.html:154 stock/models.py:406 +#: stock/templates/stock/item_base.html:228 templates/js/build.js:418 msgid "Serial Number" msgstr "Seriennummer" @@ -2865,11 +2873,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:71 stock/forms.py:261 +#: part/forms.py:71 stock/forms.py:263 msgid "File Format" msgstr "Dateiformat" -#: part/forms.py:71 stock/forms.py:261 +#: part/forms.py:71 stock/forms.py:263 msgid "Select output file format" msgstr "Ausgabe-Dateiformat auswählen" @@ -3030,7 +3038,7 @@ msgstr "Teilkategorie" #: part/models.py:78 part/templates/part/category.html:18 #: part/templates/part/category.html:89 templates/stats.html:39 -#: users/models.py:28 +#: users/models.py:32 msgid "Part Categories" msgstr "Teile-Kategorien" @@ -3336,8 +3344,8 @@ msgstr "Notizen zum Stücklisten-Objekt" msgid "BOM line checksum" msgstr "Prüfsumme der Stückliste" -#: part/models.py:1963 part/views.py:1510 part/views.py:1562 -#: stock/models.py:249 +#: part/models.py:1963 part/views.py:1512 part/views.py:1564 +#: stock/models.py:255 #, fuzzy #| msgid "Overage must be an integer value or a percentage" msgid "Quantity must be integer value for trackable parts" @@ -3381,10 +3389,10 @@ msgstr "Bestellung" #: part/templates/part/allocation.html:28 #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 -#: stock/templates/stock/item_base.html:72 -#: stock/templates/stock/item_base.html:291 +#: stock/templates/stock/item_base.html:89 +#: stock/templates/stock/item_base.html:311 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.js:751 -#: templates/js/stock.js:834 templates/js/stock.js:1094 +#: templates/js/stock.js:839 templates/js/stock.js:1099 msgid "Stock Item" msgstr "Lagerobjekt" @@ -3457,7 +3465,7 @@ msgstr "Stückliste validieren" msgid "Validate" msgstr "BOM validieren" -#: part/templates/part/bom.html:62 part/views.py:1801 +#: part/templates/part/bom.html:62 part/views.py:1803 msgid "Export Bill of Materials" msgstr "Stückliste exportieren" @@ -3585,7 +3593,7 @@ msgstr "Neuen Bau beginnen" msgid "All parts" msgstr "Alle Teile" -#: part/templates/part/category.html:24 part/views.py:2192 +#: part/templates/part/category.html:24 part/views.py:2194 msgid "Create new part category" msgstr "Teilkategorie anlegen" @@ -3647,7 +3655,7 @@ msgstr "Teilkategorie auswählen" msgid "Export Data" msgstr "Exportieren" -#: part/templates/part/category.html:174 templates/js/stock.js:643 +#: part/templates/part/category.html:174 templates/js/stock.js:648 #, fuzzy #| msgid "Create New Location" msgid "Create new location" @@ -3671,7 +3679,7 @@ msgstr "Teilkategorie anlegen" msgid "Create new Part Category" msgstr "Teilkategorie anlegen" -#: part/templates/part/category.html:216 stock/views.py:1276 +#: part/templates/part/category.html:216 stock/views.py:1458 msgid "Create new Stock Location" msgstr "Neuen Lager-Standort erstellen" @@ -3817,13 +3825,13 @@ msgstr "Parameter hinzufügen" msgid "New Parameter" msgstr "Neuer Parameter" -#: part/templates/part/params.html:25 stock/models.py:1531 +#: part/templates/part/params.html:25 stock/models.py:1541 #: templates/InvenTree/settings/header.html:8 templates/js/stock.js:123 msgid "Value" msgstr "Wert" #: part/templates/part/params.html:41 part/templates/part/related.html:41 -#: part/templates/part/supplier.html:19 users/models.py:159 +#: part/templates/part/supplier.html:19 users/models.py:164 msgid "Delete" msgstr "Löschen" @@ -3855,24 +3863,24 @@ msgid "Star this part" msgstr "Teil favorisieren" #: part/templates/part/part_base.html:49 -#: stock/templates/stock/item_base.html:108 -#: stock/templates/stock/location.html:29 +#: stock/templates/stock/item_base.html:125 +#: stock/templates/stock/location.html:43 #, fuzzy #| msgid "Source Location" msgid "Barcode actions" msgstr "Quell-Standort" #: part/templates/part/part_base.html:51 -#: stock/templates/stock/item_base.html:110 -#: stock/templates/stock/location.html:31 +#: stock/templates/stock/item_base.html:127 +#: stock/templates/stock/location.html:45 #, fuzzy #| msgid "Part QR Code" msgid "Show QR Code" msgstr "Teil-QR-Code" #: part/templates/part/part_base.html:52 -#: stock/templates/stock/item_base.html:126 -#: stock/templates/stock/location.html:32 +#: stock/templates/stock/item_base.html:143 +#: stock/templates/stock/location.html:46 msgid "Print Label" msgstr "" @@ -4037,7 +4045,7 @@ msgstr "Stückliste" msgid "Used In" msgstr "Benutzt in" -#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:349 +#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:369 msgid "Tests" msgstr "" @@ -4089,7 +4097,7 @@ msgstr "Zuliefererteil entfernen" msgid "Add part attachment" msgstr "Teilanhang hinzufügen" -#: part/views.py:209 templates/attachment_table.html:34 +#: part/views.py:209 templates/attachment_table.html:32 msgid "Edit attachment" msgstr "Anhang bearbeiten" @@ -4144,11 +4152,11 @@ msgstr "Teil duplizieren" msgid "Copied part" msgstr "Teil kopiert" -#: part/views.py:529 part/views.py:667 +#: part/views.py:529 part/views.py:669 msgid "Possible matches exist - confirm creation of new part" msgstr "" -#: part/views.py:594 templates/js/stock.js:990 +#: part/views.py:594 templates/js/stock.js:995 msgid "Create New Part" msgstr "Neues Teil anlegen" @@ -4156,165 +4164,165 @@ msgstr "Neues Teil anlegen" msgid "Created new part" msgstr "Neues Teil angelegt" -#: part/views.py:836 +#: part/views.py:838 msgid "Part QR Code" msgstr "Teil-QR-Code" -#: part/views.py:855 +#: part/views.py:857 msgid "Upload Part Image" msgstr "Teilbild hochladen" -#: part/views.py:863 part/views.py:900 +#: part/views.py:865 part/views.py:902 msgid "Updated part image" msgstr "Teilbild aktualisiert" -#: part/views.py:872 +#: part/views.py:874 msgid "Select Part Image" msgstr "Teilbild auswählen" -#: part/views.py:903 +#: part/views.py:905 msgid "Part image not found" msgstr "Teilbild nicht gefunden" -#: part/views.py:914 +#: part/views.py:916 msgid "Edit Part Properties" msgstr "Teileigenschaften bearbeiten" -#: part/views.py:945 +#: part/views.py:947 #, fuzzy #| msgid "Duplicate Part" msgid "Duplicate BOM" msgstr "Teil duplizieren" -#: part/views.py:976 +#: part/views.py:978 #, fuzzy #| msgid "Confirm unallocation of build stock" msgid "Confirm duplication of BOM from parent" msgstr "Zuweisungsaufhebung bestätigen" -#: part/views.py:997 +#: part/views.py:999 msgid "Validate BOM" msgstr "BOM validieren" -#: part/views.py:1020 +#: part/views.py:1022 #, fuzzy #| msgid "Confirm that the BOM is correct" msgid "Confirm that the BOM is valid" msgstr "Bestätigen, dass die Stückliste korrekt ist" -#: part/views.py:1031 +#: part/views.py:1033 #, fuzzy #| msgid "Validate Bill of Materials" msgid "Validated Bill of Materials" msgstr "Stückliste validieren" -#: part/views.py:1165 +#: part/views.py:1167 msgid "No BOM file provided" msgstr "Keine Stückliste angegeben" -#: part/views.py:1513 +#: part/views.py:1515 msgid "Enter a valid quantity" msgstr "Bitte eine gültige Anzahl eingeben" -#: part/views.py:1538 part/views.py:1541 +#: part/views.py:1540 part/views.py:1543 msgid "Select valid part" msgstr "Bitte ein gültiges Teil auswählen" -#: part/views.py:1547 +#: part/views.py:1549 msgid "Duplicate part selected" msgstr "Teil doppelt ausgewählt" -#: part/views.py:1585 +#: part/views.py:1587 msgid "Select a part" msgstr "Teil auswählen" -#: part/views.py:1591 +#: part/views.py:1593 #, fuzzy #| msgid "Select part to be used in BOM" msgid "Selected part creates a circular BOM" msgstr "Teil für die Nutzung in der Stückliste auswählen" -#: part/views.py:1595 +#: part/views.py:1597 msgid "Specify quantity" msgstr "Anzahl angeben" -#: part/views.py:1851 +#: part/views.py:1853 msgid "Confirm Part Deletion" msgstr "Löschen des Teils bestätigen" -#: part/views.py:1860 +#: part/views.py:1862 msgid "Part was deleted" msgstr "Teil wurde gelöscht" -#: part/views.py:1869 +#: part/views.py:1871 msgid "Part Pricing" msgstr "Teilbepreisung" -#: part/views.py:1983 +#: part/views.py:1985 msgid "Create Part Parameter Template" msgstr "Teilparametervorlage anlegen" -#: part/views.py:1993 +#: part/views.py:1995 msgid "Edit Part Parameter Template" msgstr "Teilparametervorlage bearbeiten" -#: part/views.py:2002 +#: part/views.py:2004 msgid "Delete Part Parameter Template" msgstr "Teilparametervorlage löschen" -#: part/views.py:2012 +#: part/views.py:2014 msgid "Create Part Parameter" msgstr "Teilparameter anlegen" -#: part/views.py:2064 +#: part/views.py:2066 msgid "Edit Part Parameter" msgstr "Teilparameter bearbeiten" -#: part/views.py:2080 +#: part/views.py:2082 msgid "Delete Part Parameter" msgstr "Teilparameter löschen" -#: part/views.py:2139 +#: part/views.py:2141 msgid "Edit Part Category" msgstr "Teilkategorie bearbeiten" -#: part/views.py:2176 +#: part/views.py:2178 msgid "Delete Part Category" msgstr "Teilkategorie löschen" -#: part/views.py:2184 +#: part/views.py:2186 msgid "Part category was deleted" msgstr "Teilekategorie wurde gelöscht" -#: part/views.py:2240 +#: part/views.py:2242 #, fuzzy #| msgid "Create Part Parameter Template" msgid "Create Category Parameter Template" msgstr "Teilparametervorlage anlegen" -#: part/views.py:2343 +#: part/views.py:2345 #, fuzzy #| msgid "Edit Part Parameter Template" msgid "Edit Category Parameter Template" msgstr "Teilparametervorlage bearbeiten" -#: part/views.py:2401 +#: part/views.py:2403 #, fuzzy #| msgid "Delete Part Parameter Template" msgid "Delete Category Parameter Template" msgstr "Teilparametervorlage löschen" -#: part/views.py:2426 +#: part/views.py:2428 #, fuzzy #| msgid "Create BOM item" msgid "Create BOM Item" msgstr "BOM-Position anlegen" -#: part/views.py:2498 +#: part/views.py:2500 msgid "Edit BOM item" msgstr "BOM-Position beaarbeiten" -#: part/views.py:2555 +#: part/views.py:2557 msgid "Confim BOM item deletion" msgstr "Löschung von BOM-Position bestätigen" @@ -4354,364 +4362,364 @@ msgstr "" msgid "Asset file description" msgstr "Einstellungs-Beschreibung" -#: stock/forms.py:116 +#: stock/forms.py:117 msgid "Enter unique serial numbers (or leave blank)" msgstr "Eindeutige Seriennummern eingeben (oder leer lassen)" -#: stock/forms.py:199 stock/forms.py:255 +#: stock/forms.py:201 stock/forms.py:257 #, fuzzy #| msgid "Select stock item to allocate" msgid "Select test report template" msgstr "Lagerobjekt für Zuordnung auswählen" -#: stock/forms.py:263 +#: stock/forms.py:265 msgid "Include stock items in sub locations" msgstr "Lagerobjekte in untergeordneten Lagerorten einschließen" -#: stock/forms.py:298 +#: stock/forms.py:300 #, fuzzy #| msgid "No stock items matching query" msgid "Stock item to install" msgstr "Keine zur Anfrage passenden Lagerobjekte" -#: stock/forms.py:305 +#: stock/forms.py:307 #, fuzzy #| msgid "Stock Quantity" msgid "Stock quantity to assign" msgstr "Bestand" -#: stock/forms.py:333 +#: stock/forms.py:335 #, fuzzy #| msgid "Quantity must not exceed available stock quantity ({n})" msgid "Must not exceed available quantity" msgstr "Anzahl darf nicht die verfügbare Anzahl überschreiten ({n})" -#: stock/forms.py:343 +#: stock/forms.py:345 #, fuzzy #| msgid "Does this part have tracking for unique items?" msgid "Destination location for uninstalled items" msgstr "Hat dieses Teil Tracking für einzelne Objekte?" -#: stock/forms.py:345 +#: stock/forms.py:347 #, fuzzy #| msgid "Description of the company" msgid "Add transaction note (optional)" msgstr "Firmenbeschreibung" -#: stock/forms.py:347 +#: stock/forms.py:349 #, fuzzy #| msgid "Confirm stock allocation" msgid "Confirm uninstall" msgstr "Lagerbestandszuordnung bestätigen" -#: stock/forms.py:347 +#: stock/forms.py:349 #, fuzzy #| msgid "Confirm movement of stock items" msgid "Confirm removal of installed stock items" msgstr "Bewegung der Lagerobjekte bestätigen" -#: stock/forms.py:371 +#: stock/forms.py:373 msgid "Destination stock location" msgstr "Ziel-Lagerbestand" -#: stock/forms.py:373 +#: stock/forms.py:375 msgid "Add note (required)" msgstr "" -#: stock/forms.py:377 stock/views.py:848 stock/views.py:1046 +#: stock/forms.py:379 stock/views.py:950 stock/views.py:1148 msgid "Confirm stock adjustment" msgstr "Bestands-Anpassung bestätigen" -#: stock/forms.py:377 +#: stock/forms.py:379 msgid "Confirm movement of stock items" msgstr "Bewegung der Lagerobjekte bestätigen" -#: stock/forms.py:379 +#: stock/forms.py:381 #, fuzzy #| msgid "Default Location" msgid "Set Default Location" msgstr "Standard-Lagerort" -#: stock/forms.py:379 +#: stock/forms.py:381 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:194 +#: stock/models.py:200 #, fuzzy #| msgid "Created new stock item" msgid "Created stock item" msgstr "Neues Lagerobjekt erstellt" -#: stock/models.py:230 +#: stock/models.py:236 #, 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:266 +#: stock/models.py:272 #, python-brace-format msgid "Part type ('{pf}') must be {pe}" msgstr "Teile-Typ ('{pf}') muss {pe} sein" -#: stock/models.py:276 stock/models.py:285 +#: stock/models.py:282 stock/models.py:291 msgid "Quantity must be 1 for item with a serial number" msgstr "Anzahl muss für Objekte mit Seriennummer \"1\" sein" -#: stock/models.py:277 +#: stock/models.py:283 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:299 +#: stock/models.py:305 msgid "Item cannot belong to itself" msgstr "Teil kann nicht zu sich selbst gehören" -#: stock/models.py:305 +#: stock/models.py:311 msgid "Item must have a build reference if is_building=True" msgstr "" -#: stock/models.py:312 +#: stock/models.py:318 msgid "Build reference does not point to the same part object" msgstr "" -#: stock/models.py:352 +#: stock/models.py:358 msgid "Parent Stock Item" msgstr "Eltern-Lagerobjekt" -#: stock/models.py:361 +#: stock/models.py:367 msgid "Base part" msgstr "Basis-Teil" -#: stock/models.py:370 +#: stock/models.py:376 msgid "Select a matching supplier part for this stock item" msgstr "Passenden Zulieferer für dieses Lagerobjekt auswählen" -#: stock/models.py:375 stock/templates/stock/stock_app_base.html:7 +#: stock/models.py:381 stock/templates/stock/stock_app_base.html:7 msgid "Stock Location" msgstr "Lagerort" -#: stock/models.py:378 +#: stock/models.py:384 msgid "Where is this stock item located?" msgstr "Wo wird dieses Teil normalerweise gelagert?" -#: stock/models.py:383 stock/templates/stock/item_base.html:229 +#: stock/models.py:389 stock/templates/stock/item_base.html:249 msgid "Installed In" msgstr "Installiert in" -#: stock/models.py:386 +#: stock/models.py:392 msgid "Is this item installed in another item?" msgstr "Ist dieses Teil in einem anderen verbaut?" -#: stock/models.py:402 +#: stock/models.py:408 msgid "Serial number for this item" msgstr "Seriennummer für dieses Teil" -#: stock/models.py:414 +#: stock/models.py:420 msgid "Batch code for this stock item" msgstr "Losnummer für dieses Lagerobjekt" -#: stock/models.py:418 +#: stock/models.py:424 msgid "Stock Quantity" msgstr "Bestand" -#: stock/models.py:427 +#: stock/models.py:433 msgid "Source Build" msgstr "Quellbau" -#: stock/models.py:429 +#: stock/models.py:435 msgid "Build for this stock item" msgstr "Bau für dieses Lagerobjekt" -#: stock/models.py:440 +#: stock/models.py:446 msgid "Source Purchase Order" msgstr "Quellbestellung" -#: stock/models.py:443 +#: stock/models.py:449 msgid "Purchase order for this stock item" msgstr "Bestellung für dieses Teil" -#: stock/models.py:449 +#: stock/models.py:455 msgid "Destination Sales Order" msgstr "Zielauftrag" -#: stock/models.py:455 stock/templates/stock/item_base.html:316 +#: stock/models.py:461 stock/templates/stock/item_base.html:336 #: templates/js/stock.js:612 #, fuzzy #| msgid "Export" msgid "Expiry Date" msgstr "Exportieren" -#: stock/models.py:456 +#: stock/models.py:462 msgid "" "Expiry date for stock item. Stock will be considered expired after this date" msgstr "" -#: stock/models.py:469 +#: stock/models.py:475 msgid "Delete this Stock Item when stock is depleted" msgstr "Objekt löschen wenn Lagerbestand aufgebraucht" -#: stock/models.py:479 stock/templates/stock/item_notes.html:14 +#: stock/models.py:485 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:495 msgid "Single unit purchase price at time of purchase" msgstr "" -#: stock/models.py:589 +#: stock/models.py:599 #, fuzzy #| msgid "Item assigned to customer?" msgid "Assigned to Customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/models.py:591 +#: stock/models.py:601 #, fuzzy #| msgid "Item assigned to customer?" msgid "Manually assigned to customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/models.py:604 +#: stock/models.py:614 #, fuzzy #| msgid "Item assigned to customer?" msgid "Returned from customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/models.py:606 +#: stock/models.py:616 #, fuzzy #| msgid "Create new stock location" msgid "Returned to location" msgstr "Neuen Lagerort anlegen" -#: stock/models.py:731 +#: stock/models.py:741 #, fuzzy #| msgid "Installed in Stock Item" msgid "Installed into stock item" msgstr "In Lagerobjekt installiert" -#: stock/models.py:739 +#: stock/models.py:749 #, fuzzy #| msgid "Installed in Stock Item" msgid "Installed stock item" msgstr "In Lagerobjekt installiert" -#: stock/models.py:763 +#: stock/models.py:773 #, fuzzy #| msgid "Installed in Stock Item" msgid "Uninstalled stock item" msgstr "In Lagerobjekt installiert" -#: stock/models.py:782 +#: stock/models.py:792 #, fuzzy #| msgid "Include sublocations" msgid "Uninstalled into location" msgstr "Unterlagerorte einschließen" -#: stock/models.py:862 +#: stock/models.py:872 #, fuzzy #| msgid "Part is not a virtual part" msgid "Part is not set as trackable" msgstr "Teil ist nicht virtuell" -#: stock/models.py:868 +#: stock/models.py:878 msgid "Quantity must be integer" msgstr "Anzahl muss eine Ganzzahl sein" -#: stock/models.py:874 +#: stock/models.py:884 #, 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:877 +#: stock/models.py:887 msgid "Serial numbers must be a list of integers" msgstr "Seriennummern muss eine Liste von Ganzzahlen sein" -#: stock/models.py:880 +#: stock/models.py:890 msgid "Quantity does not match serial numbers" msgstr "Anzahl stimmt nicht mit den Seriennummern überein" -#: stock/models.py:912 +#: stock/models.py:922 msgid "Add serial number" msgstr "Seriennummer hinzufügen" -#: stock/models.py:915 +#: stock/models.py:925 #, python-brace-format msgid "Serialized {n} items" msgstr "{n} Teile serialisiert" -#: stock/models.py:1026 +#: stock/models.py:1036 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:1432 +#: stock/models.py:1442 msgid "Tracking entry title" msgstr "Name des Eintrags-Trackings" -#: stock/models.py:1434 +#: stock/models.py:1444 msgid "Entry notes" msgstr "Eintrags-Notizen" -#: stock/models.py:1436 +#: stock/models.py:1446 msgid "Link to external page for further information" msgstr "Link auf externe Seite für weitere Informationen" -#: stock/models.py:1496 +#: stock/models.py:1506 #, fuzzy #| msgid "Serial number for this item" msgid "Value must be provided for this test" msgstr "Seriennummer für dieses Teil" -#: stock/models.py:1502 +#: stock/models.py:1512 msgid "Attachment must be uploaded for this test" msgstr "" -#: stock/models.py:1519 +#: stock/models.py:1529 msgid "Test" msgstr "" -#: stock/models.py:1520 +#: stock/models.py:1530 #, fuzzy #| msgid "Part name" msgid "Test name" msgstr "Name des Teils" -#: stock/models.py:1525 +#: stock/models.py:1535 #, fuzzy #| msgid "Search Results" msgid "Result" msgstr "Suchergebnisse" -#: stock/models.py:1526 templates/js/table_filters.js:172 +#: stock/models.py:1536 templates/js/table_filters.js:172 msgid "Test result" msgstr "" -#: stock/models.py:1532 +#: stock/models.py:1542 msgid "Test output value" msgstr "" -#: stock/models.py:1538 +#: stock/models.py:1548 #, fuzzy #| msgid "Attachments" msgid "Attachment" msgstr "Anhänge" -#: stock/models.py:1539 +#: stock/models.py:1549 #, fuzzy #| msgid "Delete attachment" msgid "Test result attachment" msgstr "Anhang löschen" -#: stock/models.py:1545 +#: stock/models.py:1555 #, fuzzy #| msgid "Edit notes" msgid "Test notes" msgstr "Bermerkungen bearbeiten" -#: stock/templates/stock/item.html:11 +#: stock/templates/stock/item.html:16 msgid "Stock Tracking Information" msgstr "Informationen zum Lagerbestands-Tracking" -#: stock/templates/stock/item.html:18 +#: stock/templates/stock/item.html:25 #, fuzzy #| msgid "Category" msgid "New Entry" @@ -4723,31 +4731,37 @@ msgstr "Kategorie" msgid "Stock Item Attachments" msgstr "Lagerobjekt-Notizen" -#: stock/templates/stock/item_base.html:20 +#: stock/templates/stock/item_base.html:24 +msgid "" +"You are not in the list of owners of this item. This stock item cannot be " +"edited." +msgstr "" + +#: stock/templates/stock/item_base.html:31 #, fuzzy #| msgid "This stock item does not have any child items" msgid "This stock item is in production and cannot be edited." msgstr "Dieses Lagerobjekt hat keine Kinder" -#: stock/templates/stock/item_base.html:21 +#: stock/templates/stock/item_base.html:32 msgid "Edit the stock item from the build view." msgstr "" -#: stock/templates/stock/item_base.html:34 +#: stock/templates/stock/item_base.html:45 #, fuzzy #| msgid "This stock item does not have any child items" msgid "This stock item has not passed all required tests" msgstr "Dieses Lagerobjekt hat keine Kinder" -#: stock/templates/stock/item_base.html:40 +#: stock/templates/stock/item_base.html:51 msgid "This stock item is allocated to Sales Order" msgstr "Dieses Lagerobjekt ist dem Auftrag zugewiesen" -#: stock/templates/stock/item_base.html:46 +#: stock/templates/stock/item_base.html:57 msgid "This stock item is allocated to Build" msgstr "Dieses Lagerobjekt ist dem Bau zugewiesen" -#: stock/templates/stock/item_base.html:52 +#: stock/templates/stock/item_base.html:63 msgid "" "This stock item is serialized - it has a unique serial number and the " "quantity cannot be adjusted." @@ -4755,177 +4769,177 @@ msgstr "" "Dieses Lagerobjekt ist serialisiert. Es hat eine eindeutige Seriennummer und " "die Anzahl kann nicht angepasst werden." -#: stock/templates/stock/item_base.html:56 +#: stock/templates/stock/item_base.html:67 msgid "This stock item cannot be deleted as it has child items" msgstr "Dieses Lagerobjekt kann nicht gelöscht werden, da es Kinder besitzt" -#: stock/templates/stock/item_base.html:60 +#: stock/templates/stock/item_base.html:71 msgid "" "This stock item will be automatically deleted when all stock is depleted." msgstr "" "Dieses Lagerobjekt wird automatisch gelöscht wenn der Lagerbestand " "aufgebraucht ist." -#: stock/templates/stock/item_base.html:74 -#: stock/templates/stock/item_base.html:320 templates/js/table_filters.js:111 +#: stock/templates/stock/item_base.html:91 +#: stock/templates/stock/item_base.html:340 templates/js/table_filters.js:111 msgid "Expired" msgstr "" -#: stock/templates/stock/item_base.html:78 -#: stock/templates/stock/item_base.html:322 templates/js/table_filters.js:116 +#: stock/templates/stock/item_base.html:95 +#: stock/templates/stock/item_base.html:342 templates/js/table_filters.js:116 msgid "Stale" msgstr "" -#: stock/templates/stock/item_base.html:113 templates/js/barcode.js:283 +#: stock/templates/stock/item_base.html:130 templates/js/barcode.js:283 #: templates/js/barcode.js:288 msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:115 +#: stock/templates/stock/item_base.html:132 msgid "Link Barcode" msgstr "" -#: stock/templates/stock/item_base.html:123 +#: stock/templates/stock/item_base.html:140 #, fuzzy #| msgid "Confirm stock adjustment" msgid "Document actions" msgstr "Bestands-Anpassung bestätigen" -#: stock/templates/stock/item_base.html:129 +#: stock/templates/stock/item_base.html:146 #: stock/templates/stock/item_tests.html:25 msgid "Test Report" msgstr "" -#: stock/templates/stock/item_base.html:137 +#: stock/templates/stock/item_base.html:156 #, fuzzy #| msgid "Confirm stock adjustment" msgid "Stock adjustment actions" msgstr "Bestands-Anpassung bestätigen" -#: stock/templates/stock/item_base.html:141 -#: stock/templates/stock/location.html:41 templates/stock_table.html:24 +#: stock/templates/stock/item_base.html:160 +#: stock/templates/stock/location.html:57 templates/stock_table.html:35 msgid "Count stock" msgstr "Bestand zählen" -#: stock/templates/stock/item_base.html:142 templates/stock_table.html:22 +#: stock/templates/stock/item_base.html:161 templates/stock_table.html:33 msgid "Add stock" msgstr "Bestand hinzufügen" -#: stock/templates/stock/item_base.html:143 templates/stock_table.html:23 +#: stock/templates/stock/item_base.html:162 templates/stock_table.html:34 msgid "Remove stock" msgstr "Bestand entfernen" -#: stock/templates/stock/item_base.html:145 +#: stock/templates/stock/item_base.html:164 #, fuzzy #| msgid "Order stock" msgid "Transfer stock" msgstr "Bestand bestellen" -#: stock/templates/stock/item_base.html:147 +#: stock/templates/stock/item_base.html:166 #, fuzzy #| msgid "Serialize Stock" msgid "Serialize stock" msgstr "Lagerbestand erfassen" -#: stock/templates/stock/item_base.html:151 +#: stock/templates/stock/item_base.html:170 #, fuzzy #| msgid "Item assigned to customer?" msgid "Assign to customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/templates/stock/item_base.html:154 +#: stock/templates/stock/item_base.html:173 #, fuzzy #| msgid "Count stock" msgid "Return to stock" msgstr "Bestand zählen" -#: stock/templates/stock/item_base.html:158 templates/js/stock.js:1131 +#: stock/templates/stock/item_base.html:177 templates/js/stock.js:1136 #, fuzzy #| msgid "Installed in Stock Item" msgid "Uninstall stock item" msgstr "In Lagerobjekt installiert" -#: stock/templates/stock/item_base.html:158 +#: stock/templates/stock/item_base.html:177 msgid "Uninstall" msgstr "" -#: stock/templates/stock/item_base.html:167 -#: stock/templates/stock/location.html:38 +#: stock/templates/stock/item_base.html:186 +#: stock/templates/stock/location.html:54 #, fuzzy #| msgid "Stock Locations" msgid "Stock actions" msgstr "Lagerobjekt-Standorte" -#: stock/templates/stock/item_base.html:170 +#: stock/templates/stock/item_base.html:189 #, fuzzy #| msgid "Count stock items" msgid "Convert to variant" msgstr "Lagerobjekte zählen" -#: stock/templates/stock/item_base.html:173 +#: stock/templates/stock/item_base.html:192 #, fuzzy #| msgid "Count stock items" msgid "Duplicate stock item" msgstr "Lagerobjekte zählen" -#: stock/templates/stock/item_base.html:175 +#: stock/templates/stock/item_base.html:194 #, fuzzy #| msgid "Edit Stock Item" msgid "Edit stock item" msgstr "Lagerobjekt bearbeiten" -#: stock/templates/stock/item_base.html:178 +#: stock/templates/stock/item_base.html:197 #, fuzzy #| msgid "Delete Stock Item" msgid "Delete stock item" msgstr "Lagerobjekt löschen" -#: stock/templates/stock/item_base.html:189 +#: stock/templates/stock/item_base.html:209 msgid "Stock Item Details" msgstr "Lagerbestands-Details" -#: stock/templates/stock/item_base.html:248 templates/js/build.js:442 +#: stock/templates/stock/item_base.html:268 templates/js/build.js:442 #, fuzzy #| msgid "No stock location set" msgid "No location set" msgstr "Kein Lagerort gesetzt" -#: stock/templates/stock/item_base.html:255 +#: stock/templates/stock/item_base.html:275 #, fuzzy #| msgid "Unique Identifier" msgid "Barcode Identifier" msgstr "Eindeutiger Bezeichner" -#: stock/templates/stock/item_base.html:269 templates/js/build.js:642 +#: stock/templates/stock/item_base.html:289 templates/js/build.js:642 #: templates/navbar.html:25 msgid "Build" msgstr "Bau" -#: stock/templates/stock/item_base.html:290 +#: stock/templates/stock/item_base.html:310 msgid "Parent Item" msgstr "Elternposition" -#: stock/templates/stock/item_base.html:320 +#: stock/templates/stock/item_base.html:340 #, fuzzy #| msgid "This stock item is allocated to Build" msgid "This StockItem expired on" msgstr "Dieses Lagerobjekt ist dem Bau zugewiesen" -#: stock/templates/stock/item_base.html:322 +#: stock/templates/stock/item_base.html:342 #, fuzzy #| msgid "Child Stock Items" msgid "This StockItem expires on" msgstr "Kind-Lagerobjekte" -#: stock/templates/stock/item_base.html:329 +#: stock/templates/stock/item_base.html:349 templates/js/stock.js:618 msgid "Last Updated" msgstr "Zuletzt aktualisiert" -#: stock/templates/stock/item_base.html:334 +#: stock/templates/stock/item_base.html:354 msgid "Last Stocktake" msgstr "Letzte Inventur" -#: stock/templates/stock/item_base.html:338 +#: stock/templates/stock/item_base.html:358 msgid "No stocktake performed" msgstr "Keine Inventur ausgeführt" @@ -4993,64 +5007,70 @@ msgstr "Vorlage löschen" msgid "Add Test Data" msgstr "" -#: stock/templates/stock/location.html:18 +#: stock/templates/stock/location.html:13 +msgid "" +"You are not in the list of owners of this location. This stock location " +"cannot be edited." +msgstr "" + +#: stock/templates/stock/location.html:30 msgid "All stock items" msgstr "Alle Lagerobjekte" -#: stock/templates/stock/location.html:33 +#: stock/templates/stock/location.html:47 #, fuzzy #| msgid "Child Stock Items" msgid "Check-in Items" msgstr "Kind-Lagerobjekte" -#: stock/templates/stock/location.html:47 +#: stock/templates/stock/location.html:63 #, fuzzy #| msgid "Location Description" msgid "Location actions" msgstr "Standort-Beschreibung" -#: stock/templates/stock/location.html:49 +#: stock/templates/stock/location.html:65 #, fuzzy #| msgid "Edit stock location" msgid "Edit location" msgstr "Lagerort bearbeiten" -#: stock/templates/stock/location.html:51 +#: stock/templates/stock/location.html:67 #, fuzzy #| msgid "Delete stock location" msgid "Delete location" msgstr "Lagerort löschen" -#: stock/templates/stock/location.html:61 +#: stock/templates/stock/location.html:78 msgid "Location Details" msgstr "Standort-Details" -#: stock/templates/stock/location.html:66 +#: stock/templates/stock/location.html:83 msgid "Location Path" msgstr "Standord-Pfad" -#: stock/templates/stock/location.html:71 +#: stock/templates/stock/location.html:88 msgid "Location Description" msgstr "Standort-Beschreibung" -#: stock/templates/stock/location.html:76 +#: stock/templates/stock/location.html:93 msgid "Sublocations" msgstr "Sub-Standorte" -#: stock/templates/stock/location.html:81 -#: stock/templates/stock/location.html:96 +#: stock/templates/stock/location.html:98 +#: stock/templates/stock/location.html:113 #: templates/InvenTree/search_stock_items.html:6 templates/stats.html:48 -#: templates/stats.html:57 users/models.py:31 +#: templates/stats.html:57 users/models.py:35 msgid "Stock Items" msgstr "Lagerobjekte" -#: stock/templates/stock/location.html:86 +#: stock/templates/stock/location.html:103 msgid "Stock Details" msgstr "Objekt-Details" -#: stock/templates/stock/location.html:91 +#: stock/templates/stock/location.html:108 #: templates/InvenTree/search_stock_location.html:6 templates/stats.html:52 -#: users/models.py:30 +#: users/models.py:34 msgid "Stock Locations" msgstr "Lagerobjekt-Standorte" @@ -5066,7 +5086,7 @@ msgstr "Sind Sie sicher, dass Sie diesen Anhang löschen wollen?" msgid "The following stock items will be uninstalled" msgstr "Die folgenden Objekte werden erstellt" -#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1248 +#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1430 #, fuzzy #| msgid "Count Stock Items" msgid "Convert Stock Item" @@ -5104,231 +5124,236 @@ msgstr "Kinder" msgid "Installed Items" msgstr "Installiert in" -#: stock/views.py:122 +#: stock/views.py:126 msgid "Edit Stock Location" msgstr "Lagerobjekt-Standort bearbeiten" -#: stock/views.py:147 +#: stock/views.py:234 stock/views.py:1420 stock/views.py:1533 +#: stock/views.py:1895 +msgid "Owner is required (ownership control is enabled)" +msgstr "" + +#: stock/views.py:249 msgid "Stock Location QR code" msgstr "QR-Code für diesen Standort" -#: stock/views.py:166 +#: stock/views.py:268 #, fuzzy #| msgid "Add Attachment" msgid "Add Stock Item Attachment" msgstr "Anhang hinzufügen" -#: stock/views.py:213 +#: stock/views.py:315 #, fuzzy #| msgid "Edit Stock Item" msgid "Edit Stock Item Attachment" msgstr "Lagerobjekt bearbeiten" -#: stock/views.py:230 +#: stock/views.py:332 #, fuzzy #| msgid "Delete Part Attachment" msgid "Delete Stock Item Attachment" msgstr "Teilanhang löschen" -#: stock/views.py:247 +#: stock/views.py:349 #, fuzzy #| msgid "Item assigned to customer?" msgid "Assign to Customer" msgstr "Ist dieses Objekt einem Kunden zugeteilt?" -#: stock/views.py:257 +#: stock/views.py:359 msgid "Customer must be specified" msgstr "" -#: stock/views.py:281 +#: stock/views.py:383 #, fuzzy #| msgid "Part Stock" msgid "Return to Stock" msgstr "Teilbestand" -#: stock/views.py:291 +#: stock/views.py:393 #, fuzzy #| msgid "Include sublocations" msgid "Specify a valid location" msgstr "Unterlagerorte einschließen" -#: stock/views.py:302 +#: stock/views.py:404 msgid "Stock item returned from customer" msgstr "" -#: stock/views.py:313 +#: stock/views.py:415 #, fuzzy #| msgid "Delete Template" msgid "Delete All Test Data" msgstr "Vorlage löschen" -#: stock/views.py:329 +#: stock/views.py:431 #, fuzzy #| msgid "Confirm Part Deletion" msgid "Confirm test data deletion" msgstr "Löschen des Teils bestätigen" -#: stock/views.py:349 +#: stock/views.py:451 msgid "Add Test Result" msgstr "" -#: stock/views.py:390 +#: stock/views.py:492 #, fuzzy #| msgid "Edit Template" msgid "Edit Test Result" msgstr "Vorlage bearbeiten" -#: stock/views.py:408 +#: stock/views.py:510 #, fuzzy #| msgid "Delete Template" msgid "Delete Test Result" msgstr "Vorlage löschen" -#: stock/views.py:420 +#: stock/views.py:522 #, fuzzy #| msgid "Delete Template" msgid "Select Test Report Template" msgstr "Vorlage löschen" -#: stock/views.py:450 +#: stock/views.py:552 #, fuzzy #| msgid "Select valid part" msgid "Select valid template" msgstr "Bitte ein gültiges Teil auswählen" -#: stock/views.py:503 +#: stock/views.py:605 msgid "Stock Export Options" msgstr "Lagerbestandsexportoptionen" -#: stock/views.py:625 +#: stock/views.py:727 msgid "Stock Item QR Code" msgstr "Lagerobjekt-QR-Code" -#: stock/views.py:651 +#: stock/views.py:753 #, fuzzy #| msgid "Installed in Stock Item" msgid "Install Stock Item" msgstr "In Lagerobjekt installiert" -#: stock/views.py:751 +#: stock/views.py:853 #, fuzzy #| msgid "Installed in Stock Item" msgid "Uninstall Stock Items" msgstr "In Lagerobjekt installiert" -#: stock/views.py:859 +#: stock/views.py:961 #, fuzzy #| msgid "Installed in Stock Item" msgid "Uninstalled stock items" msgstr "In Lagerobjekt installiert" -#: stock/views.py:884 +#: stock/views.py:986 msgid "Adjust Stock" msgstr "Lagerbestand anpassen" -#: stock/views.py:994 +#: stock/views.py:1096 msgid "Move Stock Items" msgstr "Lagerobjekte bewegen" -#: stock/views.py:995 +#: stock/views.py:1097 msgid "Count Stock Items" msgstr "Lagerobjekte zählen" -#: stock/views.py:996 +#: stock/views.py:1098 msgid "Remove From Stock" msgstr "Aus Lagerbestand entfernen" -#: stock/views.py:997 +#: stock/views.py:1099 msgid "Add Stock Items" msgstr "Lagerobjekte hinzufügen" -#: stock/views.py:998 +#: stock/views.py:1100 msgid "Delete Stock Items" msgstr "Lagerobjekte löschen" -#: stock/views.py:1026 +#: stock/views.py:1128 msgid "Must enter integer value" msgstr "Nur Ganzzahl eingeben" -#: stock/views.py:1031 +#: stock/views.py:1133 msgid "Quantity must be positive" msgstr "Anzahl muss positiv sein" -#: stock/views.py:1038 +#: stock/views.py:1140 #, python-brace-format msgid "Quantity must not exceed {x}" msgstr "Anzahl darf {x} nicht überschreiten" -#: stock/views.py:1117 +#: stock/views.py:1219 #, python-brace-format msgid "Added stock to {n} items" msgstr "Vorrat zu {n} Lagerobjekten hinzugefügt" -#: stock/views.py:1132 +#: stock/views.py:1234 #, python-brace-format msgid "Removed stock from {n} items" msgstr "Vorrat von {n} Lagerobjekten entfernt" -#: stock/views.py:1145 +#: stock/views.py:1247 #, python-brace-format msgid "Counted stock for {n} items" msgstr "Bestand für {n} Objekte erfasst" -#: stock/views.py:1173 +#: stock/views.py:1287 msgid "No items were moved" msgstr "Keine Lagerobjekte wurden bewegt" -#: stock/views.py:1176 +#: stock/views.py:1290 #, python-brace-format msgid "Moved {n} items to {dest}" msgstr "{n} Teile nach {dest} bewegt" -#: stock/views.py:1195 +#: stock/views.py:1309 #, python-brace-format msgid "Deleted {n} stock items" msgstr "{n} Teile im Lager gelöscht" -#: stock/views.py:1207 +#: stock/views.py:1321 msgid "Edit Stock Item" msgstr "Lagerobjekt bearbeiten" -#: stock/views.py:1298 +#: stock/views.py:1550 msgid "Serialize Stock" msgstr "Lagerbestand erfassen" -#: stock/views.py:1392 templates/js/build.js:210 +#: stock/views.py:1644 templates/js/build.js:210 msgid "Create new Stock Item" msgstr "Neues Lagerobjekt hinzufügen" -#: stock/views.py:1500 +#: stock/views.py:1787 #, fuzzy #| msgid "Count stock items" msgid "Duplicate Stock Item" msgstr "Lagerobjekte zählen" -#: stock/views.py:1577 +#: stock/views.py:1869 #, fuzzy #| msgid "Quantity must be greater than zero" msgid "Quantity cannot be negative" msgstr "Anzahl muss größer Null sein" -#: stock/views.py:1663 +#: stock/views.py:1964 msgid "Delete Stock Location" msgstr "Standort löschen" -#: stock/views.py:1677 +#: stock/views.py:1978 msgid "Delete Stock Item" msgstr "Lagerobjekt löschen" -#: stock/views.py:1689 +#: stock/views.py:1990 msgid "Delete Stock Tracking Entry" msgstr "Lagerbestands-Tracking-Eintrag löschen" -#: stock/views.py:1708 +#: stock/views.py:2009 msgid "Edit Stock Tracking Entry" msgstr "Lagerbestands-Tracking-Eintrag bearbeiten" -#: stock/views.py:1718 +#: stock/views.py:2019 msgid "Add Stock Tracking Entry" msgstr "Lagerbestands-Tracking-Eintrag hinzufügen" @@ -5688,23 +5713,23 @@ msgstr "Code auf GitHub ansehen" msgid "Submit Bug Report" msgstr "Fehlerbericht senden" -#: templates/attachment_table.html:7 +#: templates/attachment_table.html:6 msgid "Add Attachment" msgstr "Anhang hinzufügen" -#: templates/attachment_table.html:17 +#: templates/attachment_table.html:15 msgid "File" msgstr "Datei" -#: templates/attachment_table.html:18 +#: templates/attachment_table.html:16 msgid "Comment" msgstr "Kommentar" -#: templates/attachment_table.html:19 +#: templates/attachment_table.html:17 msgid "Uploaded" msgstr "" -#: templates/attachment_table.html:37 +#: templates/attachment_table.html:35 msgid "Delete attachment" msgstr "Anhang löschen" @@ -5889,7 +5914,7 @@ msgstr "Lagerbestand dem Bau zuweisen" msgid "Delete build output" msgstr "Bau entfernt" -#: templates/js/build.js:209 templates/stock_table.html:13 +#: templates/js/build.js:209 templates/stock_table.html:21 msgid "New Stock Item" msgstr "Neues Lagerobjekt" @@ -5911,7 +5936,7 @@ msgstr "Anzahl" msgid "Build stock" msgstr "Baue" -#: templates/js/build.js:582 templates/stock_table.html:26 +#: templates/js/build.js:582 templates/stock_table.html:37 msgid "Order stock" msgstr "Bestand bestellen" @@ -6127,7 +6152,7 @@ msgstr "Keine Bestellungen gefunden" msgid "Order is overdue" msgstr "Bau-Zuweisung ist vollständig" -#: templates/js/order.js:193 templates/js/stock.js:816 +#: templates/js/order.js:193 templates/js/stock.js:821 msgid "Date" msgstr "Datum" @@ -6170,7 +6195,7 @@ msgid "No parts found" msgstr "Keine Teile gefunden" #: templates/js/part.js:343 templates/js/stock.js:473 -#: templates/js/stock.js:1163 +#: templates/js/stock.js:1168 msgid "Select" msgstr "Auswählen" @@ -6344,51 +6369,51 @@ msgstr "Löschen" msgid "Stocktake" msgstr "Letzte Inventur" -#: templates/js/stock.js:732 +#: templates/js/stock.js:737 #, fuzzy #| msgid "Stock status" msgid "Stock Status" msgstr "Bestandsstatus" -#: templates/js/stock.js:747 +#: templates/js/stock.js:752 #, fuzzy #| msgid "Stock status" msgid "Set Stock Status" msgstr "Bestandsstatus" -#: templates/js/stock.js:761 +#: templates/js/stock.js:766 #, fuzzy #| msgid "Select part to build" msgid "Select Status Code" msgstr "Teil für den Bau wählen" -#: templates/js/stock.js:762 +#: templates/js/stock.js:767 #, fuzzy #| msgid "StockItem has been allocated" msgid "Status code must be selected" msgstr "Lagerobjekt wurde zugewiesen" -#: templates/js/stock.js:882 +#: templates/js/stock.js:887 msgid "No user information" msgstr "Keine Benutzerinformation" -#: templates/js/stock.js:1002 +#: templates/js/stock.js:1007 msgid "Create New Location" msgstr "Neuen Standort anlegen" -#: templates/js/stock.js:1101 +#: templates/js/stock.js:1106 #, fuzzy #| msgid "Serial Number" msgid "Serial" msgstr "Seriennummer" -#: templates/js/stock.js:1194 templates/js/table_filters.js:131 +#: templates/js/stock.js:1199 templates/js/table_filters.js:131 #, fuzzy #| msgid "Installed In" msgid "Installed" msgstr "Installiert in" -#: templates/js/stock.js:1219 +#: templates/js/stock.js:1224 #, fuzzy #| msgid "Installed In" msgid "Install item" @@ -6593,7 +6618,7 @@ msgstr "" msgid "InvenTree server issues detected" msgstr "" -#: templates/navbar.html:63 users/models.py:27 +#: templates/navbar.html:63 users/models.py:31 msgid "Admin" msgstr "Admin" @@ -6637,69 +6662,69 @@ msgstr "" msgid "Issues detected" msgstr "Bestellung aufgeben" -#: templates/stock_table.html:6 +#: templates/stock_table.html:12 #, fuzzy #| msgid "Edit Stock Location" msgid "Export Stock Information" msgstr "Lagerobjekt-Standort bearbeiten" -#: templates/stock_table.html:20 +#: templates/stock_table.html:29 msgid "Print labels" msgstr "" -#: templates/stock_table.html:22 +#: templates/stock_table.html:33 #, fuzzy #| msgid "Added stock to {n} items" msgid "Add to selected stock items" msgstr "Vorrat zu {n} Lagerobjekten hinzugefügt" -#: templates/stock_table.html:23 +#: templates/stock_table.html:34 #, fuzzy #| msgid "Remove selected BOM items" msgid "Remove from selected stock items" msgstr "Ausgewählte Stücklistenpositionen entfernen" -#: templates/stock_table.html:24 +#: templates/stock_table.html:35 #, fuzzy #| msgid "Delete Stock Item" msgid "Stocktake selected stock items" msgstr "Lagerobjekt löschen" -#: templates/stock_table.html:25 +#: templates/stock_table.html:36 #, fuzzy #| msgid "Delete Stock Item" msgid "Move selected stock items" msgstr "Lagerobjekt löschen" -#: templates/stock_table.html:25 +#: templates/stock_table.html:36 msgid "Move stock" msgstr "Bestand bewegen" -#: templates/stock_table.html:26 +#: templates/stock_table.html:37 #, fuzzy #| msgid "Remove selected BOM items" msgid "Order selected items" msgstr "Ausgewählte Stücklistenpositionen entfernen" -#: templates/stock_table.html:27 +#: templates/stock_table.html:38 #, fuzzy #| msgid "Settings" msgid "Change status" msgstr "Einstellungen" -#: templates/stock_table.html:27 +#: templates/stock_table.html:38 #, fuzzy #| msgid "Stock status" msgid "Change stock status" msgstr "Bestandsstatus" -#: templates/stock_table.html:30 +#: templates/stock_table.html:41 #, fuzzy #| msgid "Delete line item" msgid "Delete selected items" msgstr "Position löschen" -#: templates/stock_table.html:30 +#: templates/stock_table.html:41 msgid "Delete Stock" msgstr "Bestand löschen" @@ -6735,41 +6760,41 @@ msgstr "Revision" msgid "Important dates" msgstr "Stückliste importieren" -#: users/models.py:142 +#: users/models.py:147 msgid "Permission set" msgstr "" -#: users/models.py:150 +#: users/models.py:155 msgid "Group" msgstr "" -#: users/models.py:153 +#: users/models.py:158 msgid "View" msgstr "" -#: users/models.py:153 +#: users/models.py:158 msgid "Permission to view items" msgstr "" -#: users/models.py:155 +#: users/models.py:160 #, fuzzy #| msgid "Address" msgid "Add" msgstr "Adresse" -#: users/models.py:155 +#: users/models.py:160 msgid "Permission to add items" msgstr "" -#: users/models.py:157 +#: users/models.py:162 msgid "Change" msgstr "" -#: users/models.py:157 +#: users/models.py:162 msgid "Permissions to edit items" msgstr "" -#: users/models.py:159 +#: users/models.py:164 #, fuzzy #| msgid "Remove selected BOM items" msgid "Permission to delete items" diff --git a/InvenTree/locale/en/LC_MESSAGES/django.po b/InvenTree/locale/en/LC_MESSAGES/django.po index 7389af937b..ee26db4504 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: 2021-01-14 23:57+1100\n" +"POT-Creation-Date: 2021-01-17 18:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -55,7 +55,7 @@ msgid "Select Category" msgstr "" #: InvenTree/helpers.py:361 order/models.py:232 order/models.py:330 -#: stock/views.py:1573 +#: stock/views.py:1865 msgid "Invalid quantity provided" msgstr "" @@ -95,7 +95,7 @@ msgstr "" msgid "File comment" msgstr "" -#: InvenTree/models.py:68 templates/js/stock.js:873 +#: InvenTree/models.py:68 templates/js/stock.js:878 msgid "User" msgstr "" @@ -302,7 +302,7 @@ msgstr "" #: build/forms.py:78 build/templates/build/auto_allocate.html:17 #: build/templates/build/build_base.html:83 -#: build/templates/build/detail.html:29 common/models.py:596 +#: build/templates/build/detail.html:29 common/models.py:603 #: company/forms.py:112 company/templates/company/supplier_part_pricing.html:75 #: order/templates/order/order_wizard/select_parts.html:32 #: order/templates/order/purchase_order_detail.html:179 @@ -310,13 +310,13 @@ msgstr "" #: order/templates/order/sales_order_detail.html:156 #: part/templates/part/allocation.html:16 #: part/templates/part/allocation.html:49 -#: part/templates/part/sale_prices.html:82 stock/forms.py:304 -#: stock/templates/stock/item_base.html:40 -#: stock/templates/stock/item_base.html:46 -#: stock/templates/stock/item_base.html:214 +#: part/templates/part/sale_prices.html:82 stock/forms.py:306 +#: stock/templates/stock/item_base.html:51 +#: stock/templates/stock/item_base.html:57 +#: stock/templates/stock/item_base.html:234 #: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.js:338 -#: templates/js/bom.js:195 templates/js/build.js:420 templates/js/stock.js:864 -#: templates/js/stock.js:1103 +#: templates/js/bom.js:195 templates/js/build.js:420 templates/js/stock.js:869 +#: templates/js/stock.js:1108 msgid "Quantity" msgstr "" @@ -324,7 +324,7 @@ msgstr "" msgid "Enter quantity for build output" msgstr "" -#: build/forms.py:83 stock/forms.py:116 +#: build/forms.py:83 stock/forms.py:117 msgid "Serial numbers" msgstr "" @@ -381,7 +381,7 @@ msgstr "" #: build/models.py:62 build/templates/build/index.html:8 #: build/templates/build/index.html:15 order/templates/order/so_builds.html:11 #: order/templates/order/so_tabs.html:9 part/templates/part/tabs.html:31 -#: templates/InvenTree/settings/tabs.html:28 users/models.py:32 +#: templates/InvenTree/settings/tabs.html:28 users/models.py:36 msgid "Build Orders" msgstr "" @@ -405,7 +405,7 @@ msgstr "" #: templates/js/bom.js:549 templates/js/build.js:664 templates/js/company.js:56 #: templates/js/order.js:180 templates/js/order.js:274 templates/js/part.js:188 #: templates/js/part.js:271 templates/js/part.js:391 templates/js/part.js:572 -#: templates/js/stock.js:511 templates/js/stock.js:845 +#: templates/js/stock.js:511 templates/js/stock.js:850 msgid "Description" msgstr "" @@ -433,7 +433,7 @@ msgstr "" #: templates/js/barcode.js:336 templates/js/bom.js:153 templates/js/bom.js:534 #: templates/js/build.js:669 templates/js/company.js:138 #: templates/js/part.js:252 templates/js/part.js:357 templates/js/stock.js:485 -#: templates/js/stock.js:1175 +#: templates/js/stock.js:1180 msgid "Part" msgstr "" @@ -491,7 +491,7 @@ msgstr "" msgid "Build status code" msgstr "" -#: build/models.py:194 stock/models.py:412 +#: build/models.py:194 stock/models.py:418 msgid "Batch Code" msgstr "" @@ -507,11 +507,11 @@ msgstr "" #: company/templates/company/supplier_part_base.html:68 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:80 part/templates/part/part_base.html:102 -#: stock/models.py:406 stock/templates/stock/item_base.html:297 +#: stock/models.py:412 stock/templates/stock/item_base.html:317 msgid "External Link" msgstr "" -#: build/models.py:220 part/models.py:705 stock/models.py:408 +#: build/models.py:220 part/models.py:705 stock/models.py:414 msgid "Link to external URL" msgstr "" @@ -519,10 +519,10 @@ msgstr "" #: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:18 #: order/templates/order/purchase_order_detail.html:213 #: order/templates/order/so_tabs.html:23 part/models.py:831 -#: part/templates/part/tabs.html:73 stock/forms.py:313 stock/forms.py:345 -#: stock/forms.py:373 stock/models.py:478 stock/models.py:1544 +#: part/templates/part/tabs.html:73 stock/forms.py:315 stock/forms.py:347 +#: stock/forms.py:375 stock/models.py:484 stock/models.py:1554 #: stock/templates/stock/tabs.html:26 templates/js/barcode.js:391 -#: templates/js/bom.js:295 templates/js/stock.js:127 templates/js/stock.js:618 +#: templates/js/bom.js:295 templates/js/stock.js:127 templates/js/stock.js:623 msgid "Notes" msgstr "" @@ -653,8 +653,8 @@ msgid "" "The following stock items will be allocated to the specified build output" msgstr "" -#: build/templates/build/auto_allocate.html:18 stock/forms.py:343 -#: stock/templates/stock/item_base.html:244 +#: build/templates/build/auto_allocate.html:18 stock/forms.py:345 +#: stock/templates/stock/item_base.html:264 #: stock/templates/stock/stock_adjust.html:17 #: templates/InvenTree/search.html:183 templates/js/barcode.js:337 #: templates/js/build.js:434 templates/js/stock.js:597 @@ -682,8 +682,8 @@ msgstr "" #: order/templates/order/order_base.html:26 #: order/templates/order/sales_order_base.html:35 #: part/templates/part/category.html:13 part/templates/part/part_base.html:32 -#: stock/templates/stock/item_base.html:97 -#: stock/templates/stock/location.html:12 +#: stock/templates/stock/item_base.html:114 +#: stock/templates/stock/location.html:24 msgid "Admin view" msgstr "" @@ -721,10 +721,10 @@ msgstr "" #: build/templates/build/build_base.html:88 #: build/templates/build/detail.html:57 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:343 templates/InvenTree/search.html:175 +#: stock/templates/stock/item_base.html:363 templates/InvenTree/search.html:175 #: templates/js/barcode.js:42 templates/js/build.js:697 #: templates/js/order.js:185 templates/js/order.js:279 -#: templates/js/stock.js:584 templates/js/stock.js:1111 +#: templates/js/stock.js:584 templates/js/stock.js:1116 msgid "Status" msgstr "" @@ -752,7 +752,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:238 templates/js/order.js:240 +#: stock/templates/stock/item_base.html:258 templates/js/order.js:240 msgid "Sales Order" msgstr "" @@ -844,7 +844,7 @@ msgstr "" msgid "Stock can be taken from any available location." msgstr "" -#: build/templates/build/detail.html:44 stock/forms.py:371 +#: build/templates/build/detail.html:44 stock/forms.py:373 msgid "Destination" msgstr "" @@ -853,8 +853,8 @@ msgid "Destination location not specified" msgstr "" #: build/templates/build/detail.html:68 -#: stock/templates/stock/item_base.html:262 templates/js/stock.js:592 -#: templates/js/stock.js:1118 templates/js/table_filters.js:80 +#: stock/templates/stock/item_base.html:282 templates/js/stock.js:592 +#: templates/js/stock.js:1123 templates/js/table_filters.js:80 #: templates/js/table_filters.js:161 msgid "Batch" msgstr "" @@ -953,7 +953,7 @@ msgstr "" msgid "Create Build Output" msgstr "" -#: build/views.py:207 stock/models.py:887 stock/views.py:1594 +#: build/views.py:207 stock/models.py:897 stock/views.py:1886 msgid "Serial numbers already exist" msgstr "" @@ -969,7 +969,7 @@ msgstr "" msgid "Confirm unallocation of build stock" msgstr "" -#: build/views.py:303 build/views.py:388 stock/views.py:330 +#: build/views.py:303 build/views.py:388 stock/views.py:432 msgid "Check the confirmation box" msgstr "" @@ -1063,7 +1063,7 @@ msgid "Add Build Order Attachment" msgstr "" #: build/views.py:1060 order/views.py:113 order/views.py:166 part/views.py:170 -#: stock/views.py:179 +#: stock/views.py:281 msgid "Added attachment" msgstr "" @@ -1079,7 +1079,7 @@ msgstr "" msgid "Delete Attachment" msgstr "" -#: build/views.py:1123 order/views.py:242 order/views.py:257 stock/views.py:237 +#: build/views.py:1123 order/views.py:242 order/views.py:257 stock/views.py:339 msgid "Deleted attachment" msgstr "" @@ -1155,7 +1155,7 @@ msgstr "" msgid "Copy category parameter templates when creating a part" msgstr "" -#: common/models.py:115 part/templates/part/detail.html:155 stock/forms.py:255 +#: common/models.py:115 part/templates/part/detail.html:155 stock/forms.py:257 #: templates/js/table_filters.js:23 templates/js/table_filters.js:270 msgid "Template" msgstr "" @@ -1262,71 +1262,79 @@ msgid "Allow building with expired stock" msgstr "" #: common/models.py:200 -msgid "Build Order Reference Prefix" +msgid "Stock Ownership Control" msgstr "" #: common/models.py:201 -msgid "Prefix value for build order reference" -msgstr "" - -#: common/models.py:206 -msgid "Build Order Reference Regex" +msgid "Enable ownership control over stock locations and items" msgstr "" #: common/models.py:207 +msgid "Build Order Reference Prefix" +msgstr "" + +#: common/models.py:208 +msgid "Prefix value for build order reference" +msgstr "" + +#: common/models.py:213 +msgid "Build Order Reference Regex" +msgstr "" + +#: common/models.py:214 msgid "Regular expression pattern for matching build order reference" msgstr "" -#: common/models.py:211 +#: common/models.py:218 msgid "Sales Order Reference Prefix" msgstr "" -#: common/models.py:212 +#: common/models.py:219 msgid "Prefix value for sales order reference" msgstr "" -#: common/models.py:217 +#: common/models.py:224 msgid "Purchase Order Reference Prefix" msgstr "" -#: common/models.py:218 +#: common/models.py:225 msgid "Prefix value for purchase order reference" msgstr "" -#: common/models.py:441 +#: common/models.py:448 msgid "Settings key (must be unique - case insensitive" msgstr "" -#: common/models.py:443 +#: common/models.py:450 msgid "Settings value" msgstr "" -#: common/models.py:500 +#: common/models.py:507 msgid "Value must be a boolean value" msgstr "" -#: common/models.py:510 +#: common/models.py:517 msgid "Value must be an integer value" msgstr "" -#: common/models.py:524 +#: common/models.py:531 msgid "Key string must be unique" msgstr "" -#: common/models.py:597 company/forms.py:113 +#: common/models.py:604 company/forms.py:113 msgid "Price break quantity" msgstr "" -#: common/models.py:605 company/templates/company/supplier_part_pricing.html:80 +#: common/models.py:612 company/templates/company/supplier_part_pricing.html:80 #: part/templates/part/sale_prices.html:87 templates/js/bom.js:246 msgid "Price" msgstr "" -#: common/models.py:606 +#: common/models.py:613 msgid "Unit price at specified quantity" msgstr "" -#: common/models.py:629 +#: common/models.py:636 msgid "Default" msgstr "" @@ -1427,8 +1435,8 @@ msgstr "" msgid "Currency" msgstr "" -#: company/models.py:313 stock/models.py:360 -#: stock/templates/stock/item_base.html:194 +#: company/models.py:313 stock/models.py:366 +#: stock/templates/stock/item_base.html:214 msgid "Base Part" msgstr "" @@ -1441,7 +1449,7 @@ msgstr "" #: company/templates/company/supplier_part_detail.html:21 #: order/templates/order/order_base.html:89 #: order/templates/order/order_wizard/select_pos.html:30 part/bom.py:170 -#: stock/templates/stock/item_base.html:304 templates/js/company.js:48 +#: stock/templates/stock/item_base.html:324 templates/js/company.js:48 #: templates/js/company.js:164 templates/js/order.js:167 msgid "Supplier" msgstr "" @@ -1537,8 +1545,8 @@ msgid "Uses default currency" msgstr "" #: company/templates/company/detail.html:62 -#: order/templates/order/sales_order_base.html:89 stock/models.py:395 -#: stock/models.py:396 stock/templates/stock/item_base.html:221 +#: order/templates/order/sales_order_base.html:89 stock/models.py:401 +#: stock/models.py:402 stock/templates/stock/item_base.html:241 #: templates/js/company.js:40 templates/js/order.js:261 msgid "Customer" msgstr "" @@ -1554,13 +1562,13 @@ msgstr "" #: company/templates/company/detail_part.html:18 #: order/templates/order/purchase_order_detail.html:68 -#: part/templates/part/supplier.html:14 templates/js/stock.js:995 +#: part/templates/part/supplier.html:14 templates/js/stock.js:1000 msgid "New Supplier Part" msgstr "" #: company/templates/company/detail_part.html:23 #: part/templates/part/category.html:120 part/templates/part/supplier.html:17 -#: templates/stock_table.html:18 +#: templates/stock_table.html:27 msgid "Options" msgstr "" @@ -1578,7 +1586,7 @@ msgid "Delete Parts" msgstr "" #: company/templates/company/detail_part.html:63 -#: part/templates/part/category.html:116 templates/js/stock.js:989 +#: part/templates/part/category.html:116 templates/js/stock.js:994 msgid "New Part" msgstr "" @@ -1612,7 +1620,7 @@ msgstr "" #: company/templates/company/supplier_part_stock.html:33 #: part/templates/part/bom.html:63 part/templates/part/category.html:112 #: part/templates/part/category.html:126 part/templates/part/stock.html:51 -#: templates/stock_table.html:7 +#: templates/stock_table.html:13 msgid "Export" msgstr "" @@ -1635,7 +1643,7 @@ msgstr "" #: order/templates/order/purchase_orders.html:13 #: part/templates/part/orders.html:9 part/templates/part/tabs.html:48 #: templates/InvenTree/settings/tabs.html:31 templates/navbar.html:33 -#: users/models.py:33 +#: users/models.py:37 msgid "Purchase Orders" msgstr "" @@ -1655,7 +1663,7 @@ msgstr "" #: order/templates/order/sales_orders.html:13 #: part/templates/part/sales_orders.html:9 part/templates/part/tabs.html:56 #: templates/InvenTree/settings/tabs.html:34 templates/navbar.html:42 -#: users/models.py:34 +#: users/models.py:38 msgid "Sales Orders" msgstr "" @@ -1670,8 +1678,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:369 -#: stock/templates/stock/item_base.html:309 templates/js/company.js:180 +#: company/templates/company/supplier_part_base.html:19 stock/models.py:375 +#: stock/templates/stock/item_base.html:329 templates/js/company.js:180 msgid "Supplier Part" msgstr "" @@ -1712,7 +1720,7 @@ msgid "Pricing Information" msgstr "" #: company/templates/company/supplier_part_pricing.html:17 company/views.py:486 -#: part/templates/part/sale_prices.html:14 part/views.py:2565 +#: part/templates/part/sale_prices.html:14 part/views.py:2567 msgid "Add Price Break" msgstr "" @@ -1741,7 +1749,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:17 templates/InvenTree/search.html:155 +#: stock/templates/stock/location.html:29 templates/InvenTree/search.html:155 #: templates/InvenTree/settings/tabs.html:25 templates/js/part.js:192 #: templates/js/part.js:418 templates/js/stock.js:519 templates/navbar.html:22 msgid "Stock" @@ -1756,7 +1764,7 @@ msgstr "" #: part/templates/part/cat_link.html:7 part/templates/part/category.html:94 #: part/templates/part/category_tabs.html:6 #: templates/InvenTree/settings/tabs.html:22 templates/navbar.html:19 -#: templates/stats.html:35 templates/stats.html:44 users/models.py:29 +#: templates/stats.html:35 templates/stats.html:44 users/models.py:33 msgid "Parts" msgstr "" @@ -1825,7 +1833,7 @@ msgstr "" msgid "Edit Supplier Part" msgstr "" -#: company/views.py:295 templates/js/stock.js:996 +#: company/views.py:295 templates/js/stock.js:1001 msgid "Create new Supplier Part" msgstr "" @@ -1833,15 +1841,15 @@ msgstr "" msgid "Delete Supplier Part" msgstr "" -#: company/views.py:492 part/views.py:2571 +#: company/views.py:492 part/views.py:2573 msgid "Added new price break" msgstr "" -#: company/views.py:548 part/views.py:2615 +#: company/views.py:548 part/views.py:2617 msgid "Edit Price Break" msgstr "" -#: company/views.py:564 part/views.py:2631 +#: company/views.py:564 part/views.py:2633 msgid "Delete Price Break" msgstr "" @@ -1865,7 +1873,7 @@ msgstr "" msgid "Label description" msgstr "" -#: label/models.py:83 stock/forms.py:198 +#: label/models.py:83 stock/forms.py:200 msgid "Label" msgstr "" @@ -1980,8 +1988,8 @@ msgstr "" msgid "Date order was completed" msgstr "" -#: order/models.py:230 order/models.py:328 part/views.py:1504 -#: stock/models.py:259 stock/models.py:871 +#: order/models.py:230 order/models.py:328 part/views.py:1506 +#: stock/models.py:265 stock/models.py:881 msgid "Quantity must be greater than zero" msgstr "" @@ -2019,7 +2027,7 @@ msgstr "" #: order/models.py:607 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:24 -#: stock/templates/stock/item_base.html:276 templates/js/order.js:145 +#: stock/templates/stock/item_base.html:296 templates/js/order.js:145 msgid "Purchase Order" msgstr "" @@ -2031,8 +2039,8 @@ msgstr "" msgid "Number of items received" msgstr "" -#: order/models.py:630 stock/models.py:488 -#: stock/templates/stock/item_base.html:283 +#: order/models.py:630 stock/models.py:494 +#: stock/templates/stock/item_base.html:303 msgid "Purchase Price" msgstr "" @@ -2190,13 +2198,13 @@ msgstr "" #: order/templates/order/purchase_order_detail.html:39 #: order/templates/order/purchase_order_detail.html:119 #: part/templates/part/category.html:173 part/templates/part/category.html:215 -#: templates/js/stock.js:642 templates/js/stock.js:1001 +#: templates/js/stock.js:647 templates/js/stock.js:1006 msgid "New Location" msgstr "" #: order/templates/order/purchase_order_detail.html:40 #: order/templates/order/purchase_order_detail.html:120 -#: stock/templates/stock/location.html:22 +#: stock/templates/stock/location.html:35 msgid "Create new stock location" msgstr "" @@ -2275,8 +2283,8 @@ msgid "Sales Order Items" msgstr "" #: order/templates/order/sales_order_detail.html:72 -#: order/templates/order/sales_order_detail.html:154 stock/models.py:400 -#: stock/templates/stock/item_base.html:208 templates/js/build.js:418 +#: order/templates/order/sales_order_detail.html:154 stock/models.py:406 +#: stock/templates/stock/item_base.html:228 templates/js/build.js:418 msgid "Serial Number" msgstr "" @@ -2508,11 +2516,11 @@ msgstr "" msgid "Error reading BOM file (incorrect row size)" msgstr "" -#: part/forms.py:71 stock/forms.py:261 +#: part/forms.py:71 stock/forms.py:263 msgid "File Format" msgstr "" -#: part/forms.py:71 stock/forms.py:261 +#: part/forms.py:71 stock/forms.py:263 msgid "Select output file format" msgstr "" @@ -2643,7 +2651,7 @@ msgstr "" #: part/models.py:78 part/templates/part/category.html:18 #: part/templates/part/category.html:89 templates/stats.html:39 -#: users/models.py:28 +#: users/models.py:32 msgid "Part Categories" msgstr "" @@ -2917,8 +2925,8 @@ msgstr "" msgid "BOM line checksum" msgstr "" -#: part/models.py:1963 part/views.py:1510 part/views.py:1562 -#: stock/models.py:249 +#: part/models.py:1963 part/views.py:1512 part/views.py:1564 +#: stock/models.py:255 msgid "Quantity must be integer value for trackable parts" msgstr "" @@ -2954,10 +2962,10 @@ msgstr "" #: part/templates/part/allocation.html:28 #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 -#: stock/templates/stock/item_base.html:72 -#: stock/templates/stock/item_base.html:291 +#: stock/templates/stock/item_base.html:89 +#: stock/templates/stock/item_base.html:311 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.js:751 -#: templates/js/stock.js:834 templates/js/stock.js:1094 +#: templates/js/stock.js:839 templates/js/stock.js:1099 msgid "Stock Item" msgstr "" @@ -3022,7 +3030,7 @@ msgstr "" msgid "Validate" msgstr "" -#: part/templates/part/bom.html:62 part/views.py:1801 +#: part/templates/part/bom.html:62 part/views.py:1803 msgid "Export Bill of Materials" msgstr "" @@ -3122,7 +3130,7 @@ msgstr "" msgid "All parts" msgstr "" -#: part/templates/part/category.html:24 part/views.py:2192 +#: part/templates/part/category.html:24 part/views.py:2194 msgid "Create new part category" msgstr "" @@ -3174,7 +3182,7 @@ msgstr "" msgid "Export Data" msgstr "" -#: part/templates/part/category.html:174 templates/js/stock.js:643 +#: part/templates/part/category.html:174 templates/js/stock.js:648 msgid "Create new location" msgstr "" @@ -3190,7 +3198,7 @@ msgstr "" msgid "Create new Part Category" msgstr "" -#: part/templates/part/category.html:216 stock/views.py:1276 +#: part/templates/part/category.html:216 stock/views.py:1458 msgid "Create new Stock Location" msgstr "" @@ -3316,13 +3324,13 @@ msgstr "" msgid "New Parameter" msgstr "" -#: part/templates/part/params.html:25 stock/models.py:1531 +#: part/templates/part/params.html:25 stock/models.py:1541 #: templates/InvenTree/settings/header.html:8 templates/js/stock.js:123 msgid "Value" msgstr "" #: part/templates/part/params.html:41 part/templates/part/related.html:41 -#: part/templates/part/supplier.html:19 users/models.py:159 +#: part/templates/part/supplier.html:19 users/models.py:164 msgid "Delete" msgstr "" @@ -3352,20 +3360,20 @@ msgid "Star this part" msgstr "" #: part/templates/part/part_base.html:49 -#: stock/templates/stock/item_base.html:108 -#: stock/templates/stock/location.html:29 +#: stock/templates/stock/item_base.html:125 +#: stock/templates/stock/location.html:43 msgid "Barcode actions" msgstr "" #: part/templates/part/part_base.html:51 -#: stock/templates/stock/item_base.html:110 -#: stock/templates/stock/location.html:31 +#: stock/templates/stock/item_base.html:127 +#: stock/templates/stock/location.html:45 msgid "Show QR Code" msgstr "" #: part/templates/part/part_base.html:52 -#: stock/templates/stock/item_base.html:126 -#: stock/templates/stock/location.html:32 +#: stock/templates/stock/item_base.html:143 +#: stock/templates/stock/location.html:46 msgid "Print Label" msgstr "" @@ -3502,7 +3510,7 @@ msgstr "" msgid "Used In" msgstr "" -#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:349 +#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:369 msgid "Tests" msgstr "" @@ -3542,7 +3550,7 @@ msgstr "" msgid "Add part attachment" msgstr "" -#: part/views.py:209 templates/attachment_table.html:34 +#: part/views.py:209 templates/attachment_table.html:32 msgid "Edit attachment" msgstr "" @@ -3591,11 +3599,11 @@ msgstr "" msgid "Copied part" msgstr "" -#: part/views.py:529 part/views.py:667 +#: part/views.py:529 part/views.py:669 msgid "Possible matches exist - confirm creation of new part" msgstr "" -#: part/views.py:594 templates/js/stock.js:990 +#: part/views.py:594 templates/js/stock.js:995 msgid "Create New Part" msgstr "" @@ -3603,147 +3611,147 @@ msgstr "" msgid "Created new part" msgstr "" -#: part/views.py:836 +#: part/views.py:838 msgid "Part QR Code" msgstr "" -#: part/views.py:855 +#: part/views.py:857 msgid "Upload Part Image" msgstr "" -#: part/views.py:863 part/views.py:900 +#: part/views.py:865 part/views.py:902 msgid "Updated part image" msgstr "" -#: part/views.py:872 +#: part/views.py:874 msgid "Select Part Image" msgstr "" -#: part/views.py:903 +#: part/views.py:905 msgid "Part image not found" msgstr "" -#: part/views.py:914 +#: part/views.py:916 msgid "Edit Part Properties" msgstr "" -#: part/views.py:945 +#: part/views.py:947 msgid "Duplicate BOM" msgstr "" -#: part/views.py:976 +#: part/views.py:978 msgid "Confirm duplication of BOM from parent" msgstr "" -#: part/views.py:997 +#: part/views.py:999 msgid "Validate BOM" msgstr "" -#: part/views.py:1020 +#: part/views.py:1022 msgid "Confirm that the BOM is valid" msgstr "" -#: part/views.py:1031 +#: part/views.py:1033 msgid "Validated Bill of Materials" msgstr "" -#: part/views.py:1165 +#: part/views.py:1167 msgid "No BOM file provided" msgstr "" -#: part/views.py:1513 +#: part/views.py:1515 msgid "Enter a valid quantity" msgstr "" -#: part/views.py:1538 part/views.py:1541 +#: part/views.py:1540 part/views.py:1543 msgid "Select valid part" msgstr "" -#: part/views.py:1547 +#: part/views.py:1549 msgid "Duplicate part selected" msgstr "" -#: part/views.py:1585 +#: part/views.py:1587 msgid "Select a part" msgstr "" -#: part/views.py:1591 +#: part/views.py:1593 msgid "Selected part creates a circular BOM" msgstr "" -#: part/views.py:1595 +#: part/views.py:1597 msgid "Specify quantity" msgstr "" -#: part/views.py:1851 +#: part/views.py:1853 msgid "Confirm Part Deletion" msgstr "" -#: part/views.py:1860 +#: part/views.py:1862 msgid "Part was deleted" msgstr "" -#: part/views.py:1869 +#: part/views.py:1871 msgid "Part Pricing" msgstr "" -#: part/views.py:1983 +#: part/views.py:1985 msgid "Create Part Parameter Template" msgstr "" -#: part/views.py:1993 +#: part/views.py:1995 msgid "Edit Part Parameter Template" msgstr "" -#: part/views.py:2002 +#: part/views.py:2004 msgid "Delete Part Parameter Template" msgstr "" -#: part/views.py:2012 +#: part/views.py:2014 msgid "Create Part Parameter" msgstr "" -#: part/views.py:2064 +#: part/views.py:2066 msgid "Edit Part Parameter" msgstr "" -#: part/views.py:2080 +#: part/views.py:2082 msgid "Delete Part Parameter" msgstr "" -#: part/views.py:2139 +#: part/views.py:2141 msgid "Edit Part Category" msgstr "" -#: part/views.py:2176 +#: part/views.py:2178 msgid "Delete Part Category" msgstr "" -#: part/views.py:2184 +#: part/views.py:2186 msgid "Part category was deleted" msgstr "" -#: part/views.py:2240 +#: part/views.py:2242 msgid "Create Category Parameter Template" msgstr "" -#: part/views.py:2343 +#: part/views.py:2345 msgid "Edit Category Parameter Template" msgstr "" -#: part/views.py:2401 +#: part/views.py:2403 msgid "Delete Category Parameter Template" msgstr "" -#: part/views.py:2426 +#: part/views.py:2428 msgid "Create BOM Item" msgstr "" -#: part/views.py:2498 +#: part/views.py:2500 msgid "Edit BOM item" msgstr "" -#: part/views.py:2555 +#: part/views.py:2557 msgid "Confim BOM item deletion" msgstr "" @@ -3775,309 +3783,309 @@ msgstr "" msgid "Asset file description" msgstr "" -#: stock/forms.py:116 +#: stock/forms.py:117 msgid "Enter unique serial numbers (or leave blank)" msgstr "" -#: stock/forms.py:199 stock/forms.py:255 +#: stock/forms.py:201 stock/forms.py:257 msgid "Select test report template" msgstr "" -#: stock/forms.py:263 +#: stock/forms.py:265 msgid "Include stock items in sub locations" msgstr "" -#: stock/forms.py:298 +#: stock/forms.py:300 msgid "Stock item to install" msgstr "" -#: stock/forms.py:305 +#: stock/forms.py:307 msgid "Stock quantity to assign" msgstr "" -#: stock/forms.py:333 +#: stock/forms.py:335 msgid "Must not exceed available quantity" msgstr "" -#: stock/forms.py:343 +#: stock/forms.py:345 msgid "Destination location for uninstalled items" msgstr "" -#: stock/forms.py:345 +#: stock/forms.py:347 msgid "Add transaction note (optional)" msgstr "" -#: stock/forms.py:347 +#: stock/forms.py:349 msgid "Confirm uninstall" msgstr "" -#: stock/forms.py:347 +#: stock/forms.py:349 msgid "Confirm removal of installed stock items" msgstr "" -#: stock/forms.py:371 +#: stock/forms.py:373 msgid "Destination stock location" msgstr "" -#: stock/forms.py:373 +#: stock/forms.py:375 msgid "Add note (required)" msgstr "" -#: stock/forms.py:377 stock/views.py:848 stock/views.py:1046 +#: stock/forms.py:379 stock/views.py:950 stock/views.py:1148 msgid "Confirm stock adjustment" msgstr "" -#: stock/forms.py:377 +#: stock/forms.py:379 msgid "Confirm movement of stock items" msgstr "" -#: stock/forms.py:379 +#: stock/forms.py:381 msgid "Set Default Location" msgstr "" -#: stock/forms.py:379 +#: stock/forms.py:381 msgid "Set the destination as the default location for selected parts" msgstr "" -#: stock/models.py:194 +#: stock/models.py:200 msgid "Created stock item" msgstr "" -#: stock/models.py:230 +#: stock/models.py:236 msgid "StockItem with this serial number already exists" msgstr "" -#: stock/models.py:266 +#: stock/models.py:272 #, python-brace-format msgid "Part type ('{pf}') must be {pe}" msgstr "" -#: stock/models.py:276 stock/models.py:285 +#: stock/models.py:282 stock/models.py:291 msgid "Quantity must be 1 for item with a serial number" msgstr "" -#: stock/models.py:277 +#: stock/models.py:283 msgid "Serial number cannot be set if quantity greater than 1" msgstr "" -#: stock/models.py:299 +#: stock/models.py:305 msgid "Item cannot belong to itself" msgstr "" -#: stock/models.py:305 +#: stock/models.py:311 msgid "Item must have a build reference if is_building=True" msgstr "" -#: stock/models.py:312 +#: stock/models.py:318 msgid "Build reference does not point to the same part object" msgstr "" -#: stock/models.py:352 +#: stock/models.py:358 msgid "Parent Stock Item" msgstr "" -#: stock/models.py:361 +#: stock/models.py:367 msgid "Base part" msgstr "" -#: stock/models.py:370 +#: stock/models.py:376 msgid "Select a matching supplier part for this stock item" msgstr "" -#: stock/models.py:375 stock/templates/stock/stock_app_base.html:7 +#: stock/models.py:381 stock/templates/stock/stock_app_base.html:7 msgid "Stock Location" msgstr "" -#: stock/models.py:378 +#: stock/models.py:384 msgid "Where is this stock item located?" msgstr "" -#: stock/models.py:383 stock/templates/stock/item_base.html:229 +#: stock/models.py:389 stock/templates/stock/item_base.html:249 msgid "Installed In" msgstr "" -#: stock/models.py:386 +#: stock/models.py:392 msgid "Is this item installed in another item?" msgstr "" -#: stock/models.py:402 +#: stock/models.py:408 msgid "Serial number for this item" msgstr "" -#: stock/models.py:414 +#: stock/models.py:420 msgid "Batch code for this stock item" msgstr "" -#: stock/models.py:418 +#: stock/models.py:424 msgid "Stock Quantity" msgstr "" -#: stock/models.py:427 +#: stock/models.py:433 msgid "Source Build" msgstr "" -#: stock/models.py:429 +#: stock/models.py:435 msgid "Build for this stock item" msgstr "" -#: stock/models.py:440 +#: stock/models.py:446 msgid "Source Purchase Order" msgstr "" -#: stock/models.py:443 +#: stock/models.py:449 msgid "Purchase order for this stock item" msgstr "" -#: stock/models.py:449 +#: stock/models.py:455 msgid "Destination Sales Order" msgstr "" -#: stock/models.py:455 stock/templates/stock/item_base.html:316 +#: stock/models.py:461 stock/templates/stock/item_base.html:336 #: templates/js/stock.js:612 msgid "Expiry Date" msgstr "" -#: stock/models.py:456 +#: stock/models.py:462 msgid "" "Expiry date for stock item. Stock will be considered expired after this date" msgstr "" -#: stock/models.py:469 +#: stock/models.py:475 msgid "Delete this Stock Item when stock is depleted" msgstr "" -#: stock/models.py:479 stock/templates/stock/item_notes.html:14 +#: stock/models.py:485 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:495 msgid "Single unit purchase price at time of purchase" msgstr "" -#: stock/models.py:589 +#: stock/models.py:599 msgid "Assigned to Customer" msgstr "" -#: stock/models.py:591 +#: stock/models.py:601 msgid "Manually assigned to customer" msgstr "" -#: stock/models.py:604 +#: stock/models.py:614 msgid "Returned from customer" msgstr "" -#: stock/models.py:606 +#: stock/models.py:616 msgid "Returned to location" msgstr "" -#: stock/models.py:731 +#: stock/models.py:741 msgid "Installed into stock item" msgstr "" -#: stock/models.py:739 +#: stock/models.py:749 msgid "Installed stock item" msgstr "" -#: stock/models.py:763 +#: stock/models.py:773 msgid "Uninstalled stock item" msgstr "" -#: stock/models.py:782 +#: stock/models.py:792 msgid "Uninstalled into location" msgstr "" -#: stock/models.py:862 +#: stock/models.py:872 msgid "Part is not set as trackable" msgstr "" -#: stock/models.py:868 +#: stock/models.py:878 msgid "Quantity must be integer" msgstr "" -#: stock/models.py:874 +#: stock/models.py:884 #, python-brace-format msgid "Quantity must not exceed available stock quantity ({n})" msgstr "" -#: stock/models.py:877 +#: stock/models.py:887 msgid "Serial numbers must be a list of integers" msgstr "" -#: stock/models.py:880 +#: stock/models.py:890 msgid "Quantity does not match serial numbers" msgstr "" -#: stock/models.py:912 +#: stock/models.py:922 msgid "Add serial number" msgstr "" -#: stock/models.py:915 +#: stock/models.py:925 #, python-brace-format msgid "Serialized {n} items" msgstr "" -#: stock/models.py:1026 +#: stock/models.py:1036 msgid "StockItem cannot be moved as it is not in stock" msgstr "" -#: stock/models.py:1432 +#: stock/models.py:1442 msgid "Tracking entry title" msgstr "" -#: stock/models.py:1434 +#: stock/models.py:1444 msgid "Entry notes" msgstr "" -#: stock/models.py:1436 +#: stock/models.py:1446 msgid "Link to external page for further information" msgstr "" -#: stock/models.py:1496 +#: stock/models.py:1506 msgid "Value must be provided for this test" msgstr "" -#: stock/models.py:1502 +#: stock/models.py:1512 msgid "Attachment must be uploaded for this test" msgstr "" -#: stock/models.py:1519 +#: stock/models.py:1529 msgid "Test" msgstr "" -#: stock/models.py:1520 +#: stock/models.py:1530 msgid "Test name" msgstr "" -#: stock/models.py:1525 +#: stock/models.py:1535 msgid "Result" msgstr "" -#: stock/models.py:1526 templates/js/table_filters.js:172 +#: stock/models.py:1536 templates/js/table_filters.js:172 msgid "Test result" msgstr "" -#: stock/models.py:1532 +#: stock/models.py:1542 msgid "Test output value" msgstr "" -#: stock/models.py:1538 +#: stock/models.py:1548 msgid "Attachment" msgstr "" -#: stock/models.py:1539 +#: stock/models.py:1549 msgid "Test result attachment" msgstr "" -#: stock/models.py:1545 +#: stock/models.py:1555 msgid "Test notes" msgstr "" -#: stock/templates/stock/item.html:11 +#: stock/templates/stock/item.html:16 msgid "Stock Tracking Information" msgstr "" -#: stock/templates/stock/item.html:18 +#: stock/templates/stock/item.html:25 msgid "New Entry" msgstr "" @@ -4085,169 +4093,175 @@ msgstr "" msgid "Stock Item Attachments" msgstr "" -#: stock/templates/stock/item_base.html:20 +#: stock/templates/stock/item_base.html:24 +msgid "" +"You are not in the list of owners of this item. This stock item cannot be " +"edited." +msgstr "" + +#: stock/templates/stock/item_base.html:31 msgid "This stock item is in production and cannot be edited." msgstr "" -#: stock/templates/stock/item_base.html:21 +#: stock/templates/stock/item_base.html:32 msgid "Edit the stock item from the build view." msgstr "" -#: stock/templates/stock/item_base.html:34 +#: stock/templates/stock/item_base.html:45 msgid "This stock item has not passed all required tests" msgstr "" -#: stock/templates/stock/item_base.html:40 +#: stock/templates/stock/item_base.html:51 msgid "This stock item is allocated to Sales Order" msgstr "" -#: stock/templates/stock/item_base.html:46 +#: stock/templates/stock/item_base.html:57 msgid "This stock item is allocated to Build" msgstr "" -#: stock/templates/stock/item_base.html:52 +#: stock/templates/stock/item_base.html:63 msgid "" "This stock item is serialized - it has a unique serial number and the " "quantity cannot be adjusted." msgstr "" -#: stock/templates/stock/item_base.html:56 +#: stock/templates/stock/item_base.html:67 msgid "This stock item cannot be deleted as it has child items" msgstr "" -#: stock/templates/stock/item_base.html:60 +#: stock/templates/stock/item_base.html:71 msgid "" "This stock item will be automatically deleted when all stock is depleted." msgstr "" -#: stock/templates/stock/item_base.html:74 -#: stock/templates/stock/item_base.html:320 templates/js/table_filters.js:111 +#: stock/templates/stock/item_base.html:91 +#: stock/templates/stock/item_base.html:340 templates/js/table_filters.js:111 msgid "Expired" msgstr "" -#: stock/templates/stock/item_base.html:78 -#: stock/templates/stock/item_base.html:322 templates/js/table_filters.js:116 +#: stock/templates/stock/item_base.html:95 +#: stock/templates/stock/item_base.html:342 templates/js/table_filters.js:116 msgid "Stale" msgstr "" -#: stock/templates/stock/item_base.html:113 templates/js/barcode.js:283 +#: stock/templates/stock/item_base.html:130 templates/js/barcode.js:283 #: templates/js/barcode.js:288 msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:115 +#: stock/templates/stock/item_base.html:132 msgid "Link Barcode" msgstr "" -#: stock/templates/stock/item_base.html:123 +#: stock/templates/stock/item_base.html:140 msgid "Document actions" msgstr "" -#: stock/templates/stock/item_base.html:129 +#: stock/templates/stock/item_base.html:146 #: stock/templates/stock/item_tests.html:25 msgid "Test Report" msgstr "" -#: stock/templates/stock/item_base.html:137 +#: stock/templates/stock/item_base.html:156 msgid "Stock adjustment actions" msgstr "" -#: stock/templates/stock/item_base.html:141 -#: stock/templates/stock/location.html:41 templates/stock_table.html:24 +#: stock/templates/stock/item_base.html:160 +#: stock/templates/stock/location.html:57 templates/stock_table.html:35 msgid "Count stock" msgstr "" -#: stock/templates/stock/item_base.html:142 templates/stock_table.html:22 +#: stock/templates/stock/item_base.html:161 templates/stock_table.html:33 msgid "Add stock" msgstr "" -#: stock/templates/stock/item_base.html:143 templates/stock_table.html:23 +#: stock/templates/stock/item_base.html:162 templates/stock_table.html:34 msgid "Remove stock" msgstr "" -#: stock/templates/stock/item_base.html:145 +#: stock/templates/stock/item_base.html:164 msgid "Transfer stock" msgstr "" -#: stock/templates/stock/item_base.html:147 +#: stock/templates/stock/item_base.html:166 msgid "Serialize stock" msgstr "" -#: stock/templates/stock/item_base.html:151 +#: stock/templates/stock/item_base.html:170 msgid "Assign to customer" msgstr "" -#: stock/templates/stock/item_base.html:154 +#: stock/templates/stock/item_base.html:173 msgid "Return to stock" msgstr "" -#: stock/templates/stock/item_base.html:158 templates/js/stock.js:1131 +#: stock/templates/stock/item_base.html:177 templates/js/stock.js:1136 msgid "Uninstall stock item" msgstr "" -#: stock/templates/stock/item_base.html:158 +#: stock/templates/stock/item_base.html:177 msgid "Uninstall" msgstr "" -#: stock/templates/stock/item_base.html:167 -#: stock/templates/stock/location.html:38 +#: stock/templates/stock/item_base.html:186 +#: stock/templates/stock/location.html:54 msgid "Stock actions" msgstr "" -#: stock/templates/stock/item_base.html:170 +#: stock/templates/stock/item_base.html:189 msgid "Convert to variant" msgstr "" -#: stock/templates/stock/item_base.html:173 +#: stock/templates/stock/item_base.html:192 msgid "Duplicate stock item" msgstr "" -#: stock/templates/stock/item_base.html:175 +#: stock/templates/stock/item_base.html:194 msgid "Edit stock item" msgstr "" -#: stock/templates/stock/item_base.html:178 +#: stock/templates/stock/item_base.html:197 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:189 +#: stock/templates/stock/item_base.html:209 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:248 templates/js/build.js:442 +#: stock/templates/stock/item_base.html:268 templates/js/build.js:442 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:255 +#: stock/templates/stock/item_base.html:275 msgid "Barcode Identifier" msgstr "" -#: stock/templates/stock/item_base.html:269 templates/js/build.js:642 +#: stock/templates/stock/item_base.html:289 templates/js/build.js:642 #: templates/navbar.html:25 msgid "Build" msgstr "" -#: stock/templates/stock/item_base.html:290 +#: stock/templates/stock/item_base.html:310 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:320 +#: stock/templates/stock/item_base.html:340 msgid "This StockItem expired on" msgstr "" -#: stock/templates/stock/item_base.html:322 +#: stock/templates/stock/item_base.html:342 msgid "This StockItem expires on" msgstr "" -#: stock/templates/stock/item_base.html:329 +#: stock/templates/stock/item_base.html:349 templates/js/stock.js:618 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:334 +#: stock/templates/stock/item_base.html:354 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:338 +#: stock/templates/stock/item_base.html:358 msgid "No stocktake performed" msgstr "" @@ -4303,56 +4317,62 @@ msgstr "" msgid "Add Test Data" msgstr "" -#: stock/templates/stock/location.html:18 +#: stock/templates/stock/location.html:13 +msgid "" +"You are not in the list of owners of this location. This stock location " +"cannot be edited." +msgstr "" + +#: stock/templates/stock/location.html:30 msgid "All stock items" msgstr "" -#: stock/templates/stock/location.html:33 +#: stock/templates/stock/location.html:47 msgid "Check-in Items" msgstr "" -#: stock/templates/stock/location.html:47 +#: stock/templates/stock/location.html:63 msgid "Location actions" msgstr "" -#: stock/templates/stock/location.html:49 +#: stock/templates/stock/location.html:65 msgid "Edit location" msgstr "" -#: stock/templates/stock/location.html:51 +#: stock/templates/stock/location.html:67 msgid "Delete location" msgstr "" -#: stock/templates/stock/location.html:61 +#: stock/templates/stock/location.html:78 msgid "Location Details" msgstr "" -#: stock/templates/stock/location.html:66 +#: stock/templates/stock/location.html:83 msgid "Location Path" msgstr "" -#: stock/templates/stock/location.html:71 +#: stock/templates/stock/location.html:88 msgid "Location Description" msgstr "" -#: stock/templates/stock/location.html:76 +#: stock/templates/stock/location.html:93 msgid "Sublocations" msgstr "" -#: stock/templates/stock/location.html:81 -#: stock/templates/stock/location.html:96 +#: stock/templates/stock/location.html:98 +#: stock/templates/stock/location.html:113 #: templates/InvenTree/search_stock_items.html:6 templates/stats.html:48 -#: templates/stats.html:57 users/models.py:31 +#: templates/stats.html:57 users/models.py:35 msgid "Stock Items" msgstr "" -#: stock/templates/stock/location.html:86 +#: stock/templates/stock/location.html:103 msgid "Stock Details" msgstr "" -#: stock/templates/stock/location.html:91 +#: stock/templates/stock/location.html:108 #: templates/InvenTree/search_stock_location.html:6 templates/stats.html:52 -#: users/models.py:30 +#: users/models.py:34 msgid "Stock Locations" msgstr "" @@ -4364,7 +4384,7 @@ msgstr "" msgid "The following stock items will be uninstalled" msgstr "" -#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1248 +#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1430 msgid "Convert Stock Item" msgstr "" @@ -4396,197 +4416,202 @@ msgstr "" msgid "Installed Items" msgstr "" -#: stock/views.py:122 +#: stock/views.py:126 msgid "Edit Stock Location" msgstr "" -#: stock/views.py:147 +#: stock/views.py:234 stock/views.py:1420 stock/views.py:1533 +#: stock/views.py:1895 +msgid "Owner is required (ownership control is enabled)" +msgstr "" + +#: stock/views.py:249 msgid "Stock Location QR code" msgstr "" -#: stock/views.py:166 +#: stock/views.py:268 msgid "Add Stock Item Attachment" msgstr "" -#: stock/views.py:213 +#: stock/views.py:315 msgid "Edit Stock Item Attachment" msgstr "" -#: stock/views.py:230 +#: stock/views.py:332 msgid "Delete Stock Item Attachment" msgstr "" -#: stock/views.py:247 +#: stock/views.py:349 msgid "Assign to Customer" msgstr "" -#: stock/views.py:257 +#: stock/views.py:359 msgid "Customer must be specified" msgstr "" -#: stock/views.py:281 +#: stock/views.py:383 msgid "Return to Stock" msgstr "" -#: stock/views.py:291 +#: stock/views.py:393 msgid "Specify a valid location" msgstr "" -#: stock/views.py:302 +#: stock/views.py:404 msgid "Stock item returned from customer" msgstr "" -#: stock/views.py:313 +#: stock/views.py:415 msgid "Delete All Test Data" msgstr "" -#: stock/views.py:329 +#: stock/views.py:431 msgid "Confirm test data deletion" msgstr "" -#: stock/views.py:349 +#: stock/views.py:451 msgid "Add Test Result" msgstr "" -#: stock/views.py:390 +#: stock/views.py:492 msgid "Edit Test Result" msgstr "" -#: stock/views.py:408 +#: stock/views.py:510 msgid "Delete Test Result" msgstr "" -#: stock/views.py:420 +#: stock/views.py:522 msgid "Select Test Report Template" msgstr "" -#: stock/views.py:450 +#: stock/views.py:552 msgid "Select valid template" msgstr "" -#: stock/views.py:503 +#: stock/views.py:605 msgid "Stock Export Options" msgstr "" -#: stock/views.py:625 +#: stock/views.py:727 msgid "Stock Item QR Code" msgstr "" -#: stock/views.py:651 +#: stock/views.py:753 msgid "Install Stock Item" msgstr "" -#: stock/views.py:751 +#: stock/views.py:853 msgid "Uninstall Stock Items" msgstr "" -#: stock/views.py:859 +#: stock/views.py:961 msgid "Uninstalled stock items" msgstr "" -#: stock/views.py:884 +#: stock/views.py:986 msgid "Adjust Stock" msgstr "" -#: stock/views.py:994 +#: stock/views.py:1096 msgid "Move Stock Items" msgstr "" -#: stock/views.py:995 +#: stock/views.py:1097 msgid "Count Stock Items" msgstr "" -#: stock/views.py:996 +#: stock/views.py:1098 msgid "Remove From Stock" msgstr "" -#: stock/views.py:997 +#: stock/views.py:1099 msgid "Add Stock Items" msgstr "" -#: stock/views.py:998 +#: stock/views.py:1100 msgid "Delete Stock Items" msgstr "" -#: stock/views.py:1026 +#: stock/views.py:1128 msgid "Must enter integer value" msgstr "" -#: stock/views.py:1031 +#: stock/views.py:1133 msgid "Quantity must be positive" msgstr "" -#: stock/views.py:1038 +#: stock/views.py:1140 #, python-brace-format msgid "Quantity must not exceed {x}" msgstr "" -#: stock/views.py:1117 +#: stock/views.py:1219 #, python-brace-format msgid "Added stock to {n} items" msgstr "" -#: stock/views.py:1132 +#: stock/views.py:1234 #, python-brace-format msgid "Removed stock from {n} items" msgstr "" -#: stock/views.py:1145 +#: stock/views.py:1247 #, python-brace-format msgid "Counted stock for {n} items" msgstr "" -#: stock/views.py:1173 +#: stock/views.py:1287 msgid "No items were moved" msgstr "" -#: stock/views.py:1176 +#: stock/views.py:1290 #, python-brace-format msgid "Moved {n} items to {dest}" msgstr "" -#: stock/views.py:1195 +#: stock/views.py:1309 #, python-brace-format msgid "Deleted {n} stock items" msgstr "" -#: stock/views.py:1207 +#: stock/views.py:1321 msgid "Edit Stock Item" msgstr "" -#: stock/views.py:1298 +#: stock/views.py:1550 msgid "Serialize Stock" msgstr "" -#: stock/views.py:1392 templates/js/build.js:210 +#: stock/views.py:1644 templates/js/build.js:210 msgid "Create new Stock Item" msgstr "" -#: stock/views.py:1500 +#: stock/views.py:1787 msgid "Duplicate Stock Item" msgstr "" -#: stock/views.py:1577 +#: stock/views.py:1869 msgid "Quantity cannot be negative" msgstr "" -#: stock/views.py:1663 +#: stock/views.py:1964 msgid "Delete Stock Location" msgstr "" -#: stock/views.py:1677 +#: stock/views.py:1978 msgid "Delete Stock Item" msgstr "" -#: stock/views.py:1689 +#: stock/views.py:1990 msgid "Delete Stock Tracking Entry" msgstr "" -#: stock/views.py:1708 +#: stock/views.py:2009 msgid "Edit Stock Tracking Entry" msgstr "" -#: stock/views.py:1718 +#: stock/views.py:2019 msgid "Add Stock Tracking Entry" msgstr "" @@ -4864,23 +4889,23 @@ msgstr "" msgid "Submit Bug Report" msgstr "" -#: templates/attachment_table.html:7 +#: templates/attachment_table.html:6 msgid "Add Attachment" msgstr "" -#: templates/attachment_table.html:17 +#: templates/attachment_table.html:15 msgid "File" msgstr "" -#: templates/attachment_table.html:18 +#: templates/attachment_table.html:16 msgid "Comment" msgstr "" -#: templates/attachment_table.html:19 +#: templates/attachment_table.html:17 msgid "Uploaded" msgstr "" -#: templates/attachment_table.html:37 +#: templates/attachment_table.html:35 msgid "Delete attachment" msgstr "" @@ -5025,7 +5050,7 @@ msgstr "" msgid "Delete build output" msgstr "" -#: templates/js/build.js:209 templates/stock_table.html:13 +#: templates/js/build.js:209 templates/stock_table.html:21 msgid "New Stock Item" msgstr "" @@ -5041,7 +5066,7 @@ msgstr "" msgid "Build stock" msgstr "" -#: templates/js/build.js:582 templates/stock_table.html:26 +#: templates/js/build.js:582 templates/stock_table.html:37 msgid "Order stock" msgstr "" @@ -5223,7 +5248,7 @@ msgstr "" msgid "Order is overdue" msgstr "" -#: templates/js/order.js:193 templates/js/stock.js:816 +#: templates/js/order.js:193 templates/js/stock.js:821 msgid "Date" msgstr "" @@ -5260,7 +5285,7 @@ msgid "No parts found" msgstr "" #: templates/js/part.js:343 templates/js/stock.js:473 -#: templates/js/stock.js:1163 +#: templates/js/stock.js:1168 msgid "Select" msgstr "" @@ -5392,39 +5417,39 @@ msgstr "" msgid "Stocktake" msgstr "" -#: templates/js/stock.js:732 +#: templates/js/stock.js:737 msgid "Stock Status" msgstr "" -#: templates/js/stock.js:747 +#: templates/js/stock.js:752 msgid "Set Stock Status" msgstr "" -#: templates/js/stock.js:761 +#: templates/js/stock.js:766 msgid "Select Status Code" msgstr "" -#: templates/js/stock.js:762 +#: templates/js/stock.js:767 msgid "Status code must be selected" msgstr "" -#: templates/js/stock.js:882 +#: templates/js/stock.js:887 msgid "No user information" msgstr "" -#: templates/js/stock.js:1002 +#: templates/js/stock.js:1007 msgid "Create New Location" msgstr "" -#: templates/js/stock.js:1101 +#: templates/js/stock.js:1106 msgid "Serial" msgstr "" -#: templates/js/stock.js:1194 templates/js/table_filters.js:131 +#: templates/js/stock.js:1199 templates/js/table_filters.js:131 msgid "Installed" msgstr "" -#: templates/js/stock.js:1219 +#: templates/js/stock.js:1224 msgid "Install item" msgstr "" @@ -5593,7 +5618,7 @@ msgstr "" msgid "InvenTree server issues detected" msgstr "" -#: templates/navbar.html:63 users/models.py:27 +#: templates/navbar.html:63 users/models.py:31 msgid "Admin" msgstr "" @@ -5633,51 +5658,51 @@ msgstr "" msgid "Issues detected" msgstr "" -#: templates/stock_table.html:6 +#: templates/stock_table.html:12 msgid "Export Stock Information" msgstr "" -#: templates/stock_table.html:20 +#: templates/stock_table.html:29 msgid "Print labels" msgstr "" -#: templates/stock_table.html:22 +#: templates/stock_table.html:33 msgid "Add to selected stock items" msgstr "" -#: templates/stock_table.html:23 +#: templates/stock_table.html:34 msgid "Remove from selected stock items" msgstr "" -#: templates/stock_table.html:24 +#: templates/stock_table.html:35 msgid "Stocktake selected stock items" msgstr "" -#: templates/stock_table.html:25 +#: templates/stock_table.html:36 msgid "Move selected stock items" msgstr "" -#: templates/stock_table.html:25 +#: templates/stock_table.html:36 msgid "Move stock" msgstr "" -#: templates/stock_table.html:26 +#: templates/stock_table.html:37 msgid "Order selected items" msgstr "" -#: templates/stock_table.html:27 +#: templates/stock_table.html:38 msgid "Change status" msgstr "" -#: templates/stock_table.html:27 +#: templates/stock_table.html:38 msgid "Change stock status" msgstr "" -#: templates/stock_table.html:30 +#: templates/stock_table.html:41 msgid "Delete selected items" msgstr "" -#: templates/stock_table.html:30 +#: templates/stock_table.html:41 msgid "Delete Stock" msgstr "" @@ -5705,38 +5730,38 @@ msgstr "" msgid "Important dates" msgstr "" -#: users/models.py:142 +#: users/models.py:147 msgid "Permission set" msgstr "" -#: users/models.py:150 +#: users/models.py:155 msgid "Group" msgstr "" -#: users/models.py:153 +#: users/models.py:158 msgid "View" msgstr "" -#: users/models.py:153 +#: users/models.py:158 msgid "Permission to view items" msgstr "" -#: users/models.py:155 +#: users/models.py:160 msgid "Add" msgstr "" -#: users/models.py:155 +#: users/models.py:160 msgid "Permission to add items" msgstr "" -#: users/models.py:157 +#: users/models.py:162 msgid "Change" msgstr "" -#: users/models.py:157 +#: users/models.py:162 msgid "Permissions to edit items" msgstr "" -#: users/models.py:159 +#: users/models.py:164 msgid "Permission to delete items" msgstr "" diff --git a/InvenTree/locale/es/LC_MESSAGES/django.po b/InvenTree/locale/es/LC_MESSAGES/django.po index 7389af937b..ee26db4504 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: 2021-01-14 23:57+1100\n" +"POT-Creation-Date: 2021-01-17 18:11+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -55,7 +55,7 @@ msgid "Select Category" msgstr "" #: InvenTree/helpers.py:361 order/models.py:232 order/models.py:330 -#: stock/views.py:1573 +#: stock/views.py:1865 msgid "Invalid quantity provided" msgstr "" @@ -95,7 +95,7 @@ msgstr "" msgid "File comment" msgstr "" -#: InvenTree/models.py:68 templates/js/stock.js:873 +#: InvenTree/models.py:68 templates/js/stock.js:878 msgid "User" msgstr "" @@ -302,7 +302,7 @@ msgstr "" #: build/forms.py:78 build/templates/build/auto_allocate.html:17 #: build/templates/build/build_base.html:83 -#: build/templates/build/detail.html:29 common/models.py:596 +#: build/templates/build/detail.html:29 common/models.py:603 #: company/forms.py:112 company/templates/company/supplier_part_pricing.html:75 #: order/templates/order/order_wizard/select_parts.html:32 #: order/templates/order/purchase_order_detail.html:179 @@ -310,13 +310,13 @@ msgstr "" #: order/templates/order/sales_order_detail.html:156 #: part/templates/part/allocation.html:16 #: part/templates/part/allocation.html:49 -#: part/templates/part/sale_prices.html:82 stock/forms.py:304 -#: stock/templates/stock/item_base.html:40 -#: stock/templates/stock/item_base.html:46 -#: stock/templates/stock/item_base.html:214 +#: part/templates/part/sale_prices.html:82 stock/forms.py:306 +#: stock/templates/stock/item_base.html:51 +#: stock/templates/stock/item_base.html:57 +#: stock/templates/stock/item_base.html:234 #: stock/templates/stock/stock_adjust.html:18 templates/js/barcode.js:338 -#: templates/js/bom.js:195 templates/js/build.js:420 templates/js/stock.js:864 -#: templates/js/stock.js:1103 +#: templates/js/bom.js:195 templates/js/build.js:420 templates/js/stock.js:869 +#: templates/js/stock.js:1108 msgid "Quantity" msgstr "" @@ -324,7 +324,7 @@ msgstr "" msgid "Enter quantity for build output" msgstr "" -#: build/forms.py:83 stock/forms.py:116 +#: build/forms.py:83 stock/forms.py:117 msgid "Serial numbers" msgstr "" @@ -381,7 +381,7 @@ msgstr "" #: build/models.py:62 build/templates/build/index.html:8 #: build/templates/build/index.html:15 order/templates/order/so_builds.html:11 #: order/templates/order/so_tabs.html:9 part/templates/part/tabs.html:31 -#: templates/InvenTree/settings/tabs.html:28 users/models.py:32 +#: templates/InvenTree/settings/tabs.html:28 users/models.py:36 msgid "Build Orders" msgstr "" @@ -405,7 +405,7 @@ msgstr "" #: templates/js/bom.js:549 templates/js/build.js:664 templates/js/company.js:56 #: templates/js/order.js:180 templates/js/order.js:274 templates/js/part.js:188 #: templates/js/part.js:271 templates/js/part.js:391 templates/js/part.js:572 -#: templates/js/stock.js:511 templates/js/stock.js:845 +#: templates/js/stock.js:511 templates/js/stock.js:850 msgid "Description" msgstr "" @@ -433,7 +433,7 @@ msgstr "" #: templates/js/barcode.js:336 templates/js/bom.js:153 templates/js/bom.js:534 #: templates/js/build.js:669 templates/js/company.js:138 #: templates/js/part.js:252 templates/js/part.js:357 templates/js/stock.js:485 -#: templates/js/stock.js:1175 +#: templates/js/stock.js:1180 msgid "Part" msgstr "" @@ -491,7 +491,7 @@ msgstr "" msgid "Build status code" msgstr "" -#: build/models.py:194 stock/models.py:412 +#: build/models.py:194 stock/models.py:418 msgid "Batch Code" msgstr "" @@ -507,11 +507,11 @@ msgstr "" #: company/templates/company/supplier_part_base.html:68 #: company/templates/company/supplier_part_detail.html:24 #: part/templates/part/detail.html:80 part/templates/part/part_base.html:102 -#: stock/models.py:406 stock/templates/stock/item_base.html:297 +#: stock/models.py:412 stock/templates/stock/item_base.html:317 msgid "External Link" msgstr "" -#: build/models.py:220 part/models.py:705 stock/models.py:408 +#: build/models.py:220 part/models.py:705 stock/models.py:414 msgid "Link to external URL" msgstr "" @@ -519,10 +519,10 @@ msgstr "" #: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:18 #: order/templates/order/purchase_order_detail.html:213 #: order/templates/order/so_tabs.html:23 part/models.py:831 -#: part/templates/part/tabs.html:73 stock/forms.py:313 stock/forms.py:345 -#: stock/forms.py:373 stock/models.py:478 stock/models.py:1544 +#: part/templates/part/tabs.html:73 stock/forms.py:315 stock/forms.py:347 +#: stock/forms.py:375 stock/models.py:484 stock/models.py:1554 #: stock/templates/stock/tabs.html:26 templates/js/barcode.js:391 -#: templates/js/bom.js:295 templates/js/stock.js:127 templates/js/stock.js:618 +#: templates/js/bom.js:295 templates/js/stock.js:127 templates/js/stock.js:623 msgid "Notes" msgstr "" @@ -653,8 +653,8 @@ msgid "" "The following stock items will be allocated to the specified build output" msgstr "" -#: build/templates/build/auto_allocate.html:18 stock/forms.py:343 -#: stock/templates/stock/item_base.html:244 +#: build/templates/build/auto_allocate.html:18 stock/forms.py:345 +#: stock/templates/stock/item_base.html:264 #: stock/templates/stock/stock_adjust.html:17 #: templates/InvenTree/search.html:183 templates/js/barcode.js:337 #: templates/js/build.js:434 templates/js/stock.js:597 @@ -682,8 +682,8 @@ msgstr "" #: order/templates/order/order_base.html:26 #: order/templates/order/sales_order_base.html:35 #: part/templates/part/category.html:13 part/templates/part/part_base.html:32 -#: stock/templates/stock/item_base.html:97 -#: stock/templates/stock/location.html:12 +#: stock/templates/stock/item_base.html:114 +#: stock/templates/stock/location.html:24 msgid "Admin view" msgstr "" @@ -721,10 +721,10 @@ msgstr "" #: build/templates/build/build_base.html:88 #: build/templates/build/detail.html:57 #: order/templates/order/receive_parts.html:24 -#: stock/templates/stock/item_base.html:343 templates/InvenTree/search.html:175 +#: stock/templates/stock/item_base.html:363 templates/InvenTree/search.html:175 #: templates/js/barcode.js:42 templates/js/build.js:697 #: templates/js/order.js:185 templates/js/order.js:279 -#: templates/js/stock.js:584 templates/js/stock.js:1111 +#: templates/js/stock.js:584 templates/js/stock.js:1116 msgid "Status" msgstr "" @@ -752,7 +752,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:238 templates/js/order.js:240 +#: stock/templates/stock/item_base.html:258 templates/js/order.js:240 msgid "Sales Order" msgstr "" @@ -844,7 +844,7 @@ msgstr "" msgid "Stock can be taken from any available location." msgstr "" -#: build/templates/build/detail.html:44 stock/forms.py:371 +#: build/templates/build/detail.html:44 stock/forms.py:373 msgid "Destination" msgstr "" @@ -853,8 +853,8 @@ msgid "Destination location not specified" msgstr "" #: build/templates/build/detail.html:68 -#: stock/templates/stock/item_base.html:262 templates/js/stock.js:592 -#: templates/js/stock.js:1118 templates/js/table_filters.js:80 +#: stock/templates/stock/item_base.html:282 templates/js/stock.js:592 +#: templates/js/stock.js:1123 templates/js/table_filters.js:80 #: templates/js/table_filters.js:161 msgid "Batch" msgstr "" @@ -953,7 +953,7 @@ msgstr "" msgid "Create Build Output" msgstr "" -#: build/views.py:207 stock/models.py:887 stock/views.py:1594 +#: build/views.py:207 stock/models.py:897 stock/views.py:1886 msgid "Serial numbers already exist" msgstr "" @@ -969,7 +969,7 @@ msgstr "" msgid "Confirm unallocation of build stock" msgstr "" -#: build/views.py:303 build/views.py:388 stock/views.py:330 +#: build/views.py:303 build/views.py:388 stock/views.py:432 msgid "Check the confirmation box" msgstr "" @@ -1063,7 +1063,7 @@ msgid "Add Build Order Attachment" msgstr "" #: build/views.py:1060 order/views.py:113 order/views.py:166 part/views.py:170 -#: stock/views.py:179 +#: stock/views.py:281 msgid "Added attachment" msgstr "" @@ -1079,7 +1079,7 @@ msgstr "" msgid "Delete Attachment" msgstr "" -#: build/views.py:1123 order/views.py:242 order/views.py:257 stock/views.py:237 +#: build/views.py:1123 order/views.py:242 order/views.py:257 stock/views.py:339 msgid "Deleted attachment" msgstr "" @@ -1155,7 +1155,7 @@ msgstr "" msgid "Copy category parameter templates when creating a part" msgstr "" -#: common/models.py:115 part/templates/part/detail.html:155 stock/forms.py:255 +#: common/models.py:115 part/templates/part/detail.html:155 stock/forms.py:257 #: templates/js/table_filters.js:23 templates/js/table_filters.js:270 msgid "Template" msgstr "" @@ -1262,71 +1262,79 @@ msgid "Allow building with expired stock" msgstr "" #: common/models.py:200 -msgid "Build Order Reference Prefix" +msgid "Stock Ownership Control" msgstr "" #: common/models.py:201 -msgid "Prefix value for build order reference" -msgstr "" - -#: common/models.py:206 -msgid "Build Order Reference Regex" +msgid "Enable ownership control over stock locations and items" msgstr "" #: common/models.py:207 +msgid "Build Order Reference Prefix" +msgstr "" + +#: common/models.py:208 +msgid "Prefix value for build order reference" +msgstr "" + +#: common/models.py:213 +msgid "Build Order Reference Regex" +msgstr "" + +#: common/models.py:214 msgid "Regular expression pattern for matching build order reference" msgstr "" -#: common/models.py:211 +#: common/models.py:218 msgid "Sales Order Reference Prefix" msgstr "" -#: common/models.py:212 +#: common/models.py:219 msgid "Prefix value for sales order reference" msgstr "" -#: common/models.py:217 +#: common/models.py:224 msgid "Purchase Order Reference Prefix" msgstr "" -#: common/models.py:218 +#: common/models.py:225 msgid "Prefix value for purchase order reference" msgstr "" -#: common/models.py:441 +#: common/models.py:448 msgid "Settings key (must be unique - case insensitive" msgstr "" -#: common/models.py:443 +#: common/models.py:450 msgid "Settings value" msgstr "" -#: common/models.py:500 +#: common/models.py:507 msgid "Value must be a boolean value" msgstr "" -#: common/models.py:510 +#: common/models.py:517 msgid "Value must be an integer value" msgstr "" -#: common/models.py:524 +#: common/models.py:531 msgid "Key string must be unique" msgstr "" -#: common/models.py:597 company/forms.py:113 +#: common/models.py:604 company/forms.py:113 msgid "Price break quantity" msgstr "" -#: common/models.py:605 company/templates/company/supplier_part_pricing.html:80 +#: common/models.py:612 company/templates/company/supplier_part_pricing.html:80 #: part/templates/part/sale_prices.html:87 templates/js/bom.js:246 msgid "Price" msgstr "" -#: common/models.py:606 +#: common/models.py:613 msgid "Unit price at specified quantity" msgstr "" -#: common/models.py:629 +#: common/models.py:636 msgid "Default" msgstr "" @@ -1427,8 +1435,8 @@ msgstr "" msgid "Currency" msgstr "" -#: company/models.py:313 stock/models.py:360 -#: stock/templates/stock/item_base.html:194 +#: company/models.py:313 stock/models.py:366 +#: stock/templates/stock/item_base.html:214 msgid "Base Part" msgstr "" @@ -1441,7 +1449,7 @@ msgstr "" #: company/templates/company/supplier_part_detail.html:21 #: order/templates/order/order_base.html:89 #: order/templates/order/order_wizard/select_pos.html:30 part/bom.py:170 -#: stock/templates/stock/item_base.html:304 templates/js/company.js:48 +#: stock/templates/stock/item_base.html:324 templates/js/company.js:48 #: templates/js/company.js:164 templates/js/order.js:167 msgid "Supplier" msgstr "" @@ -1537,8 +1545,8 @@ msgid "Uses default currency" msgstr "" #: company/templates/company/detail.html:62 -#: order/templates/order/sales_order_base.html:89 stock/models.py:395 -#: stock/models.py:396 stock/templates/stock/item_base.html:221 +#: order/templates/order/sales_order_base.html:89 stock/models.py:401 +#: stock/models.py:402 stock/templates/stock/item_base.html:241 #: templates/js/company.js:40 templates/js/order.js:261 msgid "Customer" msgstr "" @@ -1554,13 +1562,13 @@ msgstr "" #: company/templates/company/detail_part.html:18 #: order/templates/order/purchase_order_detail.html:68 -#: part/templates/part/supplier.html:14 templates/js/stock.js:995 +#: part/templates/part/supplier.html:14 templates/js/stock.js:1000 msgid "New Supplier Part" msgstr "" #: company/templates/company/detail_part.html:23 #: part/templates/part/category.html:120 part/templates/part/supplier.html:17 -#: templates/stock_table.html:18 +#: templates/stock_table.html:27 msgid "Options" msgstr "" @@ -1578,7 +1586,7 @@ msgid "Delete Parts" msgstr "" #: company/templates/company/detail_part.html:63 -#: part/templates/part/category.html:116 templates/js/stock.js:989 +#: part/templates/part/category.html:116 templates/js/stock.js:994 msgid "New Part" msgstr "" @@ -1612,7 +1620,7 @@ msgstr "" #: company/templates/company/supplier_part_stock.html:33 #: part/templates/part/bom.html:63 part/templates/part/category.html:112 #: part/templates/part/category.html:126 part/templates/part/stock.html:51 -#: templates/stock_table.html:7 +#: templates/stock_table.html:13 msgid "Export" msgstr "" @@ -1635,7 +1643,7 @@ msgstr "" #: order/templates/order/purchase_orders.html:13 #: part/templates/part/orders.html:9 part/templates/part/tabs.html:48 #: templates/InvenTree/settings/tabs.html:31 templates/navbar.html:33 -#: users/models.py:33 +#: users/models.py:37 msgid "Purchase Orders" msgstr "" @@ -1655,7 +1663,7 @@ msgstr "" #: order/templates/order/sales_orders.html:13 #: part/templates/part/sales_orders.html:9 part/templates/part/tabs.html:56 #: templates/InvenTree/settings/tabs.html:34 templates/navbar.html:42 -#: users/models.py:34 +#: users/models.py:38 msgid "Sales Orders" msgstr "" @@ -1670,8 +1678,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:369 -#: stock/templates/stock/item_base.html:309 templates/js/company.js:180 +#: company/templates/company/supplier_part_base.html:19 stock/models.py:375 +#: stock/templates/stock/item_base.html:329 templates/js/company.js:180 msgid "Supplier Part" msgstr "" @@ -1712,7 +1720,7 @@ msgid "Pricing Information" msgstr "" #: company/templates/company/supplier_part_pricing.html:17 company/views.py:486 -#: part/templates/part/sale_prices.html:14 part/views.py:2565 +#: part/templates/part/sale_prices.html:14 part/views.py:2567 msgid "Add Price Break" msgstr "" @@ -1741,7 +1749,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:17 templates/InvenTree/search.html:155 +#: stock/templates/stock/location.html:29 templates/InvenTree/search.html:155 #: templates/InvenTree/settings/tabs.html:25 templates/js/part.js:192 #: templates/js/part.js:418 templates/js/stock.js:519 templates/navbar.html:22 msgid "Stock" @@ -1756,7 +1764,7 @@ msgstr "" #: part/templates/part/cat_link.html:7 part/templates/part/category.html:94 #: part/templates/part/category_tabs.html:6 #: templates/InvenTree/settings/tabs.html:22 templates/navbar.html:19 -#: templates/stats.html:35 templates/stats.html:44 users/models.py:29 +#: templates/stats.html:35 templates/stats.html:44 users/models.py:33 msgid "Parts" msgstr "" @@ -1825,7 +1833,7 @@ msgstr "" msgid "Edit Supplier Part" msgstr "" -#: company/views.py:295 templates/js/stock.js:996 +#: company/views.py:295 templates/js/stock.js:1001 msgid "Create new Supplier Part" msgstr "" @@ -1833,15 +1841,15 @@ msgstr "" msgid "Delete Supplier Part" msgstr "" -#: company/views.py:492 part/views.py:2571 +#: company/views.py:492 part/views.py:2573 msgid "Added new price break" msgstr "" -#: company/views.py:548 part/views.py:2615 +#: company/views.py:548 part/views.py:2617 msgid "Edit Price Break" msgstr "" -#: company/views.py:564 part/views.py:2631 +#: company/views.py:564 part/views.py:2633 msgid "Delete Price Break" msgstr "" @@ -1865,7 +1873,7 @@ msgstr "" msgid "Label description" msgstr "" -#: label/models.py:83 stock/forms.py:198 +#: label/models.py:83 stock/forms.py:200 msgid "Label" msgstr "" @@ -1980,8 +1988,8 @@ msgstr "" msgid "Date order was completed" msgstr "" -#: order/models.py:230 order/models.py:328 part/views.py:1504 -#: stock/models.py:259 stock/models.py:871 +#: order/models.py:230 order/models.py:328 part/views.py:1506 +#: stock/models.py:265 stock/models.py:881 msgid "Quantity must be greater than zero" msgstr "" @@ -2019,7 +2027,7 @@ msgstr "" #: order/models.py:607 order/templates/order/order_base.html:9 #: order/templates/order/order_base.html:24 -#: stock/templates/stock/item_base.html:276 templates/js/order.js:145 +#: stock/templates/stock/item_base.html:296 templates/js/order.js:145 msgid "Purchase Order" msgstr "" @@ -2031,8 +2039,8 @@ msgstr "" msgid "Number of items received" msgstr "" -#: order/models.py:630 stock/models.py:488 -#: stock/templates/stock/item_base.html:283 +#: order/models.py:630 stock/models.py:494 +#: stock/templates/stock/item_base.html:303 msgid "Purchase Price" msgstr "" @@ -2190,13 +2198,13 @@ msgstr "" #: order/templates/order/purchase_order_detail.html:39 #: order/templates/order/purchase_order_detail.html:119 #: part/templates/part/category.html:173 part/templates/part/category.html:215 -#: templates/js/stock.js:642 templates/js/stock.js:1001 +#: templates/js/stock.js:647 templates/js/stock.js:1006 msgid "New Location" msgstr "" #: order/templates/order/purchase_order_detail.html:40 #: order/templates/order/purchase_order_detail.html:120 -#: stock/templates/stock/location.html:22 +#: stock/templates/stock/location.html:35 msgid "Create new stock location" msgstr "" @@ -2275,8 +2283,8 @@ msgid "Sales Order Items" msgstr "" #: order/templates/order/sales_order_detail.html:72 -#: order/templates/order/sales_order_detail.html:154 stock/models.py:400 -#: stock/templates/stock/item_base.html:208 templates/js/build.js:418 +#: order/templates/order/sales_order_detail.html:154 stock/models.py:406 +#: stock/templates/stock/item_base.html:228 templates/js/build.js:418 msgid "Serial Number" msgstr "" @@ -2508,11 +2516,11 @@ msgstr "" msgid "Error reading BOM file (incorrect row size)" msgstr "" -#: part/forms.py:71 stock/forms.py:261 +#: part/forms.py:71 stock/forms.py:263 msgid "File Format" msgstr "" -#: part/forms.py:71 stock/forms.py:261 +#: part/forms.py:71 stock/forms.py:263 msgid "Select output file format" msgstr "" @@ -2643,7 +2651,7 @@ msgstr "" #: part/models.py:78 part/templates/part/category.html:18 #: part/templates/part/category.html:89 templates/stats.html:39 -#: users/models.py:28 +#: users/models.py:32 msgid "Part Categories" msgstr "" @@ -2917,8 +2925,8 @@ msgstr "" msgid "BOM line checksum" msgstr "" -#: part/models.py:1963 part/views.py:1510 part/views.py:1562 -#: stock/models.py:249 +#: part/models.py:1963 part/views.py:1512 part/views.py:1564 +#: stock/models.py:255 msgid "Quantity must be integer value for trackable parts" msgstr "" @@ -2954,10 +2962,10 @@ msgstr "" #: part/templates/part/allocation.html:28 #: part/templates/part/allocation.html:45 #: stock/templates/stock/item_base.html:8 -#: stock/templates/stock/item_base.html:72 -#: stock/templates/stock/item_base.html:291 +#: stock/templates/stock/item_base.html:89 +#: stock/templates/stock/item_base.html:311 #: stock/templates/stock/stock_adjust.html:16 templates/js/build.js:751 -#: templates/js/stock.js:834 templates/js/stock.js:1094 +#: templates/js/stock.js:839 templates/js/stock.js:1099 msgid "Stock Item" msgstr "" @@ -3022,7 +3030,7 @@ msgstr "" msgid "Validate" msgstr "" -#: part/templates/part/bom.html:62 part/views.py:1801 +#: part/templates/part/bom.html:62 part/views.py:1803 msgid "Export Bill of Materials" msgstr "" @@ -3122,7 +3130,7 @@ msgstr "" msgid "All parts" msgstr "" -#: part/templates/part/category.html:24 part/views.py:2192 +#: part/templates/part/category.html:24 part/views.py:2194 msgid "Create new part category" msgstr "" @@ -3174,7 +3182,7 @@ msgstr "" msgid "Export Data" msgstr "" -#: part/templates/part/category.html:174 templates/js/stock.js:643 +#: part/templates/part/category.html:174 templates/js/stock.js:648 msgid "Create new location" msgstr "" @@ -3190,7 +3198,7 @@ msgstr "" msgid "Create new Part Category" msgstr "" -#: part/templates/part/category.html:216 stock/views.py:1276 +#: part/templates/part/category.html:216 stock/views.py:1458 msgid "Create new Stock Location" msgstr "" @@ -3316,13 +3324,13 @@ msgstr "" msgid "New Parameter" msgstr "" -#: part/templates/part/params.html:25 stock/models.py:1531 +#: part/templates/part/params.html:25 stock/models.py:1541 #: templates/InvenTree/settings/header.html:8 templates/js/stock.js:123 msgid "Value" msgstr "" #: part/templates/part/params.html:41 part/templates/part/related.html:41 -#: part/templates/part/supplier.html:19 users/models.py:159 +#: part/templates/part/supplier.html:19 users/models.py:164 msgid "Delete" msgstr "" @@ -3352,20 +3360,20 @@ msgid "Star this part" msgstr "" #: part/templates/part/part_base.html:49 -#: stock/templates/stock/item_base.html:108 -#: stock/templates/stock/location.html:29 +#: stock/templates/stock/item_base.html:125 +#: stock/templates/stock/location.html:43 msgid "Barcode actions" msgstr "" #: part/templates/part/part_base.html:51 -#: stock/templates/stock/item_base.html:110 -#: stock/templates/stock/location.html:31 +#: stock/templates/stock/item_base.html:127 +#: stock/templates/stock/location.html:45 msgid "Show QR Code" msgstr "" #: part/templates/part/part_base.html:52 -#: stock/templates/stock/item_base.html:126 -#: stock/templates/stock/location.html:32 +#: stock/templates/stock/item_base.html:143 +#: stock/templates/stock/location.html:46 msgid "Print Label" msgstr "" @@ -3502,7 +3510,7 @@ msgstr "" msgid "Used In" msgstr "" -#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:349 +#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:369 msgid "Tests" msgstr "" @@ -3542,7 +3550,7 @@ msgstr "" msgid "Add part attachment" msgstr "" -#: part/views.py:209 templates/attachment_table.html:34 +#: part/views.py:209 templates/attachment_table.html:32 msgid "Edit attachment" msgstr "" @@ -3591,11 +3599,11 @@ msgstr "" msgid "Copied part" msgstr "" -#: part/views.py:529 part/views.py:667 +#: part/views.py:529 part/views.py:669 msgid "Possible matches exist - confirm creation of new part" msgstr "" -#: part/views.py:594 templates/js/stock.js:990 +#: part/views.py:594 templates/js/stock.js:995 msgid "Create New Part" msgstr "" @@ -3603,147 +3611,147 @@ msgstr "" msgid "Created new part" msgstr "" -#: part/views.py:836 +#: part/views.py:838 msgid "Part QR Code" msgstr "" -#: part/views.py:855 +#: part/views.py:857 msgid "Upload Part Image" msgstr "" -#: part/views.py:863 part/views.py:900 +#: part/views.py:865 part/views.py:902 msgid "Updated part image" msgstr "" -#: part/views.py:872 +#: part/views.py:874 msgid "Select Part Image" msgstr "" -#: part/views.py:903 +#: part/views.py:905 msgid "Part image not found" msgstr "" -#: part/views.py:914 +#: part/views.py:916 msgid "Edit Part Properties" msgstr "" -#: part/views.py:945 +#: part/views.py:947 msgid "Duplicate BOM" msgstr "" -#: part/views.py:976 +#: part/views.py:978 msgid "Confirm duplication of BOM from parent" msgstr "" -#: part/views.py:997 +#: part/views.py:999 msgid "Validate BOM" msgstr "" -#: part/views.py:1020 +#: part/views.py:1022 msgid "Confirm that the BOM is valid" msgstr "" -#: part/views.py:1031 +#: part/views.py:1033 msgid "Validated Bill of Materials" msgstr "" -#: part/views.py:1165 +#: part/views.py:1167 msgid "No BOM file provided" msgstr "" -#: part/views.py:1513 +#: part/views.py:1515 msgid "Enter a valid quantity" msgstr "" -#: part/views.py:1538 part/views.py:1541 +#: part/views.py:1540 part/views.py:1543 msgid "Select valid part" msgstr "" -#: part/views.py:1547 +#: part/views.py:1549 msgid "Duplicate part selected" msgstr "" -#: part/views.py:1585 +#: part/views.py:1587 msgid "Select a part" msgstr "" -#: part/views.py:1591 +#: part/views.py:1593 msgid "Selected part creates a circular BOM" msgstr "" -#: part/views.py:1595 +#: part/views.py:1597 msgid "Specify quantity" msgstr "" -#: part/views.py:1851 +#: part/views.py:1853 msgid "Confirm Part Deletion" msgstr "" -#: part/views.py:1860 +#: part/views.py:1862 msgid "Part was deleted" msgstr "" -#: part/views.py:1869 +#: part/views.py:1871 msgid "Part Pricing" msgstr "" -#: part/views.py:1983 +#: part/views.py:1985 msgid "Create Part Parameter Template" msgstr "" -#: part/views.py:1993 +#: part/views.py:1995 msgid "Edit Part Parameter Template" msgstr "" -#: part/views.py:2002 +#: part/views.py:2004 msgid "Delete Part Parameter Template" msgstr "" -#: part/views.py:2012 +#: part/views.py:2014 msgid "Create Part Parameter" msgstr "" -#: part/views.py:2064 +#: part/views.py:2066 msgid "Edit Part Parameter" msgstr "" -#: part/views.py:2080 +#: part/views.py:2082 msgid "Delete Part Parameter" msgstr "" -#: part/views.py:2139 +#: part/views.py:2141 msgid "Edit Part Category" msgstr "" -#: part/views.py:2176 +#: part/views.py:2178 msgid "Delete Part Category" msgstr "" -#: part/views.py:2184 +#: part/views.py:2186 msgid "Part category was deleted" msgstr "" -#: part/views.py:2240 +#: part/views.py:2242 msgid "Create Category Parameter Template" msgstr "" -#: part/views.py:2343 +#: part/views.py:2345 msgid "Edit Category Parameter Template" msgstr "" -#: part/views.py:2401 +#: part/views.py:2403 msgid "Delete Category Parameter Template" msgstr "" -#: part/views.py:2426 +#: part/views.py:2428 msgid "Create BOM Item" msgstr "" -#: part/views.py:2498 +#: part/views.py:2500 msgid "Edit BOM item" msgstr "" -#: part/views.py:2555 +#: part/views.py:2557 msgid "Confim BOM item deletion" msgstr "" @@ -3775,309 +3783,309 @@ msgstr "" msgid "Asset file description" msgstr "" -#: stock/forms.py:116 +#: stock/forms.py:117 msgid "Enter unique serial numbers (or leave blank)" msgstr "" -#: stock/forms.py:199 stock/forms.py:255 +#: stock/forms.py:201 stock/forms.py:257 msgid "Select test report template" msgstr "" -#: stock/forms.py:263 +#: stock/forms.py:265 msgid "Include stock items in sub locations" msgstr "" -#: stock/forms.py:298 +#: stock/forms.py:300 msgid "Stock item to install" msgstr "" -#: stock/forms.py:305 +#: stock/forms.py:307 msgid "Stock quantity to assign" msgstr "" -#: stock/forms.py:333 +#: stock/forms.py:335 msgid "Must not exceed available quantity" msgstr "" -#: stock/forms.py:343 +#: stock/forms.py:345 msgid "Destination location for uninstalled items" msgstr "" -#: stock/forms.py:345 +#: stock/forms.py:347 msgid "Add transaction note (optional)" msgstr "" -#: stock/forms.py:347 +#: stock/forms.py:349 msgid "Confirm uninstall" msgstr "" -#: stock/forms.py:347 +#: stock/forms.py:349 msgid "Confirm removal of installed stock items" msgstr "" -#: stock/forms.py:371 +#: stock/forms.py:373 msgid "Destination stock location" msgstr "" -#: stock/forms.py:373 +#: stock/forms.py:375 msgid "Add note (required)" msgstr "" -#: stock/forms.py:377 stock/views.py:848 stock/views.py:1046 +#: stock/forms.py:379 stock/views.py:950 stock/views.py:1148 msgid "Confirm stock adjustment" msgstr "" -#: stock/forms.py:377 +#: stock/forms.py:379 msgid "Confirm movement of stock items" msgstr "" -#: stock/forms.py:379 +#: stock/forms.py:381 msgid "Set Default Location" msgstr "" -#: stock/forms.py:379 +#: stock/forms.py:381 msgid "Set the destination as the default location for selected parts" msgstr "" -#: stock/models.py:194 +#: stock/models.py:200 msgid "Created stock item" msgstr "" -#: stock/models.py:230 +#: stock/models.py:236 msgid "StockItem with this serial number already exists" msgstr "" -#: stock/models.py:266 +#: stock/models.py:272 #, python-brace-format msgid "Part type ('{pf}') must be {pe}" msgstr "" -#: stock/models.py:276 stock/models.py:285 +#: stock/models.py:282 stock/models.py:291 msgid "Quantity must be 1 for item with a serial number" msgstr "" -#: stock/models.py:277 +#: stock/models.py:283 msgid "Serial number cannot be set if quantity greater than 1" msgstr "" -#: stock/models.py:299 +#: stock/models.py:305 msgid "Item cannot belong to itself" msgstr "" -#: stock/models.py:305 +#: stock/models.py:311 msgid "Item must have a build reference if is_building=True" msgstr "" -#: stock/models.py:312 +#: stock/models.py:318 msgid "Build reference does not point to the same part object" msgstr "" -#: stock/models.py:352 +#: stock/models.py:358 msgid "Parent Stock Item" msgstr "" -#: stock/models.py:361 +#: stock/models.py:367 msgid "Base part" msgstr "" -#: stock/models.py:370 +#: stock/models.py:376 msgid "Select a matching supplier part for this stock item" msgstr "" -#: stock/models.py:375 stock/templates/stock/stock_app_base.html:7 +#: stock/models.py:381 stock/templates/stock/stock_app_base.html:7 msgid "Stock Location" msgstr "" -#: stock/models.py:378 +#: stock/models.py:384 msgid "Where is this stock item located?" msgstr "" -#: stock/models.py:383 stock/templates/stock/item_base.html:229 +#: stock/models.py:389 stock/templates/stock/item_base.html:249 msgid "Installed In" msgstr "" -#: stock/models.py:386 +#: stock/models.py:392 msgid "Is this item installed in another item?" msgstr "" -#: stock/models.py:402 +#: stock/models.py:408 msgid "Serial number for this item" msgstr "" -#: stock/models.py:414 +#: stock/models.py:420 msgid "Batch code for this stock item" msgstr "" -#: stock/models.py:418 +#: stock/models.py:424 msgid "Stock Quantity" msgstr "" -#: stock/models.py:427 +#: stock/models.py:433 msgid "Source Build" msgstr "" -#: stock/models.py:429 +#: stock/models.py:435 msgid "Build for this stock item" msgstr "" -#: stock/models.py:440 +#: stock/models.py:446 msgid "Source Purchase Order" msgstr "" -#: stock/models.py:443 +#: stock/models.py:449 msgid "Purchase order for this stock item" msgstr "" -#: stock/models.py:449 +#: stock/models.py:455 msgid "Destination Sales Order" msgstr "" -#: stock/models.py:455 stock/templates/stock/item_base.html:316 +#: stock/models.py:461 stock/templates/stock/item_base.html:336 #: templates/js/stock.js:612 msgid "Expiry Date" msgstr "" -#: stock/models.py:456 +#: stock/models.py:462 msgid "" "Expiry date for stock item. Stock will be considered expired after this date" msgstr "" -#: stock/models.py:469 +#: stock/models.py:475 msgid "Delete this Stock Item when stock is depleted" msgstr "" -#: stock/models.py:479 stock/templates/stock/item_notes.html:14 +#: stock/models.py:485 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:495 msgid "Single unit purchase price at time of purchase" msgstr "" -#: stock/models.py:589 +#: stock/models.py:599 msgid "Assigned to Customer" msgstr "" -#: stock/models.py:591 +#: stock/models.py:601 msgid "Manually assigned to customer" msgstr "" -#: stock/models.py:604 +#: stock/models.py:614 msgid "Returned from customer" msgstr "" -#: stock/models.py:606 +#: stock/models.py:616 msgid "Returned to location" msgstr "" -#: stock/models.py:731 +#: stock/models.py:741 msgid "Installed into stock item" msgstr "" -#: stock/models.py:739 +#: stock/models.py:749 msgid "Installed stock item" msgstr "" -#: stock/models.py:763 +#: stock/models.py:773 msgid "Uninstalled stock item" msgstr "" -#: stock/models.py:782 +#: stock/models.py:792 msgid "Uninstalled into location" msgstr "" -#: stock/models.py:862 +#: stock/models.py:872 msgid "Part is not set as trackable" msgstr "" -#: stock/models.py:868 +#: stock/models.py:878 msgid "Quantity must be integer" msgstr "" -#: stock/models.py:874 +#: stock/models.py:884 #, python-brace-format msgid "Quantity must not exceed available stock quantity ({n})" msgstr "" -#: stock/models.py:877 +#: stock/models.py:887 msgid "Serial numbers must be a list of integers" msgstr "" -#: stock/models.py:880 +#: stock/models.py:890 msgid "Quantity does not match serial numbers" msgstr "" -#: stock/models.py:912 +#: stock/models.py:922 msgid "Add serial number" msgstr "" -#: stock/models.py:915 +#: stock/models.py:925 #, python-brace-format msgid "Serialized {n} items" msgstr "" -#: stock/models.py:1026 +#: stock/models.py:1036 msgid "StockItem cannot be moved as it is not in stock" msgstr "" -#: stock/models.py:1432 +#: stock/models.py:1442 msgid "Tracking entry title" msgstr "" -#: stock/models.py:1434 +#: stock/models.py:1444 msgid "Entry notes" msgstr "" -#: stock/models.py:1436 +#: stock/models.py:1446 msgid "Link to external page for further information" msgstr "" -#: stock/models.py:1496 +#: stock/models.py:1506 msgid "Value must be provided for this test" msgstr "" -#: stock/models.py:1502 +#: stock/models.py:1512 msgid "Attachment must be uploaded for this test" msgstr "" -#: stock/models.py:1519 +#: stock/models.py:1529 msgid "Test" msgstr "" -#: stock/models.py:1520 +#: stock/models.py:1530 msgid "Test name" msgstr "" -#: stock/models.py:1525 +#: stock/models.py:1535 msgid "Result" msgstr "" -#: stock/models.py:1526 templates/js/table_filters.js:172 +#: stock/models.py:1536 templates/js/table_filters.js:172 msgid "Test result" msgstr "" -#: stock/models.py:1532 +#: stock/models.py:1542 msgid "Test output value" msgstr "" -#: stock/models.py:1538 +#: stock/models.py:1548 msgid "Attachment" msgstr "" -#: stock/models.py:1539 +#: stock/models.py:1549 msgid "Test result attachment" msgstr "" -#: stock/models.py:1545 +#: stock/models.py:1555 msgid "Test notes" msgstr "" -#: stock/templates/stock/item.html:11 +#: stock/templates/stock/item.html:16 msgid "Stock Tracking Information" msgstr "" -#: stock/templates/stock/item.html:18 +#: stock/templates/stock/item.html:25 msgid "New Entry" msgstr "" @@ -4085,169 +4093,175 @@ msgstr "" msgid "Stock Item Attachments" msgstr "" -#: stock/templates/stock/item_base.html:20 +#: stock/templates/stock/item_base.html:24 +msgid "" +"You are not in the list of owners of this item. This stock item cannot be " +"edited." +msgstr "" + +#: stock/templates/stock/item_base.html:31 msgid "This stock item is in production and cannot be edited." msgstr "" -#: stock/templates/stock/item_base.html:21 +#: stock/templates/stock/item_base.html:32 msgid "Edit the stock item from the build view." msgstr "" -#: stock/templates/stock/item_base.html:34 +#: stock/templates/stock/item_base.html:45 msgid "This stock item has not passed all required tests" msgstr "" -#: stock/templates/stock/item_base.html:40 +#: stock/templates/stock/item_base.html:51 msgid "This stock item is allocated to Sales Order" msgstr "" -#: stock/templates/stock/item_base.html:46 +#: stock/templates/stock/item_base.html:57 msgid "This stock item is allocated to Build" msgstr "" -#: stock/templates/stock/item_base.html:52 +#: stock/templates/stock/item_base.html:63 msgid "" "This stock item is serialized - it has a unique serial number and the " "quantity cannot be adjusted." msgstr "" -#: stock/templates/stock/item_base.html:56 +#: stock/templates/stock/item_base.html:67 msgid "This stock item cannot be deleted as it has child items" msgstr "" -#: stock/templates/stock/item_base.html:60 +#: stock/templates/stock/item_base.html:71 msgid "" "This stock item will be automatically deleted when all stock is depleted." msgstr "" -#: stock/templates/stock/item_base.html:74 -#: stock/templates/stock/item_base.html:320 templates/js/table_filters.js:111 +#: stock/templates/stock/item_base.html:91 +#: stock/templates/stock/item_base.html:340 templates/js/table_filters.js:111 msgid "Expired" msgstr "" -#: stock/templates/stock/item_base.html:78 -#: stock/templates/stock/item_base.html:322 templates/js/table_filters.js:116 +#: stock/templates/stock/item_base.html:95 +#: stock/templates/stock/item_base.html:342 templates/js/table_filters.js:116 msgid "Stale" msgstr "" -#: stock/templates/stock/item_base.html:113 templates/js/barcode.js:283 +#: stock/templates/stock/item_base.html:130 templates/js/barcode.js:283 #: templates/js/barcode.js:288 msgid "Unlink Barcode" msgstr "" -#: stock/templates/stock/item_base.html:115 +#: stock/templates/stock/item_base.html:132 msgid "Link Barcode" msgstr "" -#: stock/templates/stock/item_base.html:123 +#: stock/templates/stock/item_base.html:140 msgid "Document actions" msgstr "" -#: stock/templates/stock/item_base.html:129 +#: stock/templates/stock/item_base.html:146 #: stock/templates/stock/item_tests.html:25 msgid "Test Report" msgstr "" -#: stock/templates/stock/item_base.html:137 +#: stock/templates/stock/item_base.html:156 msgid "Stock adjustment actions" msgstr "" -#: stock/templates/stock/item_base.html:141 -#: stock/templates/stock/location.html:41 templates/stock_table.html:24 +#: stock/templates/stock/item_base.html:160 +#: stock/templates/stock/location.html:57 templates/stock_table.html:35 msgid "Count stock" msgstr "" -#: stock/templates/stock/item_base.html:142 templates/stock_table.html:22 +#: stock/templates/stock/item_base.html:161 templates/stock_table.html:33 msgid "Add stock" msgstr "" -#: stock/templates/stock/item_base.html:143 templates/stock_table.html:23 +#: stock/templates/stock/item_base.html:162 templates/stock_table.html:34 msgid "Remove stock" msgstr "" -#: stock/templates/stock/item_base.html:145 +#: stock/templates/stock/item_base.html:164 msgid "Transfer stock" msgstr "" -#: stock/templates/stock/item_base.html:147 +#: stock/templates/stock/item_base.html:166 msgid "Serialize stock" msgstr "" -#: stock/templates/stock/item_base.html:151 +#: stock/templates/stock/item_base.html:170 msgid "Assign to customer" msgstr "" -#: stock/templates/stock/item_base.html:154 +#: stock/templates/stock/item_base.html:173 msgid "Return to stock" msgstr "" -#: stock/templates/stock/item_base.html:158 templates/js/stock.js:1131 +#: stock/templates/stock/item_base.html:177 templates/js/stock.js:1136 msgid "Uninstall stock item" msgstr "" -#: stock/templates/stock/item_base.html:158 +#: stock/templates/stock/item_base.html:177 msgid "Uninstall" msgstr "" -#: stock/templates/stock/item_base.html:167 -#: stock/templates/stock/location.html:38 +#: stock/templates/stock/item_base.html:186 +#: stock/templates/stock/location.html:54 msgid "Stock actions" msgstr "" -#: stock/templates/stock/item_base.html:170 +#: stock/templates/stock/item_base.html:189 msgid "Convert to variant" msgstr "" -#: stock/templates/stock/item_base.html:173 +#: stock/templates/stock/item_base.html:192 msgid "Duplicate stock item" msgstr "" -#: stock/templates/stock/item_base.html:175 +#: stock/templates/stock/item_base.html:194 msgid "Edit stock item" msgstr "" -#: stock/templates/stock/item_base.html:178 +#: stock/templates/stock/item_base.html:197 msgid "Delete stock item" msgstr "" -#: stock/templates/stock/item_base.html:189 +#: stock/templates/stock/item_base.html:209 msgid "Stock Item Details" msgstr "" -#: stock/templates/stock/item_base.html:248 templates/js/build.js:442 +#: stock/templates/stock/item_base.html:268 templates/js/build.js:442 msgid "No location set" msgstr "" -#: stock/templates/stock/item_base.html:255 +#: stock/templates/stock/item_base.html:275 msgid "Barcode Identifier" msgstr "" -#: stock/templates/stock/item_base.html:269 templates/js/build.js:642 +#: stock/templates/stock/item_base.html:289 templates/js/build.js:642 #: templates/navbar.html:25 msgid "Build" msgstr "" -#: stock/templates/stock/item_base.html:290 +#: stock/templates/stock/item_base.html:310 msgid "Parent Item" msgstr "" -#: stock/templates/stock/item_base.html:320 +#: stock/templates/stock/item_base.html:340 msgid "This StockItem expired on" msgstr "" -#: stock/templates/stock/item_base.html:322 +#: stock/templates/stock/item_base.html:342 msgid "This StockItem expires on" msgstr "" -#: stock/templates/stock/item_base.html:329 +#: stock/templates/stock/item_base.html:349 templates/js/stock.js:618 msgid "Last Updated" msgstr "" -#: stock/templates/stock/item_base.html:334 +#: stock/templates/stock/item_base.html:354 msgid "Last Stocktake" msgstr "" -#: stock/templates/stock/item_base.html:338 +#: stock/templates/stock/item_base.html:358 msgid "No stocktake performed" msgstr "" @@ -4303,56 +4317,62 @@ msgstr "" msgid "Add Test Data" msgstr "" -#: stock/templates/stock/location.html:18 +#: stock/templates/stock/location.html:13 +msgid "" +"You are not in the list of owners of this location. This stock location " +"cannot be edited." +msgstr "" + +#: stock/templates/stock/location.html:30 msgid "All stock items" msgstr "" -#: stock/templates/stock/location.html:33 +#: stock/templates/stock/location.html:47 msgid "Check-in Items" msgstr "" -#: stock/templates/stock/location.html:47 +#: stock/templates/stock/location.html:63 msgid "Location actions" msgstr "" -#: stock/templates/stock/location.html:49 +#: stock/templates/stock/location.html:65 msgid "Edit location" msgstr "" -#: stock/templates/stock/location.html:51 +#: stock/templates/stock/location.html:67 msgid "Delete location" msgstr "" -#: stock/templates/stock/location.html:61 +#: stock/templates/stock/location.html:78 msgid "Location Details" msgstr "" -#: stock/templates/stock/location.html:66 +#: stock/templates/stock/location.html:83 msgid "Location Path" msgstr "" -#: stock/templates/stock/location.html:71 +#: stock/templates/stock/location.html:88 msgid "Location Description" msgstr "" -#: stock/templates/stock/location.html:76 +#: stock/templates/stock/location.html:93 msgid "Sublocations" msgstr "" -#: stock/templates/stock/location.html:81 -#: stock/templates/stock/location.html:96 +#: stock/templates/stock/location.html:98 +#: stock/templates/stock/location.html:113 #: templates/InvenTree/search_stock_items.html:6 templates/stats.html:48 -#: templates/stats.html:57 users/models.py:31 +#: templates/stats.html:57 users/models.py:35 msgid "Stock Items" msgstr "" -#: stock/templates/stock/location.html:86 +#: stock/templates/stock/location.html:103 msgid "Stock Details" msgstr "" -#: stock/templates/stock/location.html:91 +#: stock/templates/stock/location.html:108 #: templates/InvenTree/search_stock_location.html:6 templates/stats.html:52 -#: users/models.py:30 +#: users/models.py:34 msgid "Stock Locations" msgstr "" @@ -4364,7 +4384,7 @@ msgstr "" msgid "The following stock items will be uninstalled" msgstr "" -#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1248 +#: stock/templates/stock/stockitem_convert.html:7 stock/views.py:1430 msgid "Convert Stock Item" msgstr "" @@ -4396,197 +4416,202 @@ msgstr "" msgid "Installed Items" msgstr "" -#: stock/views.py:122 +#: stock/views.py:126 msgid "Edit Stock Location" msgstr "" -#: stock/views.py:147 +#: stock/views.py:234 stock/views.py:1420 stock/views.py:1533 +#: stock/views.py:1895 +msgid "Owner is required (ownership control is enabled)" +msgstr "" + +#: stock/views.py:249 msgid "Stock Location QR code" msgstr "" -#: stock/views.py:166 +#: stock/views.py:268 msgid "Add Stock Item Attachment" msgstr "" -#: stock/views.py:213 +#: stock/views.py:315 msgid "Edit Stock Item Attachment" msgstr "" -#: stock/views.py:230 +#: stock/views.py:332 msgid "Delete Stock Item Attachment" msgstr "" -#: stock/views.py:247 +#: stock/views.py:349 msgid "Assign to Customer" msgstr "" -#: stock/views.py:257 +#: stock/views.py:359 msgid "Customer must be specified" msgstr "" -#: stock/views.py:281 +#: stock/views.py:383 msgid "Return to Stock" msgstr "" -#: stock/views.py:291 +#: stock/views.py:393 msgid "Specify a valid location" msgstr "" -#: stock/views.py:302 +#: stock/views.py:404 msgid "Stock item returned from customer" msgstr "" -#: stock/views.py:313 +#: stock/views.py:415 msgid "Delete All Test Data" msgstr "" -#: stock/views.py:329 +#: stock/views.py:431 msgid "Confirm test data deletion" msgstr "" -#: stock/views.py:349 +#: stock/views.py:451 msgid "Add Test Result" msgstr "" -#: stock/views.py:390 +#: stock/views.py:492 msgid "Edit Test Result" msgstr "" -#: stock/views.py:408 +#: stock/views.py:510 msgid "Delete Test Result" msgstr "" -#: stock/views.py:420 +#: stock/views.py:522 msgid "Select Test Report Template" msgstr "" -#: stock/views.py:450 +#: stock/views.py:552 msgid "Select valid template" msgstr "" -#: stock/views.py:503 +#: stock/views.py:605 msgid "Stock Export Options" msgstr "" -#: stock/views.py:625 +#: stock/views.py:727 msgid "Stock Item QR Code" msgstr "" -#: stock/views.py:651 +#: stock/views.py:753 msgid "Install Stock Item" msgstr "" -#: stock/views.py:751 +#: stock/views.py:853 msgid "Uninstall Stock Items" msgstr "" -#: stock/views.py:859 +#: stock/views.py:961 msgid "Uninstalled stock items" msgstr "" -#: stock/views.py:884 +#: stock/views.py:986 msgid "Adjust Stock" msgstr "" -#: stock/views.py:994 +#: stock/views.py:1096 msgid "Move Stock Items" msgstr "" -#: stock/views.py:995 +#: stock/views.py:1097 msgid "Count Stock Items" msgstr "" -#: stock/views.py:996 +#: stock/views.py:1098 msgid "Remove From Stock" msgstr "" -#: stock/views.py:997 +#: stock/views.py:1099 msgid "Add Stock Items" msgstr "" -#: stock/views.py:998 +#: stock/views.py:1100 msgid "Delete Stock Items" msgstr "" -#: stock/views.py:1026 +#: stock/views.py:1128 msgid "Must enter integer value" msgstr "" -#: stock/views.py:1031 +#: stock/views.py:1133 msgid "Quantity must be positive" msgstr "" -#: stock/views.py:1038 +#: stock/views.py:1140 #, python-brace-format msgid "Quantity must not exceed {x}" msgstr "" -#: stock/views.py:1117 +#: stock/views.py:1219 #, python-brace-format msgid "Added stock to {n} items" msgstr "" -#: stock/views.py:1132 +#: stock/views.py:1234 #, python-brace-format msgid "Removed stock from {n} items" msgstr "" -#: stock/views.py:1145 +#: stock/views.py:1247 #, python-brace-format msgid "Counted stock for {n} items" msgstr "" -#: stock/views.py:1173 +#: stock/views.py:1287 msgid "No items were moved" msgstr "" -#: stock/views.py:1176 +#: stock/views.py:1290 #, python-brace-format msgid "Moved {n} items to {dest}" msgstr "" -#: stock/views.py:1195 +#: stock/views.py:1309 #, python-brace-format msgid "Deleted {n} stock items" msgstr "" -#: stock/views.py:1207 +#: stock/views.py:1321 msgid "Edit Stock Item" msgstr "" -#: stock/views.py:1298 +#: stock/views.py:1550 msgid "Serialize Stock" msgstr "" -#: stock/views.py:1392 templates/js/build.js:210 +#: stock/views.py:1644 templates/js/build.js:210 msgid "Create new Stock Item" msgstr "" -#: stock/views.py:1500 +#: stock/views.py:1787 msgid "Duplicate Stock Item" msgstr "" -#: stock/views.py:1577 +#: stock/views.py:1869 msgid "Quantity cannot be negative" msgstr "" -#: stock/views.py:1663 +#: stock/views.py:1964 msgid "Delete Stock Location" msgstr "" -#: stock/views.py:1677 +#: stock/views.py:1978 msgid "Delete Stock Item" msgstr "" -#: stock/views.py:1689 +#: stock/views.py:1990 msgid "Delete Stock Tracking Entry" msgstr "" -#: stock/views.py:1708 +#: stock/views.py:2009 msgid "Edit Stock Tracking Entry" msgstr "" -#: stock/views.py:1718 +#: stock/views.py:2019 msgid "Add Stock Tracking Entry" msgstr "" @@ -4864,23 +4889,23 @@ msgstr "" msgid "Submit Bug Report" msgstr "" -#: templates/attachment_table.html:7 +#: templates/attachment_table.html:6 msgid "Add Attachment" msgstr "" -#: templates/attachment_table.html:17 +#: templates/attachment_table.html:15 msgid "File" msgstr "" -#: templates/attachment_table.html:18 +#: templates/attachment_table.html:16 msgid "Comment" msgstr "" -#: templates/attachment_table.html:19 +#: templates/attachment_table.html:17 msgid "Uploaded" msgstr "" -#: templates/attachment_table.html:37 +#: templates/attachment_table.html:35 msgid "Delete attachment" msgstr "" @@ -5025,7 +5050,7 @@ msgstr "" msgid "Delete build output" msgstr "" -#: templates/js/build.js:209 templates/stock_table.html:13 +#: templates/js/build.js:209 templates/stock_table.html:21 msgid "New Stock Item" msgstr "" @@ -5041,7 +5066,7 @@ msgstr "" msgid "Build stock" msgstr "" -#: templates/js/build.js:582 templates/stock_table.html:26 +#: templates/js/build.js:582 templates/stock_table.html:37 msgid "Order stock" msgstr "" @@ -5223,7 +5248,7 @@ msgstr "" msgid "Order is overdue" msgstr "" -#: templates/js/order.js:193 templates/js/stock.js:816 +#: templates/js/order.js:193 templates/js/stock.js:821 msgid "Date" msgstr "" @@ -5260,7 +5285,7 @@ msgid "No parts found" msgstr "" #: templates/js/part.js:343 templates/js/stock.js:473 -#: templates/js/stock.js:1163 +#: templates/js/stock.js:1168 msgid "Select" msgstr "" @@ -5392,39 +5417,39 @@ msgstr "" msgid "Stocktake" msgstr "" -#: templates/js/stock.js:732 +#: templates/js/stock.js:737 msgid "Stock Status" msgstr "" -#: templates/js/stock.js:747 +#: templates/js/stock.js:752 msgid "Set Stock Status" msgstr "" -#: templates/js/stock.js:761 +#: templates/js/stock.js:766 msgid "Select Status Code" msgstr "" -#: templates/js/stock.js:762 +#: templates/js/stock.js:767 msgid "Status code must be selected" msgstr "" -#: templates/js/stock.js:882 +#: templates/js/stock.js:887 msgid "No user information" msgstr "" -#: templates/js/stock.js:1002 +#: templates/js/stock.js:1007 msgid "Create New Location" msgstr "" -#: templates/js/stock.js:1101 +#: templates/js/stock.js:1106 msgid "Serial" msgstr "" -#: templates/js/stock.js:1194 templates/js/table_filters.js:131 +#: templates/js/stock.js:1199 templates/js/table_filters.js:131 msgid "Installed" msgstr "" -#: templates/js/stock.js:1219 +#: templates/js/stock.js:1224 msgid "Install item" msgstr "" @@ -5593,7 +5618,7 @@ msgstr "" msgid "InvenTree server issues detected" msgstr "" -#: templates/navbar.html:63 users/models.py:27 +#: templates/navbar.html:63 users/models.py:31 msgid "Admin" msgstr "" @@ -5633,51 +5658,51 @@ msgstr "" msgid "Issues detected" msgstr "" -#: templates/stock_table.html:6 +#: templates/stock_table.html:12 msgid "Export Stock Information" msgstr "" -#: templates/stock_table.html:20 +#: templates/stock_table.html:29 msgid "Print labels" msgstr "" -#: templates/stock_table.html:22 +#: templates/stock_table.html:33 msgid "Add to selected stock items" msgstr "" -#: templates/stock_table.html:23 +#: templates/stock_table.html:34 msgid "Remove from selected stock items" msgstr "" -#: templates/stock_table.html:24 +#: templates/stock_table.html:35 msgid "Stocktake selected stock items" msgstr "" -#: templates/stock_table.html:25 +#: templates/stock_table.html:36 msgid "Move selected stock items" msgstr "" -#: templates/stock_table.html:25 +#: templates/stock_table.html:36 msgid "Move stock" msgstr "" -#: templates/stock_table.html:26 +#: templates/stock_table.html:37 msgid "Order selected items" msgstr "" -#: templates/stock_table.html:27 +#: templates/stock_table.html:38 msgid "Change status" msgstr "" -#: templates/stock_table.html:27 +#: templates/stock_table.html:38 msgid "Change stock status" msgstr "" -#: templates/stock_table.html:30 +#: templates/stock_table.html:41 msgid "Delete selected items" msgstr "" -#: templates/stock_table.html:30 +#: templates/stock_table.html:41 msgid "Delete Stock" msgstr "" @@ -5705,38 +5730,38 @@ msgstr "" msgid "Important dates" msgstr "" -#: users/models.py:142 +#: users/models.py:147 msgid "Permission set" msgstr "" -#: users/models.py:150 +#: users/models.py:155 msgid "Group" msgstr "" -#: users/models.py:153 +#: users/models.py:158 msgid "View" msgstr "" -#: users/models.py:153 +#: users/models.py:158 msgid "Permission to view items" msgstr "" -#: users/models.py:155 +#: users/models.py:160 msgid "Add" msgstr "" -#: users/models.py:155 +#: users/models.py:160 msgid "Permission to add items" msgstr "" -#: users/models.py:157 +#: users/models.py:162 msgid "Change" msgstr "" -#: users/models.py:157 +#: users/models.py:162 msgid "Permissions to edit items" msgstr "" -#: users/models.py:159 +#: users/models.py:164 msgid "Permission to delete items" msgstr "" diff --git a/InvenTree/part/templatetags/inventree_extras.py b/InvenTree/part/templatetags/inventree_extras.py index 9692e208e2..c0dc15c2b0 100644 --- a/InvenTree/part/templatetags/inventree_extras.py +++ b/InvenTree/part/templatetags/inventree_extras.py @@ -145,3 +145,22 @@ def get_color_theme_css(username): inventree_css_static_url = os.path.join(settings.STATIC_URL, inventree_css_sheet) return inventree_css_static_url + + +@register.simple_tag() +def authorized_owners(group): + """ Return authorized owners """ + + owners = [] + + try: + for owner in group.get_related_owners(include_group=True): + owners.append(owner.owner) + except AttributeError: + # group is None + pass + except TypeError: + # group.get_users returns None + pass + + return owners diff --git a/InvenTree/script/translate.py b/InvenTree/script/translate.py index 407acb93c8..3a08c0b410 100644 --- a/InvenTree/script/translate.py +++ b/InvenTree/script/translate.py @@ -28,8 +28,10 @@ def manually_translate_file(filename, save=False): print("For each missing translation:") print("a) Directly enter a new tranlation in the target language") print("b) Leave empty to skip") + print("c) Press Ctrl+C to exit") - input("Press to continue") + print("-------------------------") + input("Press to start") print("") with open(filename, 'r') as f: @@ -58,7 +60,10 @@ def manually_translate_file(filename, save=False): print("Source:", source_line) print("Enter translation for {t}".format(t=msgid)) - translation = str(input(">")) + try: + translation = str(input(">")) + except KeyboardInterrupt: + break if translation and len(translation) > 0: # Update the line with the new translation @@ -71,7 +76,7 @@ def manually_translate_file(filename, save=False): output_file.writelines(out) print("Translation done: written to", filename) - print("Run 'make translate' to rebuild translation data") + print("Run 'invoke translate' to rebuild translation data") if __name__ == '__main__': diff --git a/InvenTree/script/translation_stats.py b/InvenTree/script/translation_stats.py new file mode 100644 index 0000000000..0ba969479b --- /dev/null +++ b/InvenTree/script/translation_stats.py @@ -0,0 +1,68 @@ +""" +This script calculates translation coverage for various languages +""" + +import os + + +def calculate_coverage(filename): + """ + Calculate translation coverage for a .po file + """ + + with open(filename, 'r') as f: + lines = f.readlines() + + lines_count = 0 + lines_covered = 0 + lines_uncovered = 0 + + for line in lines: + + if line.startswith("msgid "): + lines_count += 1 + + elif line.startswith("msgstr"): + if line.startswith('msgstr ""') or line.startswith("msgstr ''"): + lines_uncovered += 1 + else: + lines_covered += 1 + + # Return stats for the file + return (lines_count, lines_covered, lines_uncovered) + + +if __name__ == '__main__': + + MY_DIR = os.path.dirname(os.path.realpath(__file__)) + LC_DIR = os.path.abspath(os.path.join(MY_DIR, '..', 'locale')) + + locales = {} + + print("InvenTree translation coverage:") + + for locale in os.listdir(LC_DIR): + path = os.path.join(LC_DIR, locale) + if os.path.exists(path) and os.path.isdir(path): + + locale_file = os.path.join(path, 'LC_MESSAGES', 'django.po') + + if os.path.exists(locale_file) and os.path.isfile(locale_file): + locales[locale] = locale_file + + print("-" * 16) + + for locale in locales.keys(): + locale_file = locales[locale] + stats = calculate_coverage(locale_file) + + (total, covered, uncovered) = stats + + if total > 0: + percentage = int(covered / total * 100) + else: + percentage = 0 + + print(f"| {locale.ljust(4, ' ')} : {str(percentage).rjust(4, ' ')}% |") + + print("-" * 16) diff --git a/InvenTree/stock/fixtures/stock.yaml b/InvenTree/stock/fixtures/stock.yaml index 45a5f5dd7f..00d3920205 100644 --- a/InvenTree/stock/fixtures/stock.yaml +++ b/InvenTree/stock/fixtures/stock.yaml @@ -25,6 +25,18 @@ lft: 0 rght: 0 +# Capacitor C_22N_0805 in 'Office' +- model: stock.stockitem + pk: 11 + fields: + part: 5 + location: 4 + quantity: 666 + level: 0 + tree_id: 0 + lft: 0 + rght: 0 + # 1234 2K2 resistors in 'Drawer_1' - model: stock.stockitem pk: 1234 diff --git a/InvenTree/stock/forms.py b/InvenTree/stock/forms.py index ec7cbf7805..7659981ecd 100644 --- a/InvenTree/stock/forms.py +++ b/InvenTree/stock/forms.py @@ -90,7 +90,8 @@ class EditStockLocationForm(HelperForm): fields = [ 'name', 'parent', - 'description' + 'description', + 'owner', ] @@ -138,6 +139,7 @@ class CreateStockItemForm(HelperForm): 'link', 'delete_on_deplete', 'status', + 'owner', ] # Custom clean to prevent complex StockItem.clean() logic from running (yet) @@ -414,6 +416,7 @@ class EditStockItemForm(HelperForm): 'purchase_price', 'link', 'delete_on_deplete', + 'owner', ] diff --git a/InvenTree/stock/migrations/0057_stock_location_item_owner.py b/InvenTree/stock/migrations/0057_stock_location_item_owner.py new file mode 100644 index 0000000000..1c2e74341a --- /dev/null +++ b/InvenTree/stock/migrations/0057_stock_location_item_owner.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.7 on 2021-01-11 21:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('users', '0005_owner_model'), + ('stock', '0056_stockitem_expiry_date'), + ] + + operations = [ + migrations.AddField( + model_name='stockitem', + name='owner', + field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_items', to='users.Owner'), + ), + migrations.AddField( + model_name='stocklocation', + name='owner', + field=models.ForeignKey(blank=True, help_text='Select Owner', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='stock_locations', to='users.Owner'), + ), + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 0fa4247cd1..78895b54e3 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -38,6 +38,8 @@ from InvenTree.status_codes import StockStatus from InvenTree.models import InvenTreeTree, InvenTreeAttachment from InvenTree.fields import InvenTreeURLField +from users.models import Owner + from company import models as CompanyModels from part import models as PartModels @@ -48,6 +50,10 @@ class StockLocation(InvenTreeTree): Stock locations can be heirarchical as required """ + owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, blank=True, null=True, + help_text='Select Owner', + related_name='stock_locations') + def get_absolute_url(self): return reverse('stock-location-detail', kwargs={'pk': self.id}) @@ -489,6 +495,10 @@ class StockItem(MPTTModel): help_text=_('Single unit purchase price at time of purchase'), ) + owner = models.ForeignKey(Owner, on_delete=models.SET_NULL, blank=True, null=True, + help_text='Select Owner', + related_name='stock_items') + def is_stale(self): """ Returns True if this Stock item is "stale". diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index 8f8502af2a..74fe8acdcd 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -8,17 +8,25 @@ {% include "stock/tabs.html" with tab="tracking" %} +{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} +{% if owner_control.value == "True" %} + {% authorized_owners item.owner as owners %} +{% endif %} +

{% trans "Stock Tracking Information" %}


-{% if roles.stock.change %} -
-
- + +{% if owner_control.value == "False" or owner_control.value == "True" and user in owners %} + {% if roles.stock.change and not item.is_building %} +
+
+ +
-
+ {% endif %} {% endif %}
diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 16b34cb3ea..feb64cd41d 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -15,6 +15,17 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} {% block pre_content %} {% include 'stock/loc_link.html' with location=item.location %} +{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} +{% if owner_control.value == "True" %} + {% authorized_owners item.owner as owners %} + + {% if not user in owners and not user.is_superuser %} +
+ {% trans "You are not in the list of owners of this item. This stock item cannot be edited." %}
+
+ {% endif %} +{% endif %} + {% if item.is_building %}
{% trans "This stock item is in production and cannot be edited." %}
@@ -68,6 +79,12 @@ InvenTree | {% trans "Stock Item" %} - {{ item }} {% endblock %} {% block page_data %} + +{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} +{% if owner_control.value == "True" %} + {% authorized_owners item.owner as owners %} +{% endif %} +

{% trans "Stock Item" %} {% if item.is_expired %} @@ -132,54 +149,57 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}

{% endif %} - {% if roles.stock.change and not item.is_building %} -
- - -
- {% endif %} - - {% if roles.stock.change and not item.is_building %} -
- - +
+ {% endif %} + + {% if roles.stock.change and not item.is_building %} +
+ + +
+ {% endif %} {% endif %}
diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index d613a26b22..f3501a50cf 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -1,8 +1,20 @@ {% extends "stock/stock_app_base.html" %} {% load static %} +{% load inventree_extras %} {% load i18n %} {% block content %} +{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} +{% if owner_control.value == "True" %} + {% authorized_owners location.owner as owners %} + + {% if location and not user in owners and not user.is_superuser %} +
+ {% trans "You are not in the list of owners of this location. This stock location cannot be edited." %}
+
+ {% endif %} +{% endif %} +
{% if location %} @@ -18,11 +30,13 @@

{% trans "All stock items" %}

{% endif %}
- {% if roles.stock_location.add %} - - {% endif %} + {% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser or not location %} + {% if roles.stock_location.add %} + + {% endif %} + {% endif %} {% if location %}
@@ -33,25 +47,28 @@
  • {% trans "Check-in Items" %}
  • - {% if roles.stock.change %} - - {% endif %} - {% if roles.stock_location.change %} -
    - - -
    + + {% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %} + {% if roles.stock.change %} + + {% endif %} + {% if roles.stock_location.change %} +
    + + +
    + {% endif %} {% endif %} {% endif %}
    diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 9b3e926456..c8f6cc77b8 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -117,7 +117,7 @@ class StockItemListTest(StockAPITestCase): response = self.get_stock() - self.assertEqual(len(response), 19) + self.assertEqual(len(response), 20) def test_filter_by_part(self): """ @@ -126,7 +126,7 @@ class StockItemListTest(StockAPITestCase): response = self.get_stock(part=25) - self.assertEqual(len(response), 7) + self.assertEqual(len(response), 8) response = self.get_stock(part=10004) @@ -166,7 +166,7 @@ class StockItemListTest(StockAPITestCase): self.assertEqual(len(response), 1) response = self.get_stock(depleted=0) - self.assertEqual(len(response), 18) + self.assertEqual(len(response), 19) def test_filter_by_in_stock(self): """ @@ -174,7 +174,7 @@ class StockItemListTest(StockAPITestCase): """ response = self.get_stock(in_stock=1) - self.assertEqual(len(response), 16) + self.assertEqual(len(response), 17) response = self.get_stock(in_stock=0) self.assertEqual(len(response), 3) @@ -185,7 +185,7 @@ class StockItemListTest(StockAPITestCase): """ codes = { - StockStatus.OK: 17, + StockStatus.OK: 18, StockStatus.DESTROYED: 1, StockStatus.LOST: 1, StockStatus.DAMAGED: 0, @@ -218,7 +218,7 @@ class StockItemListTest(StockAPITestCase): self.assertIsNotNone(item['serial']) response = self.get_stock(serialized=0) - self.assertEqual(len(response), 7) + self.assertEqual(len(response), 8) for item in response: self.assertIsNone(item['serial']) @@ -230,7 +230,7 @@ class StockItemListTest(StockAPITestCase): # First, we can assume that the 'stock expiry' feature is disabled response = self.get_stock(expired=1) - self.assertEqual(len(response), 19) + self.assertEqual(len(response), 20) # Now, ensure that the expiry date feature is enabled! InvenTreeSetting.set_setting('STOCK_ENABLE_EXPIRY', True, self.user) @@ -242,7 +242,7 @@ class StockItemListTest(StockAPITestCase): self.assertTrue(item['expired']) response = self.get_stock(expired=0) - self.assertEqual(len(response), 18) + self.assertEqual(len(response), 19) for item in response: self.assertFalse(item['expired']) @@ -259,7 +259,7 @@ class StockItemListTest(StockAPITestCase): self.assertEqual(len(response), 4) response = self.get_stock(expired=0) - self.assertEqual(len(response), 15) + self.assertEqual(len(response), 16) class StockItemTest(StockAPITestCase): diff --git a/InvenTree/stock/test_views.py b/InvenTree/stock/test_views.py index b842de3836..8789e9f76e 100644 --- a/InvenTree/stock/test_views.py +++ b/InvenTree/stock/test_views.py @@ -10,6 +10,8 @@ from common.models import InvenTreeSetting import json from datetime import datetime, timedelta +from InvenTree.status_codes import StockStatus + class StockViewTestCase(TestCase): @@ -230,3 +232,184 @@ class StockItemTest(StockViewTestCase): self.assertEqual(response.status_code, 200) data = json.loads(response.content) self.assertFalse(data['form_valid']) + + +class StockOwnershipTest(StockViewTestCase): + """ Tests for stock ownership views """ + + def setUp(self): + """ Add another user for ownership tests """ + + super().setUp() + + # Promote existing user with staff, admin and superuser statuses + self.user.is_staff = True + self.user.is_admin = True + self.user.is_superuser = True + self.user.save() + + # Create a new user + user = get_user_model() + + self.new_user = user.objects.create_user( + username='john', + email='john@email.com', + password='custom123', + ) + + # Put the user into a new group with the correct permissions + group = Group.objects.create(name='new_group') + self.new_user.groups.add(group) + + # Give the group *all* the permissions! + for rule in group.rule_sets.all(): + rule.can_view = True + rule.can_change = True + rule.can_add = True + rule.can_delete = True + + rule.save() + + def enable_ownership(self): + # Enable stock location ownership + + InvenTreeSetting.set_setting('STOCK_OWNERSHIP_CONTROL', True, self.user) + self.assertEqual(True, InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')) + + def test_owner_control(self): + # Test stock location and item ownership + from .models import StockLocation, StockItem + from users.models import Owner + + user_group = self.user.groups.all()[0] + user_group_owner = Owner.get_owner(user_group) + new_user_group = self.new_user.groups.all()[0] + new_user_group_owner = Owner.get_owner(new_user_group) + + user_as_owner = Owner.get_owner(self.user) + new_user_as_owner = Owner.get_owner(self.new_user) + + test_location_id = 4 + test_item_id = 11 + + # Enable ownership control + self.enable_ownership() + + # Set ownership on existing location + response = self.client.post(reverse('stock-location-edit', args=(test_location_id,)), + {'name': 'Office', 'owner': user_group_owner.pk}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": true', status_code=200) + + # Set ownership on existing item (and change location) + response = self.client.post(reverse('stock-item-edit', args=(test_item_id,)), + {'part': 1, 'status': StockStatus.OK, 'owner': user_as_owner.pk}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": true', status_code=200) + + # Logout + self.client.logout() + + # Login with new user + self.client.login(username='john', password='custom123') + + # Test location edit + response = self.client.post(reverse('stock-location-edit', args=(test_location_id,)), + {'name': 'Office', 'owner': new_user_group_owner.pk}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + # Make sure the location's owner is unchanged + location = StockLocation.objects.get(pk=test_location_id) + self.assertEqual(location.owner, user_group_owner) + + # Test item edit + response = self.client.post(reverse('stock-item-edit', args=(test_item_id,)), + {'part': 1, 'status': StockStatus.OK, 'owner': new_user_as_owner.pk}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + + # Make sure the item's owner is unchanged + item = StockItem.objects.get(pk=test_item_id) + self.assertEqual(item.owner, user_as_owner) + + # Create new parent location + parent_location = { + 'name': 'John Desk', + 'description': 'John\'s desk', + 'owner': new_user_group_owner.pk, + } + + # Create new parent location + response = self.client.post(reverse('stock-location-create'), + parent_location, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": true', status_code=200) + + # Retrieve created location + parent_location = StockLocation.objects.get(name=parent_location['name']) + + # Create new child location + new_location = { + 'name': 'Upper Left Drawer', + 'description': 'John\'s desk - Upper left drawer', + } + + # Try to create new location with neither parent or owner + response = self.client.post(reverse('stock-location-create'), + new_location, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": false', status_code=200) + + # Try to create new location with invalid owner + new_location['parent'] = parent_location.id + new_location['owner'] = user_group_owner.pk + response = self.client.post(reverse('stock-location-create'), + new_location, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": false', status_code=200) + + # Try to create new location with valid owner + new_location['owner'] = new_user_group_owner.pk + response = self.client.post(reverse('stock-location-create'), + new_location, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": true', status_code=200) + + # Retrieve created location + location_created = StockLocation.objects.get(name=new_location['name']) + + # Create new item + new_item = { + 'part': 25, + 'location': location_created.pk, + 'quantity': 123, + 'status': StockStatus.OK, + } + + # Try to create new item with no owner + response = self.client.post(reverse('stock-item-create'), + new_item, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": false', status_code=200) + + # Try to create new item with invalid owner + new_item['owner'] = user_as_owner.pk + response = self.client.post(reverse('stock-item-create'), + new_item, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": false', status_code=200) + + # Try to create new item with valid owner + new_item['owner'] = new_user_as_owner.pk + response = self.client.post(reverse('stock-item-create'), + new_item, HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": true', status_code=200) + + # Logout + self.client.logout() + + # Login with admin + self.client.login(username='username', password='password') + + # Switch owner of location + response = self.client.post(reverse('stock-location-edit', args=(location_created.pk,)), + {'name': new_location['name'], 'owner': user_group_owner.pk}, + HTTP_X_REQUESTED_WITH='XMLHttpRequest') + self.assertContains(response, '"form_valid": true', status_code=200) + + # Check that owner was updated for item in this location + stock_item = StockItem.objects.all().last() + self.assertEqual(stock_item.owner, user_group_owner) diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 2de775422a..11fc76b6e0 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -11,6 +11,8 @@ from django.views.generic import DetailView, ListView, UpdateView from django.forms.models import model_to_dict from django.forms import HiddenInput from django.urls import reverse +from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group from django.utils.translation import ugettext as _ @@ -33,6 +35,8 @@ from part.models import Part from .models import StockItem, StockLocation, StockItemTracking, StockItemAttachment, StockItemTestResult import common.settings +from common.models import InvenTreeSetting +from users.models import Owner from .admin import StockItemResource @@ -125,6 +129,7 @@ class StockLocationEdit(AjaxUpdateView): """ Customize form data for StockLocation editing. Limit the choices for 'parent' field to those which make sense. + If ownership control is enabled and location has parent, disable owner field. """ form = super(AjaxUpdateView, self).get_form() @@ -137,8 +142,105 @@ class StockLocationEdit(AjaxUpdateView): form.fields['parent'].queryset = parent_choices + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if not stock_ownership_control: + # Hide owner field + form.fields['owner'].widget = HiddenInput() + else: + # Get location's owner + location_owner = location.owner + + if location_owner: + if location.parent: + try: + # If location has parent and owner: automatically select parent's owner + parent_owner = location.parent.owner + form.fields['owner'].initial = parent_owner + except AttributeError: + pass + else: + # If current owner exists: automatically select it + form.fields['owner'].initial = location_owner + + # Update queryset or disable field (only if not admin) + if not self.request.user.is_superuser: + if type(location_owner.owner) is Group: + user_as_owner = Owner.get_owner(self.request.user) + queryset = location_owner.get_related_owners(include_group=True) + + if user_as_owner not in queryset: + # Only owners or admin can change current owner + form.fields['owner'].disabled = True + else: + form.fields['owner'].queryset = queryset + return form + def save(self, object, form, **kwargs): + """ If location has children and ownership control is enabled: + - update owner of all children location of this location + - update owner for all stock items at this location + """ + + self.object = form.save() + + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if stock_ownership_control: + # Get authorized users + authorized_owners = self.object.owner.get_related_owners() + + # Update children locations + children_locations = self.object.get_children() + for child in children_locations: + # Check if current owner is subset of new owner + if child.owner and authorized_owners: + if child.owner in authorized_owners: + continue + + child.owner = self.object.owner + child.save() + + # Update stock items + stock_items = self.object.get_stock_items() + + for stock_item in stock_items: + # Check if current owner is subset of new owner + if stock_item.owner and authorized_owners: + if stock_item.owner in authorized_owners: + continue + + stock_item.owner = self.object.owner + stock_item.save() + + return self.object + + def validate(self, item, form): + """ Check that owner is set if stock ownership control is enabled """ + + parent = form.cleaned_data.get('parent', None) + + owner = form.cleaned_data.get('owner', None) + + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if stock_ownership_control: + if not owner and not self.request.user.is_superuser: + form.add_error('owner', _('Owner is required (ownership control is enabled)')) + else: + try: + if parent.owner: + if parent.owner != owner: + error = f'Owner requires to be equivalent to parent\'s owner ({parent.owner})' + form.add_error('owner', error) + except AttributeError: + # No parent + pass + class StockLocationQRCode(QRCodeView): """ View for displaying a QR code for a StockLocation object """ @@ -1082,6 +1184,18 @@ class StockAdjust(AjaxView, FormMixin): count += 1 + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if stock_ownership_control: + # Fetch destination owner + destination_owner = destination.owner + + if destination_owner: + # Update owner + item.owner = destination_owner + item.save() + if count == 0: return _('No items were moved') @@ -1148,8 +1262,76 @@ class StockItemEdit(AjaxUpdateView): if not item.part.trackable and not item.serialized: form.fields['serial'].widget = HiddenInput() + location = item.location + + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if not stock_ownership_control: + form.fields['owner'].widget = HiddenInput() + else: + try: + location_owner = location.owner + except AttributeError: + location_owner = None + + # Check if location has owner + if location_owner: + form.fields['owner'].initial = location_owner + + # Check location's owner type and filter potential owners + if type(location_owner.owner) is Group: + user_as_owner = Owner.get_owner(self.request.user) + queryset = location_owner.get_related_owners(include_group=True) + + if user_as_owner in queryset: + form.fields['owner'].initial = user_as_owner + + form.fields['owner'].queryset = queryset + + elif type(location_owner.owner) is get_user_model(): + # If location's owner is a user: automatically set owner field and disable it + form.fields['owner'].disabled = True + form.fields['owner'].initial = location_owner + + try: + item_owner = item.owner + except AttributeError: + item_owner = None + + # Check if item has owner + if item_owner: + form.fields['owner'].initial = item_owner + + # Check item's owner type and filter potential owners + if type(item_owner.owner) is Group: + user_as_owner = Owner.get_owner(self.request.user) + queryset = item_owner.get_related_owners(include_group=True) + + if user_as_owner in queryset: + form.fields['owner'].initial = user_as_owner + + form.fields['owner'].queryset = queryset + + elif type(item_owner.owner) is get_user_model(): + # If item's owner is a user: automatically set owner field and disable it + form.fields['owner'].disabled = True + form.fields['owner'].initial = item_owner + return form + def validate(self, item, form): + """ Check that owner is set if stock ownership control is enabled """ + + owner = form.cleaned_data.get('owner', None) + + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if stock_ownership_control: + if not owner and not self.request.user.is_superuser: + form.add_error('owner', _('Owner is required (ownership control is enabled)')) + class StockItemConvert(AjaxUpdateView): """ @@ -1202,6 +1384,76 @@ class StockLocationCreate(AjaxCreateView): return initials + def get_form(self): + """ Disable owner field when: + - creating child location + - and stock ownership control is enable + """ + + form = super().get_form() + + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if not stock_ownership_control: + # Hide owner field + form.fields['owner'].widget = HiddenInput() + else: + # If user did not selected owner: automatically match to parent's owner + if not form['owner'].data: + try: + parent_id = form['parent'].value() + parent = StockLocation.objects.get(pk=parent_id) + + if parent: + form.fields['owner'].initial = parent.owner + if not self.request.user.is_superuser: + form.fields['owner'].disabled = True + except StockLocation.DoesNotExist: + pass + except ValueError: + pass + + return form + + def save(self, form): + """ If parent location exists then use it to set the owner """ + + self.object = form.save(commit=False) + + parent = form.cleaned_data.get('parent', None) + + if parent: + # Select parent's owner + self.object.owner = parent.owner + + self.object.save() + + return self.object + + def validate(self, item, form): + """ Check that owner is set if stock ownership control is enabled """ + + parent = form.cleaned_data.get('parent', None) + + owner = form.cleaned_data.get('owner', None) + + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if stock_ownership_control: + if not owner and not self.request.user.is_superuser: + form.add_error('owner', _('Owner is required (ownership control is enabled)')) + else: + try: + if parent.owner: + if parent.owner != owner: + error = f'Owner requires to be equivalent to parent\'s owner ({parent.owner})' + form.add_error('owner', error) + except AttributeError: + # No parent + pass + class StockItemSerialize(AjaxUpdateView): """ View for manually serializing a StockItem """ @@ -1396,7 +1648,42 @@ class StockItemCreate(AjaxCreateView): # Otherwise if the user has selected a SupplierPart, we know what Part they meant! if form['supplier_part'].value() is not None: pass - + + location = None + try: + loc_id = form['location'].value() + location = StockLocation.objects.get(pk=loc_id) + except StockLocation.DoesNotExist: + pass + except ValueError: + pass + + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + if not stock_ownership_control: + form.fields['owner'].widget = HiddenInput() + else: + try: + location_owner = location.owner + except AttributeError: + location_owner = None + + if location_owner: + # Check location's owner type and filter potential owners + if type(location_owner.owner) is Group: + user_as_owner = Owner.get_owner(self.request.user) + queryset = location_owner.get_related_owners() + + if user_as_owner in queryset: + form.fields['owner'].initial = user_as_owner + + form.fields['owner'].queryset = queryset + + elif type(location_owner.owner) is get_user_model(): + # If location's owner is a user: automatically set owner field and disable it + form.fields['owner'].disabled = True + form.fields['owner'].initial = location_owner + return form def get_initial(self): @@ -1473,10 +1760,15 @@ class StockItemCreate(AjaxCreateView): data = form.cleaned_data - part = data['part'] + part = data.get('part', None) quantity = data.get('quantity', None) + owner = data.get('owner', None) + + if not part: + return + if not quantity: return @@ -1512,6 +1804,15 @@ class StockItemCreate(AjaxCreateView): _('Serial numbers already exist') + ': ' + exists ) + # Is ownership control enabled? + stock_ownership_control = InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if stock_ownership_control: + # Check if owner is set + if not owner and not self.request.user.is_superuser: + form.add_error('owner', _('Owner is required (ownership control is enabled)')) + return + def save(self, form, **kwargs): """ Create a new StockItem based on the provided form data. diff --git a/InvenTree/templates/InvenTree/settings/stock.html b/InvenTree/templates/InvenTree/settings/stock.html index 5ad308decc..588f01e0e9 100644 --- a/InvenTree/templates/InvenTree/settings/stock.html +++ b/InvenTree/templates/InvenTree/settings/stock.html @@ -19,6 +19,7 @@ {% include "InvenTree/settings/setting.html" with key="STOCK_STALE_DAYS" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_SALE" %} {% include "InvenTree/settings/setting.html" with key="STOCK_ALLOW_EXPIRED_BUILD" %} + {% include "InvenTree/settings/setting.html" with key="STOCK_OWNERSHIP_CONTROL" %} -{% endblock %} \ No newline at end of file +{% endblock %} diff --git a/InvenTree/templates/attachment_table.html b/InvenTree/templates/attachment_table.html index d13b7b33b1..35b114cc05 100644 --- a/InvenTree/templates/attachment_table.html +++ b/InvenTree/templates/attachment_table.html @@ -1,6 +1,5 @@ {% load i18n %} -{% if roles.stock.change %}
    -{% endif %}
    diff --git a/InvenTree/templates/stock_table.html b/InvenTree/templates/stock_table.html index 130a67c6e6..0542afef4d 100644 --- a/InvenTree/templates/stock_table.html +++ b/InvenTree/templates/stock_table.html @@ -1,4 +1,10 @@ {% load i18n %} +{% load inventree_extras %} + +{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} +{% if owner_control.value == "True" %} + {% authorized_owners location.owner as owners %} +{% endif %}
    @@ -6,8 +12,8 @@ - {% if read_only %} - {% else %} + + {% if owner_control.value == "True" and user in owners or user.is_superuser or owner_control.value == "False" %} {% if roles.stock.add %}
    + +
    {% endif %} {% endif %}
    diff --git a/InvenTree/users/admin.py b/InvenTree/users/admin.py index c84f1310ce..d8406bfddd 100644 --- a/InvenTree/users/admin.py +++ b/InvenTree/users/admin.py @@ -11,7 +11,7 @@ from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin from django.utils.safestring import mark_safe -from users.models import RuleSet +from users.models import RuleSet, Owner User = get_user_model() @@ -215,8 +215,17 @@ class InvenTreeUserAdmin(UserAdmin): ) +class OwnerAdmin(admin.ModelAdmin): + """ + Custom admin interface for the Owner model + """ + pass + + admin.site.unregister(Group) admin.site.register(Group, RoleGroupAdmin) admin.site.unregister(User) admin.site.register(User, InvenTreeUserAdmin) + +admin.site.register(Owner, OwnerAdmin) diff --git a/InvenTree/users/apps.py b/InvenTree/users/apps.py index 07e303c1be..1541b1aed4 100644 --- a/InvenTree/users/apps.py +++ b/InvenTree/users/apps.py @@ -16,6 +16,11 @@ class UsersConfig(AppConfig): except (OperationalError, ProgrammingError): pass + try: + self.update_owners() + except (OperationalError, ProgrammingError): + pass + def assign_permissions(self): from django.contrib.auth.models import Group @@ -31,3 +36,17 @@ class UsersConfig(AppConfig): for group in Group.objects.all(): update_group_roles(group) + + def update_owners(self): + + from django.contrib.auth import get_user_model + from django.contrib.auth.models import Group + from users.models import Owner + + # Create group owners + for group in Group.objects.all(): + Owner.create(group) + + # Create user owners + for user in get_user_model().objects.all(): + Owner.create(user) diff --git a/InvenTree/users/migrations/0005_owner_model.py b/InvenTree/users/migrations/0005_owner_model.py new file mode 100644 index 0000000000..01ef66d9a3 --- /dev/null +++ b/InvenTree/users/migrations/0005_owner_model.py @@ -0,0 +1,27 @@ +# Generated by Django 3.0.7 on 2021-01-11 18:54 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ('users', '0004_auto_20210113_1909'), + ] + + operations = [ + migrations.CreateModel( + name='Owner', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('owner_id', models.PositiveIntegerField(blank=True, null=True)), + ('owner_type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + ), + migrations.AddConstraint( + model_name='owner', + constraint=models.UniqueConstraint(fields=('owner_type', 'owner_id'), name='unique_owner'), + ), + ] diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index c9862e3aff..57cee2774f 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -1,12 +1,16 @@ # -*- coding: utf-8 -*- +from django.contrib.auth import get_user_model from django.contrib.auth.models import Group, Permission +from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType +from django.db.models import UniqueConstraint, Q +from django.db.utils import IntegrityError from django.db import models from django.utils.translation import gettext_lazy as _ from django.dispatch import receiver -from django.db.models.signals import post_save +from django.db.models.signals import post_save, post_delete class RuleSet(models.Model): @@ -116,6 +120,7 @@ class RuleSet(models.Model): 'report_reportasset', 'report_testreport', 'part_partstar', + 'users_owner', # Third-party tables 'error_report_error', @@ -350,7 +355,7 @@ def update_group_roles(group, debug=False): print(f"Removing permission {perm} from group {group.name}") -@receiver(post_save, sender=Group) +@receiver(post_save, sender=Group, dispatch_uid='create_missing_rule_sets') def create_missing_rule_sets(sender, instance, **kwargs): """ Called *after* a Group object is saved. @@ -392,3 +397,151 @@ def check_user_role(user, role, permission): # No matching permissions found return False + + +class Owner(models.Model): + """ + The Owner class is a proxy for a Group or User instance. + Owner can be associated to any InvenTree model (part, stock, build, etc.) + + owner_type: Model type (Group or User) + owner_id: Group or User instance primary key + owner: Returns the Group or User instance combining the owner_type and owner_id fields + """ + + class Meta: + # Ensure all owners are unique + constraints = [ + UniqueConstraint(fields=['owner_type', 'owner_id'], + name='unique_owner') + ] + + owner_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True) + + owner_id = models.PositiveIntegerField(null=True, blank=True) + + owner = GenericForeignKey('owner_type', 'owner_id') + + def __str__(self): + """ Defines the owner string representation """ + return f'{self.owner} ({self.owner_type.name})' + + @classmethod + def create(cls, obj): + """ Check if owner exist then create new owner entry """ + + # Check for existing owner + existing_owner = cls.get_owner(obj) + + if not existing_owner: + # Create new owner + try: + return cls.objects.create(owner=obj) + except IntegrityError: + return None + + return existing_owner + + @classmethod + def get_owner(cls, user_or_group): + """ Get owner instance for a group or user """ + + user_model = get_user_model() + owner = None + content_type_id = 0 + content_type_id_list = [ContentType.objects.get_for_model(Group).id, + ContentType.objects.get_for_model(user_model).id] + + # If instance type is obvious: set content type + if type(user_or_group) is Group: + content_type_id = content_type_id_list[0] + elif type(user_or_group) is get_user_model(): + content_type_id = content_type_id_list[1] + + if content_type_id: + try: + owner = Owner.objects.get(owner_id=user_or_group.id, + owner_type=content_type_id) + except Owner.DoesNotExist: + pass + else: + # Check whether user_or_group is a Group instance + try: + group = Group.objects.get(pk=user_or_group.id) + except Group.DoesNotExist: + group = None + + if group: + try: + owner = Owner.objects.get(owner_id=user_or_group.id, + owner_type=content_type_id_list[0]) + except Owner.DoesNotExist: + pass + + return owner + + # Check whether user_or_group is a User instance + try: + user = user_model.objects.get(pk=user_or_group.id) + except user_model.DoesNotExist: + user = None + + if user: + try: + owner = Owner.objects.get(owner_id=user_or_group.id, + owner_type=content_type_id_list[1]) + except Owner.DoesNotExist: + pass + + return owner + + return owner + + def get_related_owners(self, include_group=False): + """ + Get all owners "related" to an owner. + This method is useful to retrieve all "user-type" owners linked to a "group-type" owner + """ + + user_model = get_user_model() + related_owners = None + + if type(self.owner) is Group: + users = user_model.objects.filter(groups__name=self.owner.name) + + if include_group: + # Include "group-type" owner in the query + query = Q(owner_id__in=users, owner_type=ContentType.objects.get_for_model(user_model).id) | \ + Q(owner_id=self.owner.id, owner_type=ContentType.objects.get_for_model(Group).id) + else: + query = Q(owner_id__in=users, owner_type=ContentType.objects.get_for_model(user_model).id) + + related_owners = Owner.objects.filter(query) + + elif type(self.owner) is user_model: + related_owners = [self] + + return related_owners + + +@receiver(post_save, sender=Group, dispatch_uid='create_owner') +@receiver(post_save, sender=get_user_model(), dispatch_uid='create_owner') +def create_owner(sender, instance, **kwargs): + """ + Callback function to create a new owner instance + after either a new group or user instance is saved. + """ + + Owner.create(obj=instance) + + +@receiver(post_delete, sender=Group, dispatch_uid='delete_owner') +@receiver(post_delete, sender=get_user_model(), dispatch_uid='delete_owner') +def delete_owner(sender, instance, **kwargs): + """ + Callback function to delete an owner instance + after either a new group or user instance is deleted. + """ + + owner = Owner.get_owner(instance) + owner.delete() diff --git a/InvenTree/users/tests.py b/InvenTree/users/tests.py index e277422f71..895d0a84af 100644 --- a/InvenTree/users/tests.py +++ b/InvenTree/users/tests.py @@ -3,9 +3,10 @@ from __future__ import unicode_literals from django.test import TestCase from django.apps import apps +from django.contrib.auth import get_user_model from django.contrib.auth.models import Group -from users.models import RuleSet +from users.models import RuleSet, Owner class RuleSetModelTest(TestCase): @@ -157,3 +158,48 @@ class RuleSetModelTest(TestCase): # There should now not be any permissions assigned to this group self.assertEqual(group.permissions.count(), 0) + + +class OwnerModelTest(TestCase): + """ + Some simplistic tests to ensure the Owner model is setup correctly. + """ + + def setUp(self): + """ Add users and groups """ + + # Create a new user + self.user = get_user_model().objects.create_user( + username='john', + email='john@email.com', + password='custom123', + ) + + # Put the user into a new group + self.group = Group.objects.create(name='new_group') + self.user.groups.add(self.group) + + def test_owner(self): + + # Check that owner was created for user + user_as_owner = Owner.get_owner(self.user) + self.assertEqual(type(user_as_owner), Owner) + + # Check that owner was created for group + group_as_owner = Owner.get_owner(self.group) + self.assertEqual(type(group_as_owner), Owner) + + # Get related owners (user + group) + related_owners = group_as_owner.get_related_owners(include_group=True) + self.assertTrue(user_as_owner in related_owners) + self.assertTrue(group_as_owner in related_owners) + + # Delete user and verify owner was deleted too + self.user.delete() + user_as_owner = Owner.get_owner(self.user) + self.assertEqual(user_as_owner, None) + + # Delete group and verify owner was deleted too + self.group.delete() + group_as_owner = Owner.get_owner(self.group) + self.assertEqual(group_as_owner, None) diff --git a/tasks.py b/tasks.py index 69c53b83d4..77604eaf3a 100644 --- a/tasks.py +++ b/tasks.py @@ -186,6 +186,10 @@ def translate(c): manage(c, "makemessages -e py -e html -e js") manage(c, "compilemessages") + path = os.path.join('InvenTree', 'script', 'translation_stats.py') + + c.run(f'python {path}') + @task def style(c): """