Merge branch 'inventree:master' into matmair/issue2201

This commit is contained in:
Matthias Mair 2021-11-23 18:28:41 +01:00 committed by GitHub
commit e3dfb1295b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 57439 additions and 55111 deletions

View File

@ -46,7 +46,7 @@ class InvenTreeAPITestCase(APITestCase):
self.user.is_staff = True self.user.is_staff = True
self.user.save() self.user.save()
for role in self.roles: for role in self.roles:
self.assignRole(role) self.assignRole(role)

View File

@ -1,9 +1,8 @@
""" """
Pull rendered copies of the templated Pull rendered copies of the templated
""" """
from django.http import response from django.test import TestCase
from django.test import TestCase, testcases
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
import os import os

View File

@ -53,7 +53,7 @@ class InvenTreeModelMoneyField(ModelMoneyField):
""" """
Custom MoneyField for clean migrations while using dynamic currency settings Custom MoneyField for clean migrations while using dynamic currency settings
""" """
def __init__(self, **kwargs): def __init__(self, **kwargs):
# detect if creating migration # detect if creating migration
if 'migrate' in sys.argv or 'makemigrations' in sys.argv: if 'migrate' in sys.argv or 'makemigrations' in sys.argv:

View File

@ -38,7 +38,7 @@ class InvenTreeOrderingFilter(OrderingFilter):
ordering = [] ordering = []
for field in ordering_initial: for field in ordering_initial:
reverse = field.startswith('-') reverse = field.startswith('-')
if reverse: if reverse:
@ -52,7 +52,7 @@ class InvenTreeOrderingFilter(OrderingFilter):
""" """
Potentially, a single field could be "aliased" to multiple field, Potentially, a single field could be "aliased" to multiple field,
(For example to enforce a particular ordering sequence) (For example to enforce a particular ordering sequence)
e.g. to filter first by the integer value... e.g. to filter first by the integer value...

View File

@ -36,7 +36,7 @@ class Command(BaseCommand):
img = model.image img = model.image
url = img.thumbnail.name url = img.thumbnail.name
loc = os.path.join(settings.MEDIA_ROOT, url) loc = os.path.join(settings.MEDIA_ROOT, url)
if not os.path.exists(loc): if not os.path.exists(loc):
logger.info(f"Generating thumbnail image for '{img}'") logger.info(f"Generating thumbnail image for '{img}'")

View File

@ -31,7 +31,7 @@ class InvenTreeMetadata(SimpleMetadata):
""" """
def determine_metadata(self, request, view): def determine_metadata(self, request, view):
self.request = request self.request = request
self.view = view self.view = view
@ -98,7 +98,7 @@ class InvenTreeMetadata(SimpleMetadata):
Override get_serializer_info so that we can add 'default' values Override get_serializer_info so that we can add 'default' values
to any fields whose Meta.model specifies a default value to any fields whose Meta.model specifies a default value
""" """
serializer_info = super().get_serializer_info(serializer) serializer_info = super().get_serializer_info(serializer)
model_class = None model_class = None
@ -174,7 +174,7 @@ class InvenTreeMetadata(SimpleMetadata):
# Extract extra information if an instance is available # Extract extra information if an instance is available
if hasattr(serializer, 'instance'): if hasattr(serializer, 'instance'):
instance = serializer.instance instance = serializer.instance
if instance is None and model_class is not None: if instance is None and model_class is not None:
# Attempt to find the instance based on kwargs lookup # Attempt to find the instance based on kwargs lookup
kwargs = getattr(self.view, 'kwargs', None) kwargs = getattr(self.view, 'kwargs', None)
@ -240,7 +240,7 @@ class InvenTreeMetadata(SimpleMetadata):
# Introspect writable related fields # Introspect writable related fields
if field_info['type'] == 'field' and not field_info['read_only']: if field_info['type'] == 'field' and not field_info['read_only']:
# If the field is a PrimaryKeyRelatedField, we can extract the model from the queryset # If the field is a PrimaryKeyRelatedField, we can extract the model from the queryset
if isinstance(field, serializers.PrimaryKeyRelatedField): if isinstance(field, serializers.PrimaryKeyRelatedField):
model = field.queryset.model model = field.queryset.model

View File

@ -66,7 +66,7 @@ class InvenTreeMoneySerializer(MoneyField):
if currency and amount is not None and not isinstance(amount, MONEY_CLASSES) and amount is not empty: if currency and amount is not None and not isinstance(amount, MONEY_CLASSES) and amount is not empty:
return Money(amount, currency) return Money(amount, currency)
return amount return amount

View File

@ -673,7 +673,7 @@ LANGUAGES = [
('el', _('Greek')), ('el', _('Greek')),
('en', _('English')), ('en', _('English')),
('es', _('Spanish')), ('es', _('Spanish')),
('es-mx', _('Spanish (Mexican')), ('es-mx', _('Spanish (Mexican)')),
('fr', _('French')), ('fr', _('French')),
('he', _('Hebrew')), ('he', _('Hebrew')),
('it', _('Italian')), ('it', _('Italian')),

View File

@ -106,7 +106,7 @@ def offload_task(taskname, *args, force_sync=False, **kwargs):
except NameError: except NameError:
logger.warning(f"WARNING: '{taskname}' not started - No function named '{func}'") logger.warning(f"WARNING: '{taskname}' not started - No function named '{func}'")
return return
# Workers are not running: run it as synchronous task # Workers are not running: run it as synchronous task
_func(*args, **kwargs) _func(*args, **kwargs)

View File

@ -19,7 +19,7 @@ from base64 import b64encode
class HTMLAPITests(TestCase): class HTMLAPITests(TestCase):
""" """
Test that we can access the REST API endpoints via the HTML interface. Test that we can access the REST API endpoints via the HTML interface.
History: Discovered on 2021-06-28 a bug in InvenTreeModelSerializer, History: Discovered on 2021-06-28 a bug in InvenTreeModelSerializer,
which raised an AssertionError when using the HTML API interface, which raised an AssertionError when using the HTML API interface,
while the regular JSON interface continued to work as expected. while the regular JSON interface continued to work as expected.
@ -280,7 +280,7 @@ class APITests(InvenTreeAPITestCase):
""" """
Tests for detail API endpoint actions Tests for detail API endpoint actions
""" """
self.basicAuth() self.basicAuth()
url = reverse('api-part-detail', kwargs={'pk': 1}) url = reverse('api-part-detail', kwargs={'pk': 1})

View File

@ -77,7 +77,7 @@ apipatterns = [
settings_urls = [ settings_urls = [
url(r'^i18n/?', include('django.conf.urls.i18n')), url(r'^i18n/?', include('django.conf.urls.i18n')),
url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'), url(r'^appearance/?', AppearanceSelectView.as_view(), name='settings-appearance'),
url(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'), url(r'^currencies-refresh/', CurrencyRefreshView.as_view(), name='settings-currencies-refresh'),

View File

@ -120,10 +120,10 @@ def isInvenTreeDevelopmentVersion():
def inventreeDocsVersion(): def inventreeDocsVersion():
""" """
Return the version string matching the latest documentation. Return the version string matching the latest documentation.
Development -> "latest" Development -> "latest"
Release -> "major.minor.sub" e.g. "0.5.2" Release -> "major.minor.sub" e.g. "0.5.2"
""" """
if isInvenTreeDevelopmentVersion(): if isInvenTreeDevelopmentVersion():

View File

@ -198,7 +198,7 @@ class BuildUnallocate(generics.CreateAPIView):
queryset = Build.objects.none() queryset = Build.objects.none()
serializer_class = BuildUnallocationSerializer serializer_class = BuildUnallocationSerializer
def get_serializer_context(self): def get_serializer_context(self):
ctx = super().get_serializer_context() ctx = super().get_serializer_context()
@ -231,7 +231,7 @@ class BuildComplete(generics.CreateAPIView):
ctx['build'] = Build.objects.get(pk=self.kwargs.get('pk', None)) ctx['build'] = Build.objects.get(pk=self.kwargs.get('pk', None))
except: except:
pass pass
return ctx return ctx
@ -296,7 +296,7 @@ class BuildItemList(generics.ListCreateAPIView):
kwargs['location_detail'] = str2bool(params.get('location_detail', False)) kwargs['location_detail'] = str2bool(params.get('location_detail', False))
except AttributeError: except AttributeError:
pass pass
return self.serializer_class(*args, **kwargs) return self.serializer_class(*args, **kwargs)
def get_queryset(self): def get_queryset(self):

View File

@ -66,7 +66,7 @@ def get_next_build_number():
attempts.add(reference) attempts.add(reference)
else: else:
break break
return reference return reference
@ -94,13 +94,13 @@ class Build(MPTTModel, ReferenceIndexingMixin):
""" """
OVERDUE_FILTER = Q(status__in=BuildStatus.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date()) OVERDUE_FILTER = Q(status__in=BuildStatus.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
@staticmethod @staticmethod
def get_api_url(): def get_api_url():
return reverse('api-build-list') return reverse('api-build-list')
def api_instance_filters(self): def api_instance_filters(self):
return { return {
'parent': { 'parent': {
'exclude_tree': self.pk, 'exclude_tree': self.pk,
@ -1178,7 +1178,7 @@ class BuildItem(models.Model):
bom_item = PartModels.BomItem.objects.get(part=self.build.part, sub_part=ancestor) bom_item = PartModels.BomItem.objects.get(part=self.build.part, sub_part=ancestor)
except PartModels.BomItem.DoesNotExist: except PartModels.BomItem.DoesNotExist:
continue continue
# A matching BOM item has been found! # A matching BOM item has been found!
if idx == 0 or bom_item.allow_variants: if idx == 0 or bom_item.allow_variants:
bom_item_valid = True bom_item_valid = True
@ -1234,7 +1234,7 @@ class BuildItem(models.Model):
thumb_url = self.stock_item.part.image.thumbnail.url thumb_url = self.stock_item.part.image.thumbnail.url
except: except:
pass pass
if thumb_url is None and self.bom_item and self.bom_item.sub_part: if thumb_url is None and self.bom_item and self.bom_item.sub_part:
try: try:
thumb_url = self.bom_item.sub_part.image.thumbnail.url thumb_url = self.bom_item.sub_part.image.thumbnail.url

View File

@ -309,7 +309,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
) )
def validate_bom_item(self, bom_item): def validate_bom_item(self, bom_item):
# TODO: Fix this validation - allow for variants and substitutes! # TODO: Fix this validation - allow for variants and substitutes!
build = self.context['build'] build = self.context['build']
@ -332,7 +332,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
if not stock_item.in_stock: if not stock_item.in_stock:
raise ValidationError(_("Item must be in stock")) raise ValidationError(_("Item must be in stock"))
return stock_item return stock_item
quantity = serializers.DecimalField( quantity = serializers.DecimalField(
@ -398,7 +398,7 @@ class BuildAllocationItemSerializer(serializers.Serializer):
# Output *cannot* be set for un-tracked parts # Output *cannot* be set for un-tracked parts
if output is not None and not bom_item.sub_part.trackable: if output is not None and not bom_item.sub_part.trackable:
raise ValidationError({ raise ValidationError({
'output': _('Build output cannot be specified for allocation of untracked parts') 'output': _('Build output cannot be specified for allocation of untracked parts')
}) })
@ -422,14 +422,14 @@ class BuildAllocationSerializer(serializers.Serializer):
""" """
Validation Validation
""" """
super().validate(data) super().validate(data)
items = data.get('items', []) items = data.get('items', [])
if len(items) == 0: if len(items) == 0:
raise ValidationError(_('Allocation items must be provided')) raise ValidationError(_('Allocation items must be provided'))
return data return data
def save(self): def save(self):

View File

@ -2,14 +2,21 @@
{% load static %} {% load static %}
{% load inventree_extras %} {% load inventree_extras %}
{% include "sidebar_item.html" with label='details' text="Build Order Details" icon="fa-info-circle" %} {% trans "Build Order Details" as text %}
{% include "sidebar_item.html" with label='details' text=text icon="fa-info-circle" %}
{% if build.active %} {% if build.active %}
{% include "sidebar_item.html" with label='allocate' text="Allocate Stock" icon="fa-tasks" %} {% trans "Allocate Stock" as text %}
{% include "sidebar_item.html" with label='allocate' text=text icon="fa-tasks" %}
{% endif %} {% endif %}
{% if not build.is_complete %} {% if not build.is_complete %}
{% include "sidebar_item.html" with label='outputs' text="Pending Items" icon="fa-tools" %} {% trans "Pending Items" as text %}
{% include "sidebar_item.html" with label='outputs' text=text icon="fa-tools" %}
{% endif %} {% endif %}
{% include "sidebar_item.html" with label='completed' text="Completed Items" icon="fa-boxes" %} {% trans "Completed Items" as text %}
{% include "sidebar_item.html" with label='children' text="Child Build Orders" icon="fa-sitemap" %} {% include "sidebar_item.html" with label='completed' text=text icon="fa-boxes" %}
{% include "sidebar_item.html" with label='attachments' text="Attachments" icon="fa-paperclip" %} {% trans "Child Build Orders" as text %}
{% include "sidebar_item.html" with label='notes' text="Notes" icon="fa-clipboard" %} {% include "sidebar_item.html" with label='children' text=text icon="fa-sitemap" %}
{% trans "Attachments" as text %}
{% include "sidebar_item.html" with label='attachments' text=text icon="fa-paperclip" %}
{% trans "Notes" as text %}
{% include "sidebar_item.html" with label='notes' text=text icon="fa-clipboard" %}

View File

@ -73,7 +73,7 @@ class GlobalSettingsDetail(generics.RetrieveUpdateAPIView):
permission_classes = [ permission_classes = [
GlobalSettingsPermissions, GlobalSettingsPermissions,
] ]
class UserSettingsList(SettingsList): class UserSettingsList(SettingsList):
""" """
@ -124,7 +124,7 @@ class UserSettingsDetail(generics.RetrieveUpdateAPIView):
queryset = common.models.InvenTreeUserSetting.objects.all() queryset = common.models.InvenTreeUserSetting.objects.all()
serializer_class = common.serializers.UserSettingsSerializer serializer_class = common.serializers.UserSettingsSerializer
permission_classes = [ permission_classes = [
UserSettingsPermissions, UserSettingsPermissions,
] ]

View File

@ -12,7 +12,7 @@ class CommonConfig(AppConfig):
name = 'common' name = 'common'
def ready(self): def ready(self):
self.clear_restart_flag() self.clear_restart_flag()
def clear_restart_flag(self): def clear_restart_flag(self):
@ -22,7 +22,7 @@ class CommonConfig(AppConfig):
try: try:
import common.models import common.models
if common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED'): if common.models.InvenTreeSetting.get_setting('SERVER_RESTART_REQUIRED'):
logger.info("Clearing SERVER_RESTART_REQUIRED flag") logger.info("Clearing SERVER_RESTART_REQUIRED flag")
common.models.InvenTreeSetting.set_setting('SERVER_RESTART_REQUIRED', False, None) common.models.InvenTreeSetting.set_setting('SERVER_RESTART_REQUIRED', False, None)

View File

@ -108,7 +108,9 @@ class BaseInvenTreeSetting(models.Model):
for key, value in settings.items(): for key, value in settings.items():
validator = cls.get_setting_validator(key) validator = cls.get_setting_validator(key)
if cls.validator_is_bool(validator): if cls.is_protected(key):
value = '***'
elif cls.validator_is_bool(validator):
value = InvenTree.helpers.str2bool(value) value = InvenTree.helpers.str2bool(value)
elif cls.validator_is_int(validator): elif cls.validator_is_int(validator):
try: try:
@ -485,7 +487,7 @@ class BaseInvenTreeSetting(models.Model):
elif self.is_int(): elif self.is_int():
return 'integer' return 'integer'
else: else:
return 'string' return 'string'
@ -538,6 +540,19 @@ class BaseInvenTreeSetting(models.Model):
return value return value
@classmethod
def is_protected(cls, key):
"""
Check if the setting value is protected
"""
key = str(key).strip().upper()
if key in cls.GLOBAL_SETTINGS:
return cls.GLOBAL_SETTINGS[key].get('protected', False)
else:
return False
def settings_group_options(): def settings_group_options():
"""build up group tuple for settings based on gour choices""" """build up group tuple for settings based on gour choices"""

View File

@ -45,6 +45,18 @@ class SettingsSerializer(InvenTreeModelSerializer):
return results return results
def get_value(self, obj):
"""
Make sure protected values are not returned
"""
result = obj.value
# never return protected values
if obj.is_protected:
result = '***'
return result
class GlobalSettingsSerializer(SettingsSerializer): class GlobalSettingsSerializer(SettingsSerializer):
""" """

View File

@ -170,7 +170,7 @@ class ManufacturerPartParameterList(generics.ListCreateAPIView):
queryset = ManufacturerPartParameter.objects.all() queryset = ManufacturerPartParameter.objects.all()
serializer_class = ManufacturerPartParameterSerializer serializer_class = ManufacturerPartParameterSerializer
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
# Do we wish to include any extra detail? # Do we wish to include any extra detail?

View File

@ -477,7 +477,7 @@ class SupplierPart(models.Model):
return reverse('supplier-part-detail', kwargs={'pk': self.id}) return reverse('supplier-part-detail', kwargs={'pk': self.id})
def api_instance_filters(self): def api_instance_filters(self):
return { return {
'manufacturer_part': { 'manufacturer_part': {
'part': self.part.pk 'part': self.part.pk

View File

@ -187,7 +187,7 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
part_detail = kwargs.pop('part_detail', True) part_detail = kwargs.pop('part_detail', True)
supplier_detail = kwargs.pop('supplier_detail', True) supplier_detail = kwargs.pop('supplier_detail', True)
manufacturer_detail = kwargs.pop('manufacturer_detail', True) manufacturer_detail = kwargs.pop('manufacturer_detail', True)
prettify = kwargs.pop('pretty', False) prettify = kwargs.pop('pretty', False)
super(SupplierPartSerializer, self).__init__(*args, **kwargs) super(SupplierPartSerializer, self).__init__(*args, **kwargs)

View File

@ -2,5 +2,7 @@
{% load static %} {% load static %}
{% load inventree_extras %} {% load inventree_extras %}
{% include "sidebar_item.html" with label='parameters' text="Parameters" icon="fa-th-list" %} {% trans "Parameters" as text %}
{% include "sidebar_item.html" with label='supplier-parts' text="Supplier Parts" icon="fa-building" %} {% include "sidebar_item.html" with label='parameters' text=text icon="fa-th-list" %}
{% trans "Supplier Parts" as text %}
{% include "sidebar_item.html" with label='supplier-parts' text=text icon="fa-building" %}

View File

@ -3,17 +3,24 @@
{% load inventree_extras %} {% load inventree_extras %}
{% if company.is_manufacturer %} {% if company.is_manufacturer %}
{% include "sidebar_item.html" with label='manufacturer-parts' text="Manufactured Parts" icon="fa-industry" %} {% trans "Manufactured Parts" as text %}
{% include "sidebar_item.html" with label='manufacturer-parts' text=text icon="fa-industry" %}
{% endif %} {% endif %}
{% if company.is_supplier %} {% if company.is_supplier %}
{% include "sidebar_item.html" with label='supplier-parts' text="Supplied Parts" icon="fa-building" %} {% trans "Supplied Parts" as text %}
{% include "sidebar_item.html" with label='purchase-orders' text="Purchase Orders" icon="fa-shopping-cart" %} {% include "sidebar_item.html" with label='supplier-parts' text=text icon="fa-building" %}
{% trans "Purchase Orders" as text %}
{% include "sidebar_item.html" with label='purchase-orders' text=text icon="fa-shopping-cart" %}
{% endif %} {% endif %}
{% if company.is_manufacturer or company.is_supplier %} {% if company.is_manufacturer or company.is_supplier %}
{% include "sidebar_item.html" with label='company-stock' text="Supplied Stock Items" icon="fa-boxes" %} {% trans "Supplied Stock Items" as text %}
{% include "sidebar_item.html" with label='company-stock' text=text icon="fa-boxes" %}
{% endif %} {% endif %}
{% if company.is_customer %} {% if company.is_customer %}
{% include "sidebar_item.html" with label='sales-orders' text="Sales Orders" icon="fa-truck" %} {% trans "Sales Orders" as text %}
{% include "sidebar_item.html" with label='assigned-stock' text="Assigned Stock Items" icon="fa-sign-out-alt" %} {% include "sidebar_item.html" with label='sales-orders' text=text icon="fa-truck" %}
{% trans "Assigned Stock Items" as text %}
{% include "sidebar_item.html" with label='assigned-stock' text=text icon="fa-sign-out-alt" %}
{% endif %} {% endif %}
{% include "sidebar_item.html" with label='company-notes' text="Notes" icon="fa-clipboard" %} {% trans "Notes" as text %}
{% include "sidebar_item.html" with label='company-notes' text=text icon="fa-clipboard" %}

View File

@ -2,6 +2,9 @@
{% load static %} {% load static %}
{% load inventree_extras %} {% load inventree_extras %}
{% include "sidebar_item.html" with label='stock' text="Stock Items" icon="fa-boxes" %} {% trans "Stock Items" as text %}
{% include "sidebar_item.html" with label='purchase-orders' text="Purchase Orders" icon="fa-shopping-cart" %} {% include "sidebar_item.html" with label='stock' text=text icon="fa-boxes" %}
{% include "sidebar_item.html" with label='pricing' text="Supplier Part Pricing" icon="fa-dollar-sign" %} {% trans "Purchase Orders" as text %}
{% include "sidebar_item.html" with label='purchase-orders' text=text icon="fa-shopping-cart" %}
{% trans "Supplier Part Pricing" as text %}
{% include "sidebar_item.html" with label='pricing' text=text icon="fa-dollar-sign" %}

View File

@ -202,7 +202,7 @@ class ManufacturerTest(InvenTreeAPITestCase):
data = { data = {
'MPN': 'MPN-TEST-123', 'MPN': 'MPN-TEST-123',
} }
response = self.client.patch(url, data, format='json') response = self.client.patch(url, data, format='json')
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)

View File

@ -29,7 +29,7 @@ company_urls = [
] ]
manufacturer_part_urls = [ manufacturer_part_urls = [
url(r'^(?P<pk>\d+)/', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part.html'), name='manufacturer-part-detail'), url(r'^(?P<pk>\d+)/', views.ManufacturerPartDetail.as_view(template_name='company/manufacturer_part.html'), name='manufacturer-part-detail'),
] ]

View File

@ -399,7 +399,7 @@ class PartLabelMixin:
if key in params: if key in params:
parts = params.getlist(key, []) parts = params.getlist(key, [])
break break
valid_ids = [] valid_ids = []
for part in parts: for part in parts:

View File

@ -186,7 +186,7 @@ class LabelTemplate(models.Model):
""" """
template_string = Template(self.filename_pattern) template_string = Template(self.filename_pattern)
ctx = self.context(request) ctx = self.context(request)
context = Context(ctx) context = Context(ctx)

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-11-19 07:42+1100\n" "POT-Creation-Date: 2021-11-22 22:08+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -131,8 +131,8 @@ msgstr ""
msgid "File comment" msgid "File comment"
msgstr "" msgstr ""
#: InvenTree/models.py:118 InvenTree/models.py:119 common/models.py:1170 #: InvenTree/models.py:118 InvenTree/models.py:119 common/models.py:1185
#: common/models.py:1171 part/models.py:2205 part/models.py:2225 #: common/models.py:1186 part/models.py:2205 part/models.py:2225
#: report/templates/report/inventree_test_report_base.html:96 #: report/templates/report/inventree_test_report_base.html:96
#: templates/js/translated/stock.js:2054 #: templates/js/translated/stock.js:2054
msgid "User" msgid "User"
@ -234,7 +234,7 @@ msgid "Spanish"
msgstr "" msgstr ""
#: InvenTree/settings.py:667 #: InvenTree/settings.py:667
msgid "Spanish (Mexican" msgid "Spanish (Mexican)"
msgstr "" msgstr ""
#: InvenTree/settings.py:668 #: InvenTree/settings.py:668
@ -544,7 +544,7 @@ msgstr ""
#: build/forms.py:36 build/models.py:1283 #: build/forms.py:36 build/models.py:1283
#: build/templates/build/build_base.html:124 #: build/templates/build/build_base.html:124
#: build/templates/build/detail.html:35 common/models.py:1210 #: build/templates/build/detail.html:35 common/models.py:1225
#: company/forms.py:42 company/templates/company/supplier_part.html:251 #: company/forms.py:42 company/templates/company/supplier_part.html:251
#: order/forms.py:102 order/models.py:729 order/models.py:991 #: order/forms.py:102 order/models.py:729 order/models.py:991
#: order/templates/order/order_wizard/match_parts.html:30 #: order/templates/order/order_wizard/match_parts.html:30
@ -1454,688 +1454,688 @@ msgstr ""
msgid "Select {name} file to upload" msgid "Select {name} file to upload"
msgstr "" msgstr ""
#: common/models.py:338 common/models.py:955 common/models.py:1163 #: common/models.py:340 common/models.py:970 common/models.py:1178
msgid "Settings key (must be unique - case insensitive" msgid "Settings key (must be unique - case insensitive"
msgstr "" msgstr ""
#: common/models.py:340 #: common/models.py:342
msgid "Settings value" msgid "Settings value"
msgstr "" msgstr ""
#: common/models.py:375 #: common/models.py:377
msgid "Must be an integer value" msgid "Must be an integer value"
msgstr "" msgstr ""
#: common/models.py:380 #: common/models.py:382
msgid "Chosen value is not a valid option" msgid "Chosen value is not a valid option"
msgstr "" msgstr ""
#: common/models.py:403 #: common/models.py:405
msgid "Value must be a boolean value" msgid "Value must be a boolean value"
msgstr "" msgstr ""
#: common/models.py:414 #: common/models.py:416
msgid "Value must be an integer value" msgid "Value must be an integer value"
msgstr "" msgstr ""
#: common/models.py:437 #: common/models.py:439
msgid "Key string must be unique" msgid "Key string must be unique"
msgstr "" msgstr ""
#: common/models.py:544 #: common/models.py:559
msgid "No group" msgid "No group"
msgstr "" msgstr ""
#: common/models.py:586 #: common/models.py:601
msgid "Restart required" msgid "Restart required"
msgstr "" msgstr ""
#: common/models.py:587 #: common/models.py:602
msgid "A setting has been changed which requires a server restart" msgid "A setting has been changed which requires a server restart"
msgstr "" msgstr ""
#: common/models.py:594 #: common/models.py:609
msgid "InvenTree Instance Name" msgid "InvenTree Instance Name"
msgstr "" msgstr ""
#: common/models.py:596 #: common/models.py:611
msgid "String descriptor for the server instance" msgid "String descriptor for the server instance"
msgstr "" msgstr ""
#: common/models.py:600 #: common/models.py:615
msgid "Use instance name" msgid "Use instance name"
msgstr "" msgstr ""
#: common/models.py:601 #: common/models.py:616
msgid "Use the instance name in the title-bar" msgid "Use the instance name in the title-bar"
msgstr "" msgstr ""
#: common/models.py:607 company/models.py:100 company/models.py:101 #: common/models.py:622 company/models.py:100 company/models.py:101
msgid "Company name" msgid "Company name"
msgstr "" msgstr ""
#: common/models.py:608 #: common/models.py:623
msgid "Internal company name" msgid "Internal company name"
msgstr "" msgstr ""
#: common/models.py:613 #: common/models.py:628
msgid "Base URL" msgid "Base URL"
msgstr "" msgstr ""
#: common/models.py:614 #: common/models.py:629
msgid "Base URL for server instance" msgid "Base URL for server instance"
msgstr "" msgstr ""
#: common/models.py:620 #: common/models.py:635
msgid "Default Currency" msgid "Default Currency"
msgstr "" msgstr ""
#: common/models.py:621 #: common/models.py:636
msgid "Default currency" msgid "Default currency"
msgstr "" msgstr ""
#: common/models.py:627 #: common/models.py:642
msgid "Download from URL" msgid "Download from URL"
msgstr "" msgstr ""
#: common/models.py:628 #: common/models.py:643
msgid "Allow download of remote images and files from external URL" msgid "Allow download of remote images and files from external URL"
msgstr "" msgstr ""
#: common/models.py:634 #: common/models.py:649
msgid "Barcode Support" msgid "Barcode Support"
msgstr "" msgstr ""
#: common/models.py:635 #: common/models.py:650
msgid "Enable barcode scanner support" msgid "Enable barcode scanner support"
msgstr "" msgstr ""
#: common/models.py:641 #: common/models.py:656
msgid "IPN Regex" msgid "IPN Regex"
msgstr "" msgstr ""
#: common/models.py:642 #: common/models.py:657
msgid "Regular expression pattern for matching Part IPN" msgid "Regular expression pattern for matching Part IPN"
msgstr "" msgstr ""
#: common/models.py:646 #: common/models.py:661
msgid "Allow Duplicate IPN" msgid "Allow Duplicate IPN"
msgstr "" msgstr ""
#: common/models.py:647 #: common/models.py:662
msgid "Allow multiple parts to share the same IPN" msgid "Allow multiple parts to share the same IPN"
msgstr "" msgstr ""
#: common/models.py:653 #: common/models.py:668
msgid "Allow Editing IPN" msgid "Allow Editing IPN"
msgstr "" msgstr ""
#: common/models.py:654 #: common/models.py:669
msgid "Allow changing the IPN value while editing a part" msgid "Allow changing the IPN value while editing a part"
msgstr "" msgstr ""
#: common/models.py:660 #: common/models.py:675
msgid "Copy Part BOM Data" msgid "Copy Part BOM Data"
msgstr "" msgstr ""
#: common/models.py:661 #: common/models.py:676
msgid "Copy BOM data by default when duplicating a part" msgid "Copy BOM data by default when duplicating a part"
msgstr "" msgstr ""
#: common/models.py:667 #: common/models.py:682
msgid "Copy Part Parameter Data" msgid "Copy Part Parameter Data"
msgstr "" msgstr ""
#: common/models.py:668 #: common/models.py:683
msgid "Copy parameter data by default when duplicating a part" msgid "Copy parameter data by default when duplicating a part"
msgstr "" msgstr ""
#: common/models.py:674 #: common/models.py:689
msgid "Copy Part Test Data" msgid "Copy Part Test Data"
msgstr "" msgstr ""
#: common/models.py:675 #: common/models.py:690
msgid "Copy test data by default when duplicating a part" msgid "Copy test data by default when duplicating a part"
msgstr "" msgstr ""
#: common/models.py:681 #: common/models.py:696
msgid "Copy Category Parameter Templates" msgid "Copy Category Parameter Templates"
msgstr "" msgstr ""
#: common/models.py:682 #: common/models.py:697
msgid "Copy category parameter templates when creating a part" msgid "Copy category parameter templates when creating a part"
msgstr "" msgstr ""
#: common/models.py:688 part/models.py:2429 report/models.py:187 #: common/models.py:703 part/models.py:2429 report/models.py:187
#: templates/js/translated/table_filters.js:38 #: templates/js/translated/table_filters.js:38
#: templates/js/translated/table_filters.js:367 #: templates/js/translated/table_filters.js:367
msgid "Template" msgid "Template"
msgstr "" msgstr ""
#: common/models.py:689 #: common/models.py:704
msgid "Parts are templates by default" msgid "Parts are templates by default"
msgstr "" msgstr ""
#: common/models.py:695 part/models.py:888 templates/js/translated/bom.js:954 #: common/models.py:710 part/models.py:888 templates/js/translated/bom.js:954
#: templates/js/translated/table_filters.js:162 #: templates/js/translated/table_filters.js:162
#: templates/js/translated/table_filters.js:379 #: templates/js/translated/table_filters.js:379
msgid "Assembly" msgid "Assembly"
msgstr "" msgstr ""
#: common/models.py:696 #: common/models.py:711
msgid "Parts can be assembled from other components by default" msgid "Parts can be assembled from other components by default"
msgstr "" msgstr ""
#: common/models.py:702 part/models.py:894 #: common/models.py:717 part/models.py:894
#: templates/js/translated/table_filters.js:383 #: templates/js/translated/table_filters.js:383
msgid "Component" msgid "Component"
msgstr "" msgstr ""
#: common/models.py:703 #: common/models.py:718
msgid "Parts can be used as sub-components by default" msgid "Parts can be used as sub-components by default"
msgstr "" msgstr ""
#: common/models.py:709 part/models.py:905 #: common/models.py:724 part/models.py:905
msgid "Purchaseable" msgid "Purchaseable"
msgstr "" msgstr ""
#: common/models.py:710 #: common/models.py:725
msgid "Parts are purchaseable by default" msgid "Parts are purchaseable by default"
msgstr "" msgstr ""
#: common/models.py:716 part/models.py:910 #: common/models.py:731 part/models.py:910
#: templates/js/translated/table_filters.js:391 #: templates/js/translated/table_filters.js:391
msgid "Salable" msgid "Salable"
msgstr "" msgstr ""
#: common/models.py:717 #: common/models.py:732
msgid "Parts are salable by default" msgid "Parts are salable by default"
msgstr "" msgstr ""
#: common/models.py:723 part/models.py:900 #: common/models.py:738 part/models.py:900
#: templates/js/translated/table_filters.js:46 #: templates/js/translated/table_filters.js:46
#: templates/js/translated/table_filters.js:94 #: templates/js/translated/table_filters.js:94
#: templates/js/translated/table_filters.js:395 #: templates/js/translated/table_filters.js:395
msgid "Trackable" msgid "Trackable"
msgstr "" msgstr ""
#: common/models.py:724 #: common/models.py:739
msgid "Parts are trackable by default" msgid "Parts are trackable by default"
msgstr "" msgstr ""
#: common/models.py:730 part/models.py:920 #: common/models.py:745 part/models.py:920
#: part/templates/part/part_base.html:144 #: part/templates/part/part_base.html:144
#: templates/js/translated/table_filters.js:42 #: templates/js/translated/table_filters.js:42
msgid "Virtual" msgid "Virtual"
msgstr "" msgstr ""
#: common/models.py:731 #: common/models.py:746
msgid "Parts are virtual by default" msgid "Parts are virtual by default"
msgstr "" msgstr ""
#: common/models.py:737 #: common/models.py:752
msgid "Show Import in Views" msgid "Show Import in Views"
msgstr "" msgstr ""
#: common/models.py:738 #: common/models.py:753
msgid "Display the import wizard in some part views" msgid "Display the import wizard in some part views"
msgstr "" msgstr ""
#: common/models.py:744 #: common/models.py:759
msgid "Show Price in Forms" msgid "Show Price in Forms"
msgstr "" msgstr ""
#: common/models.py:745 #: common/models.py:760
msgid "Display part price in some forms" msgid "Display part price in some forms"
msgstr "" msgstr ""
#: common/models.py:756 #: common/models.py:771
msgid "Show Price in BOM" msgid "Show Price in BOM"
msgstr "" msgstr ""
#: common/models.py:757 #: common/models.py:772
msgid "Include pricing information in BOM tables" msgid "Include pricing information in BOM tables"
msgstr "" msgstr ""
#: common/models.py:763 #: common/models.py:778
msgid "Show related parts" msgid "Show related parts"
msgstr "" msgstr ""
#: common/models.py:764 #: common/models.py:779
msgid "Display related parts for a part" msgid "Display related parts for a part"
msgstr "" msgstr ""
#: common/models.py:770 #: common/models.py:785
msgid "Create initial stock" msgid "Create initial stock"
msgstr "" msgstr ""
#: common/models.py:771 #: common/models.py:786
msgid "Create initial stock on part creation" msgid "Create initial stock on part creation"
msgstr "" msgstr ""
#: common/models.py:777 #: common/models.py:792
msgid "Internal Prices" msgid "Internal Prices"
msgstr "" msgstr ""
#: common/models.py:778 #: common/models.py:793
msgid "Enable internal prices for parts" msgid "Enable internal prices for parts"
msgstr "" msgstr ""
#: common/models.py:784 #: common/models.py:799
msgid "Internal Price as BOM-Price" msgid "Internal Price as BOM-Price"
msgstr "" msgstr ""
#: common/models.py:785 #: common/models.py:800
msgid "Use the internal price (if set) in BOM-price calculations" msgid "Use the internal price (if set) in BOM-price calculations"
msgstr "" msgstr ""
#: common/models.py:791 #: common/models.py:806
msgid "Part Name Display Format" msgid "Part Name Display Format"
msgstr "" msgstr ""
#: common/models.py:792 #: common/models.py:807
msgid "Format to display the part name" msgid "Format to display the part name"
msgstr "" msgstr ""
#: common/models.py:799 #: common/models.py:814
msgid "Enable Reports" msgid "Enable Reports"
msgstr "" msgstr ""
#: common/models.py:800 #: common/models.py:815
msgid "Enable generation of reports" msgid "Enable generation of reports"
msgstr "" msgstr ""
#: common/models.py:806 templates/stats.html:25 #: common/models.py:821 templates/stats.html:25
msgid "Debug Mode" msgid "Debug Mode"
msgstr "" msgstr ""
#: common/models.py:807 #: common/models.py:822
msgid "Generate reports in debug mode (HTML output)" msgid "Generate reports in debug mode (HTML output)"
msgstr "" msgstr ""
#: common/models.py:813 #: common/models.py:828
msgid "Page Size" msgid "Page Size"
msgstr "" msgstr ""
#: common/models.py:814 #: common/models.py:829
msgid "Default page size for PDF reports" msgid "Default page size for PDF reports"
msgstr "" msgstr ""
#: common/models.py:824 #: common/models.py:839
msgid "Test Reports" msgid "Test Reports"
msgstr "" msgstr ""
#: common/models.py:825 #: common/models.py:840
msgid "Enable generation of test reports" msgid "Enable generation of test reports"
msgstr "" msgstr ""
#: common/models.py:831 #: common/models.py:846
msgid "Stock Expiry" msgid "Stock Expiry"
msgstr "" msgstr ""
#: common/models.py:832 #: common/models.py:847
msgid "Enable stock expiry functionality" msgid "Enable stock expiry functionality"
msgstr "" msgstr ""
#: common/models.py:838 #: common/models.py:853
msgid "Sell Expired Stock" msgid "Sell Expired Stock"
msgstr "" msgstr ""
#: common/models.py:839 #: common/models.py:854
msgid "Allow sale of expired stock" msgid "Allow sale of expired stock"
msgstr "" msgstr ""
#: common/models.py:845 #: common/models.py:860
msgid "Stock Stale Time" msgid "Stock Stale Time"
msgstr "" msgstr ""
#: common/models.py:846 #: common/models.py:861
msgid "Number of days stock items are considered stale before expiring" msgid "Number of days stock items are considered stale before expiring"
msgstr "" msgstr ""
#: common/models.py:848 #: common/models.py:863
msgid "days" msgid "days"
msgstr "" msgstr ""
#: common/models.py:853 #: common/models.py:868
msgid "Build Expired Stock" msgid "Build Expired Stock"
msgstr "" msgstr ""
#: common/models.py:854 #: common/models.py:869
msgid "Allow building with expired stock" msgid "Allow building with expired stock"
msgstr "" msgstr ""
#: common/models.py:860 #: common/models.py:875
msgid "Stock Ownership Control" msgid "Stock Ownership Control"
msgstr "" msgstr ""
#: common/models.py:861 #: common/models.py:876
msgid "Enable ownership control over stock locations and items" msgid "Enable ownership control over stock locations and items"
msgstr "" msgstr ""
#: common/models.py:867 #: common/models.py:882
msgid "Group by Part" msgid "Group by Part"
msgstr "" msgstr ""
#: common/models.py:868 #: common/models.py:883
msgid "Group stock items by part reference in table views" msgid "Group stock items by part reference in table views"
msgstr "" msgstr ""
#: common/models.py:874 #: common/models.py:889
msgid "Build Order Reference Prefix" msgid "Build Order Reference Prefix"
msgstr "" msgstr ""
#: common/models.py:875 #: common/models.py:890
msgid "Prefix value for build order reference" msgid "Prefix value for build order reference"
msgstr "" msgstr ""
#: common/models.py:880 #: common/models.py:895
msgid "Build Order Reference Regex" msgid "Build Order Reference Regex"
msgstr "" msgstr ""
#: common/models.py:881 #: common/models.py:896
msgid "Regular expression pattern for matching build order reference" msgid "Regular expression pattern for matching build order reference"
msgstr "" msgstr ""
#: common/models.py:885 #: common/models.py:900
msgid "Sales Order Reference Prefix" msgid "Sales Order Reference Prefix"
msgstr "" msgstr ""
#: common/models.py:886 #: common/models.py:901
msgid "Prefix value for sales order reference" msgid "Prefix value for sales order reference"
msgstr "" msgstr ""
#: common/models.py:891 #: common/models.py:906
msgid "Purchase Order Reference Prefix" msgid "Purchase Order Reference Prefix"
msgstr "" msgstr ""
#: common/models.py:892 #: common/models.py:907
msgid "Prefix value for purchase order reference" msgid "Prefix value for purchase order reference"
msgstr "" msgstr ""
#: common/models.py:898 #: common/models.py:913
msgid "Enable password forgot" msgid "Enable password forgot"
msgstr "" msgstr ""
#: common/models.py:899 #: common/models.py:914
msgid "Enable password forgot function on the login pages" msgid "Enable password forgot function on the login pages"
msgstr "" msgstr ""
#: common/models.py:904 #: common/models.py:919
msgid "Enable registration" msgid "Enable registration"
msgstr "" msgstr ""
#: common/models.py:905 #: common/models.py:920
msgid "Enable self-registration for users on the login pages" msgid "Enable self-registration for users on the login pages"
msgstr "" msgstr ""
#: common/models.py:910 #: common/models.py:925
msgid "Enable SSO" msgid "Enable SSO"
msgstr "" msgstr ""
#: common/models.py:911 #: common/models.py:926
msgid "Enable SSO on the login pages" msgid "Enable SSO on the login pages"
msgstr "" msgstr ""
#: common/models.py:916 #: common/models.py:931
msgid "Email required" msgid "Email required"
msgstr "" msgstr ""
#: common/models.py:917 #: common/models.py:932
msgid "Require user to supply mail on signup" msgid "Require user to supply mail on signup"
msgstr "" msgstr ""
#: common/models.py:922 #: common/models.py:937
msgid "Auto-fill SSO users" msgid "Auto-fill SSO users"
msgstr "" msgstr ""
#: common/models.py:923 #: common/models.py:938
msgid "Automatically fill out user-details from SSO account-data" msgid "Automatically fill out user-details from SSO account-data"
msgstr "" msgstr ""
#: common/models.py:928 #: common/models.py:943
msgid "Mail twice" msgid "Mail twice"
msgstr "" msgstr ""
#: common/models.py:929 #: common/models.py:944
msgid "On signup ask users twice for their mail" msgid "On signup ask users twice for their mail"
msgstr "" msgstr ""
#: common/models.py:934 #: common/models.py:949
msgid "Password twice" msgid "Password twice"
msgstr "" msgstr ""
#: common/models.py:935 #: common/models.py:950
msgid "On signup ask users twice for their password" msgid "On signup ask users twice for their password"
msgstr "" msgstr ""
#: common/models.py:940 #: common/models.py:955
msgid "Group on signup" msgid "Group on signup"
msgstr "" msgstr ""
#: common/models.py:941 #: common/models.py:956
msgid "Group to which new users are assigned on registration" msgid "Group to which new users are assigned on registration"
msgstr "" msgstr ""
#: common/models.py:986 #: common/models.py:1001
msgid "Show subscribed parts" msgid "Show subscribed parts"
msgstr "" msgstr ""
#: common/models.py:987 #: common/models.py:1002
msgid "Show subscribed parts on the homepage" msgid "Show subscribed parts on the homepage"
msgstr "" msgstr ""
#: common/models.py:992 #: common/models.py:1007
msgid "Show subscribed categories" msgid "Show subscribed categories"
msgstr "" msgstr ""
#: common/models.py:993 #: common/models.py:1008
msgid "Show subscribed part categories on the homepage" msgid "Show subscribed part categories on the homepage"
msgstr "" msgstr ""
#: common/models.py:998 #: common/models.py:1013
msgid "Show latest parts" msgid "Show latest parts"
msgstr "" msgstr ""
#: common/models.py:999 #: common/models.py:1014
msgid "Show latest parts on the homepage" msgid "Show latest parts on the homepage"
msgstr "" msgstr ""
#: common/models.py:1004 #: common/models.py:1019
msgid "Recent Part Count" msgid "Recent Part Count"
msgstr "" msgstr ""
#: common/models.py:1005 #: common/models.py:1020
msgid "Number of recent parts to display on index page" msgid "Number of recent parts to display on index page"
msgstr "" msgstr ""
#: common/models.py:1011 #: common/models.py:1026
msgid "Show unvalidated BOMs" msgid "Show unvalidated BOMs"
msgstr "" msgstr ""
#: common/models.py:1012 #: common/models.py:1027
msgid "Show BOMs that await validation on the homepage" msgid "Show BOMs that await validation on the homepage"
msgstr "" msgstr ""
#: common/models.py:1017 #: common/models.py:1032
msgid "Show recent stock changes" msgid "Show recent stock changes"
msgstr "" msgstr ""
#: common/models.py:1018 #: common/models.py:1033
msgid "Show recently changed stock items on the homepage" msgid "Show recently changed stock items on the homepage"
msgstr "" msgstr ""
#: common/models.py:1023 #: common/models.py:1038
msgid "Recent Stock Count" msgid "Recent Stock Count"
msgstr "" msgstr ""
#: common/models.py:1024 #: common/models.py:1039
msgid "Number of recent stock items to display on index page" msgid "Number of recent stock items to display on index page"
msgstr "" msgstr ""
#: common/models.py:1029 #: common/models.py:1044
msgid "Show low stock" msgid "Show low stock"
msgstr "" msgstr ""
#: common/models.py:1030 #: common/models.py:1045
msgid "Show low stock items on the homepage" msgid "Show low stock items on the homepage"
msgstr "" msgstr ""
#: common/models.py:1035 #: common/models.py:1050
msgid "Show depleted stock" msgid "Show depleted stock"
msgstr "" msgstr ""
#: common/models.py:1036 #: common/models.py:1051
msgid "Show depleted stock items on the homepage" msgid "Show depleted stock items on the homepage"
msgstr "" msgstr ""
#: common/models.py:1041 #: common/models.py:1056
msgid "Show needed stock" msgid "Show needed stock"
msgstr "" msgstr ""
#: common/models.py:1042 #: common/models.py:1057
msgid "Show stock items needed for builds on the homepage" msgid "Show stock items needed for builds on the homepage"
msgstr "" msgstr ""
#: common/models.py:1047 #: common/models.py:1062
msgid "Show expired stock" msgid "Show expired stock"
msgstr "" msgstr ""
#: common/models.py:1048 #: common/models.py:1063
msgid "Show expired stock items on the homepage" msgid "Show expired stock items on the homepage"
msgstr "" msgstr ""
#: common/models.py:1053 #: common/models.py:1068
msgid "Show stale stock" msgid "Show stale stock"
msgstr "" msgstr ""
#: common/models.py:1054 #: common/models.py:1069
msgid "Show stale stock items on the homepage" msgid "Show stale stock items on the homepage"
msgstr "" msgstr ""
#: common/models.py:1059 #: common/models.py:1074
msgid "Show pending builds" msgid "Show pending builds"
msgstr "" msgstr ""
#: common/models.py:1060 #: common/models.py:1075
msgid "Show pending builds on the homepage" msgid "Show pending builds on the homepage"
msgstr "" msgstr ""
#: common/models.py:1065 #: common/models.py:1080
msgid "Show overdue builds" msgid "Show overdue builds"
msgstr "" msgstr ""
#: common/models.py:1066 #: common/models.py:1081
msgid "Show overdue builds on the homepage" msgid "Show overdue builds on the homepage"
msgstr "" msgstr ""
#: common/models.py:1071 #: common/models.py:1086
msgid "Show outstanding POs" msgid "Show outstanding POs"
msgstr "" msgstr ""
#: common/models.py:1072 #: common/models.py:1087
msgid "Show outstanding POs on the homepage" msgid "Show outstanding POs on the homepage"
msgstr "" msgstr ""
#: common/models.py:1077 #: common/models.py:1092
msgid "Show overdue POs" msgid "Show overdue POs"
msgstr "" msgstr ""
#: common/models.py:1078 #: common/models.py:1093
msgid "Show overdue POs on the homepage" msgid "Show overdue POs on the homepage"
msgstr "" msgstr ""
#: common/models.py:1083 #: common/models.py:1098
msgid "Show outstanding SOs" msgid "Show outstanding SOs"
msgstr "" msgstr ""
#: common/models.py:1084 #: common/models.py:1099
msgid "Show outstanding SOs on the homepage" msgid "Show outstanding SOs on the homepage"
msgstr "" msgstr ""
#: common/models.py:1089 #: common/models.py:1104
msgid "Show overdue SOs" msgid "Show overdue SOs"
msgstr "" msgstr ""
#: common/models.py:1090 #: common/models.py:1105
msgid "Show overdue SOs on the homepage" msgid "Show overdue SOs on the homepage"
msgstr "" msgstr ""
#: common/models.py:1096 #: common/models.py:1111
msgid "Inline label display" msgid "Inline label display"
msgstr "" msgstr ""
#: common/models.py:1097 #: common/models.py:1112
msgid "Display PDF labels in the browser, instead of downloading as a file" msgid "Display PDF labels in the browser, instead of downloading as a file"
msgstr "" msgstr ""
#: common/models.py:1103 #: common/models.py:1118
msgid "Inline report display" msgid "Inline report display"
msgstr "" msgstr ""
#: common/models.py:1104 #: common/models.py:1119
msgid "Display PDF reports in the browser, instead of downloading as a file" msgid "Display PDF reports in the browser, instead of downloading as a file"
msgstr "" msgstr ""
#: common/models.py:1110 #: common/models.py:1125
msgid "Search Preview Results" msgid "Search Preview Results"
msgstr "" msgstr ""
#: common/models.py:1111 #: common/models.py:1126
msgid "Number of results to show in search preview window" msgid "Number of results to show in search preview window"
msgstr "" msgstr ""
#: common/models.py:1117 #: common/models.py:1132
msgid "Search Show Stock" msgid "Search Show Stock"
msgstr "" msgstr ""
#: common/models.py:1118 #: common/models.py:1133
msgid "Display stock levels in search preview window" msgid "Display stock levels in search preview window"
msgstr "" msgstr ""
#: common/models.py:1124 #: common/models.py:1139
msgid "Hide Inactive Parts" msgid "Hide Inactive Parts"
msgstr "" msgstr ""
#: common/models.py:1125 #: common/models.py:1140
msgid "Hide inactive parts in search preview window" msgid "Hide inactive parts in search preview window"
msgstr "" msgstr ""
#: common/models.py:1131 #: common/models.py:1146
msgid "Show Quantity in Forms" msgid "Show Quantity in Forms"
msgstr "" msgstr ""
#: common/models.py:1132 #: common/models.py:1147
msgid "Display available part quantity in some forms" msgid "Display available part quantity in some forms"
msgstr "" msgstr ""
#: common/models.py:1138 #: common/models.py:1153
msgid "Escape Key Closes Forms" msgid "Escape Key Closes Forms"
msgstr "" msgstr ""
#: common/models.py:1139 #: common/models.py:1154
msgid "Use the escape key to close modal forms" msgid "Use the escape key to close modal forms"
msgstr "" msgstr ""
#: common/models.py:1145 #: common/models.py:1160
msgid "Fixed Navbar" msgid "Fixed Navbar"
msgstr "" msgstr ""
#: common/models.py:1146 #: common/models.py:1161
msgid "InvenTree navbar position is fixed to the top of the screen" msgid "InvenTree navbar position is fixed to the top of the screen"
msgstr "" msgstr ""
#: common/models.py:1211 company/forms.py:43 #: common/models.py:1226 company/forms.py:43
msgid "Price break quantity" msgid "Price break quantity"
msgstr "" msgstr ""
#: common/models.py:1218 company/serializers.py:264 #: common/models.py:1233 company/serializers.py:264
#: company/templates/company/supplier_part.html:256 #: company/templates/company/supplier_part.html:256
#: templates/js/translated/part.js:1508 #: templates/js/translated/part.js:1508
msgid "Price" msgid "Price"
msgstr "" msgstr ""
#: common/models.py:1219 #: common/models.py:1234
msgid "Unit price at specified quantity" msgid "Unit price at specified quantity"
msgstr "" msgstr ""

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -49,7 +49,7 @@ class POList(generics.ListCreateAPIView):
""" """
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
item = serializer.save() item = serializer.save()
item.created_by = request.user item.created_by = request.user
item.save() item.save()
@ -404,7 +404,7 @@ class SOList(generics.ListCreateAPIView):
""" """
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
item = serializer.save() item = serializer.save()
item.created_by = request.user item.created_by = request.user
item.save() item.save()

View File

@ -772,7 +772,7 @@ class PurchaseOrderLineItem(OrderLineItem):
def get_base_part(self): def get_base_part(self):
""" """
Return the base part.Part object for the line item Return the base part.Part object for the line item
Note: Returns None if the SupplierPart is not set! Note: Returns None if the SupplierPart is not set!
""" """
if self.part is None: if self.part is None:

View File

@ -553,10 +553,10 @@ class SOLineItemSerializer(InvenTreeModelSerializer):
allocations = SalesOrderAllocationSerializer(many=True, read_only=True, location_detail=True) allocations = SalesOrderAllocationSerializer(many=True, read_only=True, location_detail=True)
quantity = InvenTreeDecimalField() quantity = InvenTreeDecimalField()
allocated = serializers.FloatField(source='allocated_quantity', read_only=True) allocated = serializers.FloatField(source='allocated_quantity', read_only=True)
fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True) fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True)
sale_price = InvenTreeMoneySerializer( sale_price = InvenTreeMoneySerializer(
allow_null=True allow_null=True
) )

View File

@ -5,7 +5,8 @@
{% block sidebar %} {% block sidebar %}
{% url "po-detail" order.id as url %} {% url "po-detail" order.id as url %}
{% include "sidebar_item.html" with url=url text="Return to Orders" icon="fa-undo" %} {% trans "Return to Orders" as text %}
{% include "sidebar_item.html" with url=url text=text icon="fa-undo" %}
{% endblock %} {% endblock %}
{% block page_content %} {% block page_content %}

View File

@ -2,7 +2,11 @@
{% load static %} {% load static %}
{% load inventree_extras %} {% load inventree_extras %}
{% include "sidebar_item.html" with label='order-items' text="Line Items" icon="fa-list-ol" %} {% trans "Line Items" as text %}
{% include "sidebar_item.html" with label='received-items' text="Received Stock" icon="fa-sign-in-alt" %} {% include "sidebar_item.html" with label='order-items' text=text icon="fa-list-ol" %}
{% include "sidebar_item.html" with label='order-attachments' text="Attachments" icon="fa-paperclip" %} {% trans "Received Stock" as text %}
{% include "sidebar_item.html" with label='order-notes' text="Notes" icon="fa-clipboard" %} {% include "sidebar_item.html" with label='received-items' text=text icon="fa-sign-in-alt" %}
{% trans "Attachments" as text %}
{% include "sidebar_item.html" with label='order-attachments' text=text icon="fa-paperclip" %}
{% trans "Notes" as text %}
{% include "sidebar_item.html" with label='order-notes' text=text icon="fa-clipboard" %}

View File

@ -2,7 +2,11 @@
{% load static %} {% load static %}
{% load inventree_extras %} {% load inventree_extras %}
{% include "sidebar_item.html" with label='order-items' text="Line Items" icon="fa-list-ol" %} {% trans "Line Items" as text %}
{% include "sidebar_item.html" with label='order-builds' text="Build Orders" icon="fa-tools" %} {% include "sidebar_item.html" with label='order-items' text=text icon="fa-list-ol" %}
{% include "sidebar_item.html" with label='order-attachments' text="Attachments" icon="fa-paperclip" %} {% trans "Build Orders" as text %}
{% include "sidebar_item.html" with label='order-notes' text="Notes" icon="fa-clipboard" %} {% include "sidebar_item.html" with label='order-builds' text=text icon="fa-tools" %}
{% trans "Attachments" as text %}
{% include "sidebar_item.html" with label='order-attachments' text=text icon="fa-paperclip" %}
{% trans "Notes" as text %}
{% include "sidebar_item.html" with label='order-notes' text=text icon="fa-clipboard" %}

View File

@ -228,7 +228,7 @@ class PurchaseOrderReceiveTest(OrderTest):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.assignRole('purchase_order.add') self.assignRole('purchase_order.add')
self.url = reverse('api-po-receive', kwargs={'pk': 1}) self.url = reverse('api-po-receive', kwargs={'pk': 1})

View File

@ -406,7 +406,7 @@ class PurchaseOrderUpload(FileManagementFormView):
def done(self, form_list, **kwargs): def done(self, form_list, **kwargs):
""" Once all the data is in, process it to add PurchaseOrderLineItem instances to the order """ """ Once all the data is in, process it to add PurchaseOrderLineItem instances to the order """
order = self.get_order() order = self.get_order()
items = self.get_clean_items() items = self.get_clean_items()
@ -432,7 +432,7 @@ class PurchaseOrderUpload(FileManagementFormView):
except IntegrityError: except IntegrityError:
# PurchaseOrderLineItem already exists # PurchaseOrderLineItem already exists
pass pass
return HttpResponseRedirect(reverse('po-detail', kwargs={'pk': self.kwargs['pk']})) return HttpResponseRedirect(reverse('po-detail', kwargs={'pk': self.kwargs['pk']}))
@ -449,7 +449,7 @@ class SalesOrderExport(AjaxView):
role_required = 'sales_order.view' role_required = 'sales_order.view'
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
order = get_object_or_404(SalesOrder, pk=self.kwargs.get('pk', None)) order = get_object_or_404(SalesOrder, pk=self.kwargs.get('pk', None))
export_format = request.GET.get('format', 'csv') export_format = request.GET.get('format', 'csv')

View File

@ -205,7 +205,7 @@ class BomItemResource(ModelResource):
# If we are not generating an "import" template, # If we are not generating an "import" template,
# just return the complete list of fields # just return the complete list of fields
if not self.is_importing: if not getattr(self, 'is_importing', False):
return fields return fields
# Otherwise, remove some fields we are not interested in # Otherwise, remove some fields we are not interested in

View File

@ -169,7 +169,7 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
""" """
API endpoint for detail view of a single PartCategory object API endpoint for detail view of a single PartCategory object
""" """
serializer_class = part_serializers.CategorySerializer serializer_class = part_serializers.CategorySerializer
queryset = PartCategory.objects.all() queryset = PartCategory.objects.all()
@ -222,7 +222,7 @@ class CategoryParameterList(generics.ListAPIView):
if category is not None: if category is not None:
try: try:
category = PartCategory.objects.get(pk=category) category = PartCategory.objects.get(pk=category)
fetch_parent = str2bool(params.get('fetch_parent', True)) fetch_parent = str2bool(params.get('fetch_parent', True))
@ -734,7 +734,7 @@ class PartList(generics.ListCreateAPIView):
raise ValidationError({ raise ValidationError({
'initial_stock_quantity': [_('Must be a valid quantity')], 'initial_stock_quantity': [_('Must be a valid quantity')],
}) })
initial_stock_location = request.data.get('initial_stock_location', None) initial_stock_location = request.data.get('initial_stock_location', None)
try: try:
@ -850,7 +850,7 @@ class PartList(generics.ListCreateAPIView):
id_values.append(val) id_values.append(val)
except ValueError: except ValueError:
pass pass
queryset = queryset.exclude(pk__in=id_values) queryset = queryset.exclude(pk__in=id_values)
# Exclude part variant tree? # Exclude part variant tree?
@ -1096,7 +1096,7 @@ class BomFilter(rest_filters.FilterSet):
queryset = queryset.filter(pk__in=pks) queryset = queryset.filter(pk__in=pks)
else: else:
queryset = queryset.exclude(pk__in=pks) queryset = queryset.exclude(pk__in=pks)
return queryset return queryset
# Filters for linked 'part' # Filters for linked 'part'
@ -1257,7 +1257,7 @@ class BomList(generics.ListCreateAPIView):
queryset = self.annotate_pricing(queryset) queryset = self.annotate_pricing(queryset)
return queryset return queryset
def include_pricing(self): def include_pricing(self):
""" """
Determine if pricing information should be included in the response Determine if pricing information should be included in the response
@ -1291,7 +1291,7 @@ class BomList(generics.ListCreateAPIView):
# Get default currency from settings # Get default currency from settings
default_currency = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY') default_currency = InvenTreeSetting.get_setting('INVENTREE_DEFAULT_CURRENCY')
if price: if price:
if currency and default_currency: if currency and default_currency:
try: try:
@ -1381,7 +1381,7 @@ class BomItemSubstituteList(generics.ListCreateAPIView):
serializer_class = part_serializers.BomItemSubstituteSerializer serializer_class = part_serializers.BomItemSubstituteSerializer
queryset = BomItemSubstitute.objects.all() queryset = BomItemSubstitute.objects.all()
filter_backends = [ filter_backends = [
DjangoFilterBackend, DjangoFilterBackend,
filters.SearchFilter, filters.SearchFilter,

View File

@ -59,7 +59,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
uids = [] uids = []
def add_items(items, level, cascade): def add_items(items, level, cascade=True):
# Add items at a given layer # Add items at a given layer
for item in items: for item in items:
@ -172,7 +172,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
# Filter manufacturer parts # Filter manufacturer parts
manufacturer_parts = ManufacturerPart.objects.filter(part__pk=b_part.pk).prefetch_related('supplier_parts') manufacturer_parts = ManufacturerPart.objects.filter(part__pk=b_part.pk).prefetch_related('supplier_parts')
for mp_idx, mp_part in enumerate(manufacturer_parts): for mp_idx, mp_part in enumerate(manufacturer_parts):
# Extract the "name" field of the Manufacturer (Company) # Extract the "name" field of the Manufacturer (Company)
@ -190,7 +190,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
# Generate a column name for this manufacturer # Generate a column name for this manufacturer
k_man = f'{_("Manufacturer")}_{mp_idx}' k_man = f'{_("Manufacturer")}_{mp_idx}'
k_mpn = f'{_("MPN")}_{mp_idx}' k_mpn = f'{_("MPN")}_{mp_idx}'
try: try:
manufacturer_cols[k_man].update({bom_idx: manufacturer_name}) manufacturer_cols[k_man].update({bom_idx: manufacturer_name})
manufacturer_cols[k_mpn].update({bom_idx: manufacturer_mpn}) manufacturer_cols[k_mpn].update({bom_idx: manufacturer_mpn})
@ -200,7 +200,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=Fa
# We wish to include supplier data for this manufacturer part # We wish to include supplier data for this manufacturer part
if supplier_data: if supplier_data:
for sp_idx, sp_part in enumerate(mp_part.supplier_parts.all()): for sp_idx, sp_part in enumerate(mp_part.supplier_parts.all()):
supplier_parts_used.add(sp_part) supplier_parts_used.add(sp_part)

View File

@ -1587,7 +1587,7 @@ class Part(MPTTModel):
# Exclude any parts that this part is used *in* (to prevent recursive BOMs) # Exclude any parts that this part is used *in* (to prevent recursive BOMs)
used_in = self.get_used_in().all() used_in = self.get_used_in().all()
parts = parts.exclude(id__in=[item.part.id for item in used_in]) parts = parts.exclude(id__in=[part.id for part in used_in])
return parts return parts
@ -2118,7 +2118,7 @@ class Part(MPTTModel):
""" """
Returns True if the total stock for this part is less than the minimum stock level Returns True if the total stock for this part is less than the minimum stock level
""" """
return self.get_stock_count() < self.minimum_stock return self.get_stock_count() < self.minimum_stock
@ -2155,7 +2155,7 @@ class PartSellPriceBreak(common.models.PriceBreak):
""" """
Represents a price break for selling this part Represents a price break for selling this part
""" """
@staticmethod @staticmethod
def get_api_url(): def get_api_url():
return reverse('api-part-sale-price-list') return reverse('api-part-sale-price-list')

View File

@ -446,9 +446,9 @@ class BomItemSerializer(InvenTreeModelSerializer):
purchase_price_min = MoneyField(max_digits=10, decimal_places=6, read_only=True) purchase_price_min = MoneyField(max_digits=10, decimal_places=6, read_only=True)
purchase_price_max = MoneyField(max_digits=10, decimal_places=6, read_only=True) purchase_price_max = MoneyField(max_digits=10, decimal_places=6, read_only=True)
purchase_price_avg = serializers.SerializerMethodField() purchase_price_avg = serializers.SerializerMethodField()
purchase_price_range = serializers.SerializerMethodField() purchase_price_range = serializers.SerializerMethodField()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@ -520,7 +520,7 @@ class BomItemSerializer(InvenTreeModelSerializer):
def get_purchase_price_avg(self, obj): def get_purchase_price_avg(self, obj):
""" Return purchase price average """ """ Return purchase price average """
try: try:
purchase_price_avg = obj.purchase_price_avg purchase_price_avg = obj.purchase_price_avg
except AttributeError: except AttributeError:

View File

@ -62,7 +62,7 @@ def notify_low_stock(part: part.models.Part):
def notify_low_stock_if_required(part: part.models.Part): def notify_low_stock_if_required(part: part.models.Part):
""" """
Check if the stock quantity has fallen below the minimum threshold of part. Check if the stock quantity has fallen below the minimum threshold of part.
If true, notify the users who have subscribed to the part If true, notify the users who have subscribed to the part
""" """

View File

@ -5,7 +5,8 @@
{% block sidebar %} {% block sidebar %}
{% url "part-detail" part.id as url %} {% url "part-detail" part.id as url %}
{% include "sidebar_link.html" with url=url text="Return to BOM" icon="fa-undo" %} {% trans "Return to BOM" as text %}
{% include "sidebar_link.html" with url=url text=text icon="fa-undo" %}
{% endblock %} {% endblock %}
{% block heading %} {% block heading %}

View File

@ -4,12 +4,16 @@
{% settings_value 'PART_SHOW_IMPORT' as show_import %} {% settings_value 'PART_SHOW_IMPORT' as show_import %}
{% include "sidebar_item.html" with label="subcategories" text="Subcategories" icon="fa-sitemap" %} {% trans "Subcategories" as text %}
{% include "sidebar_item.html" with label="parts" text="Parts" icon="fa-shapes" %} {% include "sidebar_item.html" with label="subcategories" text=text icon="fa-sitemap" %}
{% trans "Parts" as text %}
{% include "sidebar_item.html" with label="parts" text=text icon="fa-shapes" %}
{% if show_import and user.is_staff and roles.part.add %} {% if show_import and user.is_staff and roles.part.add %}
{% url "part-import" as url %} {% url "part-import" as url %}
{% include "sidebar_link.html" with url=url text="Import Parts" icon="fa-file-upload" %} {% trans "Import Parts" as text %}
{% include "sidebar_link.html" with url=url text=text icon="fa-file-upload" %}
{% endif %} {% endif %}
{% if category %} {% if category %}
{% include "sidebar_item.html" with label="parameters" text="Parameters" icon="fa-tasks" %} {% trans "Parameters" as text %}
{% include "sidebar_item.html" with label="parameters" text=text icon="fa-tasks" %}
{% endif %} {% endif %}

View File

@ -5,7 +5,8 @@
{% block sidebar %} {% block sidebar %}
{% url 'part-index' as url %} {% url 'part-index' as url %}
{% include "sidebar_link.html" with url=url text="Return to Parts" icon="fa-undo" %} {% trans "Return to Parts" as text %}
{% include "sidebar_link.html" with url=url text=text icon="fa-undo" %}
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -5,34 +5,49 @@
{% settings_value "PART_INTERNAL_PRICE" as show_internal_price %} {% settings_value "PART_INTERNAL_PRICE" as show_internal_price %}
{% settings_value 'PART_SHOW_RELATED' as show_related %} {% settings_value 'PART_SHOW_RELATED' as show_related %}
{% include "sidebar_item.html" with label="part-details" text="Details" icon="fa-shapes" %} {% trans "Details" as text %}
{% include "sidebar_item.html" with label="part-parameters" text="Parameters" icon="fa-th-list" %} {% include "sidebar_item.html" with label="part-details" text=text icon="fa-shapes" %}
{% trans "Parameters" as text %}
{% include "sidebar_item.html" with label="part-parameters" text=text icon="fa-th-list" %}
{% if part.is_template %} {% if part.is_template %}
{% include "sidebar_item.html" with label="variants" text="Variants" icon="fa-shapes" %} {% trans "Variants" as text %}
{% include "sidebar_item.html" with label="variants" text=text icon="fa-shapes" %}
{% endif %} {% endif %}
{% include "sidebar_item.html" with label="part-stock" text="Stock" icon="fa-boxes" %} {% trans "Stock" as text %}
{% include "sidebar_item.html" with label="part-stock" text=text icon="fa-boxes" %}
{% if part.assembly %} {% if part.assembly %}
{% include "sidebar_item.html" with label="bom" text="Bill of Materials" icon="fa-list" %} {% trans "Bill of Materials" as text %}
{% include "sidebar_item.html" with label="bom" text=text icon="fa-list" %}
{% if roles.build.view %} {% if roles.build.view %}
{% include "sidebar_item.html" with label="build-orders" text="Build Orders" icon="fa-tools" %} {% trans "Build Orders" as text %}
{% include "sidebar_item.html" with label="build-orders" text=text icon="fa-tools" %}
{% endif %} {% endif %}
{% endif %} {% endif %}
{% if part.component %} {% if part.component %}
{% include "sidebar_item.html" with label="used-in" text="Used In" icon="fa-layer-group" %} {% trans "Used In" as text %}
{% include "sidebar_item.html" with label="used-in" text=text icon="fa-layer-group" %}
{% endif %} {% endif %}
{% include "sidebar_item.html" with label="pricing" text="Pricing" icon="fa-dollar-sign" %} {% trans "Pricing" as text %}
{% include "sidebar_item.html" with label="pricing" text=text icon="fa-dollar-sign" %}
{% if part.purchaseable and roles.purchase_order.view %} {% if part.purchaseable and roles.purchase_order.view %}
{% include "sidebar_item.html" with label="suppliers" text="Suppliers" icon="fa-building" %} {% trans "Suppliers" as text %}
{% include "sidebar_item.html" with label="purchase-orders" text="Purchase Orders" icon="fa-shopping-cart" %} {% include "sidebar_item.html" with label="suppliers" text=text icon="fa-building" %}
{% trans "Purchase Orders" as text %}
{% include "sidebar_item.html" with label="purchase-orders" text=text icon="fa-shopping-cart" %}
{% endif %} {% endif %}
{% if part.salable and roles.sales_order.view %} {% if part.salable and roles.sales_order.view %}
{% include "sidebar_item.html" with label="sales-orders" text="Sales Orders" icon="fa-truck" %} {% trans "Sales Orders" as text %}
{% include "sidebar_item.html" with label="sales-orders" text=text icon="fa-truck" %}
{% endif %} {% endif %}
{% if part.trackable %} {% if part.trackable %}
{% include "sidebar_item.html" with label="test-templates" text="Test Templates" icon="fa-vial" %} {% trans "Test Templates" as text %}
{% include "sidebar_item.html" with label="test-templates" text=text icon="fa-vial" %}
{% endif %} {% endif %}
{% if show_related %} {% if show_related %}
{% include "sidebar_item.html" with label="related-parts" text="Related Parts" icon="fa-random" %} {% trans "Related Parts" as text %}
{% include "sidebar_item.html" with label="related-parts" text=text icon="fa-random" %}
{% endif %} {% endif %}
{% include "sidebar_item.html" with label="part-attachments" text="Attachments" icon="fa-paperclip" %} {% trans "Attachments" as text %}
{% include "sidebar_item.html" with label="part-notes" text="Notes" icon="fa-clipboard" %} {% include "sidebar_item.html" with label="part-attachments" text=text icon="fa-paperclip" %}
{% trans "Notes" as text %}
{% include "sidebar_item.html" with label="part-notes" text=text icon="fa-clipboard" %}

View File

@ -236,7 +236,7 @@ def settings_value(key, *args, **kwargs):
if 'user' in kwargs: if 'user' in kwargs:
return InvenTreeUserSetting.get_setting(key, user=kwargs['user']) return InvenTreeUserSetting.get_setting(key, user=kwargs['user'])
return InvenTreeSetting.get_setting(key) return InvenTreeSetting.get_setting(key)
@ -384,7 +384,7 @@ def keyvalue(dict, key):
def call_method(obj, method_name, *args): def call_method(obj, method_name, *args):
""" """
enables calling model methods / functions from templates with arguments enables calling model methods / functions from templates with arguments
usage: usage:
{% call_method model_object 'fnc_name' argument1 %} {% call_method model_object 'fnc_name' argument1 %}
""" """

View File

@ -542,7 +542,7 @@ class PartAPITest(InvenTreeAPITestCase):
# Check that there is a new manufacturer part *and* a new supplier part # Check that there is a new manufacturer part *and* a new supplier part
self.assertEqual(new_part.supplier_parts.count(), 1) self.assertEqual(new_part.supplier_parts.count(), 1)
self.assertEqual(new_part.manufacturer_parts.count(), 1) self.assertEqual(new_part.manufacturer_parts.count(), 1)
def test_strange_chars(self): def test_strange_chars(self):
""" """
Test that non-standard ASCII chars are accepted Test that non-standard ASCII chars are accepted
@ -911,7 +911,7 @@ class BomItemTest(InvenTreeAPITestCase):
# How many BOM items currently exist in the database? # How many BOM items currently exist in the database?
n = BomItem.objects.count() n = BomItem.objects.count()
url = reverse('api-bom-list') url = reverse('api-bom-list')
response = self.get(url, expected_code=200) response = self.get(url, expected_code=200)
self.assertEqual(len(response.data), n) self.assertEqual(len(response.data), n)
@ -962,7 +962,7 @@ class BomItemTest(InvenTreeAPITestCase):
} }
self.post(url, data, expected_code=201) self.post(url, data, expected_code=201)
# Now try to create a BomItem which references itself # Now try to create a BomItem which references itself
data['part'] = 100 data['part'] = 100
data['sub_part'] = 100 data['sub_part'] = 100
@ -1003,7 +1003,7 @@ class BomItemTest(InvenTreeAPITestCase):
# Now we will create some variant parts and stock # Now we will create some variant parts and stock
for ii in range(5): for ii in range(5):
# Create a variant part! # Create a variant part!
variant = Part.objects.create( variant = Part.objects.create(
name=f"Variant_{ii}", name=f"Variant_{ii}",

View File

@ -153,7 +153,7 @@ class BomItemTest(TestCase):
subs = [] subs = []
for ii in range(5): for ii in range(5):
# Create a new part # Create a new part
sub_part = Part.objects.create( sub_part = Part.objects.create(
name=f"Orphan {ii}", name=f"Orphan {ii}",
@ -181,7 +181,7 @@ class BomItemTest(TestCase):
# There should be now 5 substitute parts available # There should be now 5 substitute parts available
self.assertEqual(bom_item.substitutes.count(), 5) self.assertEqual(bom_item.substitutes.count(), 5)
# Try to create a substitute which points to the same sub-part (should fail) # Try to create a substitute which points to the same sub-part (should fail)
with self.assertRaises(django_exceptions.ValidationError): with self.assertRaises(django_exceptions.ValidationError):
BomItemSubstitute.objects.create( BomItemSubstitute.objects.create(

View File

@ -370,7 +370,7 @@ class PartSubscriptionTests(TestCase):
# electronics / IC / MCU # electronics / IC / MCU
self.category = PartCategory.objects.get(pk=4) self.category = PartCategory.objects.get(pk=4)
self.part = Part.objects.create( self.part = Part.objects.create(
category=self.category, category=self.category,
name='STM32F103', name='STM32F103',
@ -382,7 +382,7 @@ class PartSubscriptionTests(TestCase):
""" """
Test basic subscription against a part Test basic subscription against a part
""" """
# First check that the user is *not* subscribed to the part # First check that the user is *not* subscribed to the part
self.assertFalse(self.part.is_starred_by(self.user)) self.assertFalse(self.part.is_starred_by(self.user))
@ -450,7 +450,7 @@ class PartSubscriptionTests(TestCase):
""" """
Check that a parent category can be subscribed to Check that a parent category can be subscribed to
""" """
# Top-level "electronics" category # Top-level "electronics" category
cat = PartCategory.objects.get(pk=1) cat = PartCategory.objects.get(pk=1)

View File

@ -40,7 +40,7 @@ part_detail_urls = [
url(r'^bom-export/?', views.BomExport.as_view(), name='bom-export'), url(r'^bom-export/?', views.BomExport.as_view(), name='bom-export'),
url(r'^bom-download/?', views.BomDownload.as_view(), name='bom-download'), url(r'^bom-download/?', views.BomDownload.as_view(), name='bom-download'),
url(r'^validate-bom/', views.BomValidate.as_view(), name='bom-validate'), url(r'^validate-bom/', views.BomValidate.as_view(), name='bom-validate'),
url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'), url(r'^pricing/', views.PartPricing.as_view(), name='part-pricing'),
url(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'), url(r'^bom-upload/?', views.BomUpload.as_view(), name='upload-bom'),

View File

@ -459,7 +459,7 @@ class PartDetail(InvenTreeRoleMixin, DetailView):
part = self.get_object() part = self.get_object()
ctx = part.get_context_data(self.request) ctx = part.get_context_data(self.request)
context.update(**ctx) context.update(**ctx)
# Pricing information # Pricing information
@ -1056,7 +1056,7 @@ class BomUpload(InvenTreeRoleMixin, FileManagementFormView):
matches = sorted(matches, key=lambda item: item['match'], reverse=True) matches = sorted(matches, key=lambda item: item['match'], reverse=True)
part_options = [m['part'] for m in matches] part_options = [m['part'] for m in matches]
# Supply list of part options for each row, sorted by how closely they match the part name # Supply list of part options for each row, sorted by how closely they match the part name
row['item_options'] = part_options row['item_options'] = part_options
@ -1520,11 +1520,11 @@ class CategoryDetail(InvenTreeRoleMixin, DetailView):
# Prefetch parts parameters # Prefetch parts parameters
parts_parameters = category.prefetch_parts_parameters(cascade=cascade) parts_parameters = category.prefetch_parts_parameters(cascade=cascade)
# Get table headers (unique parameters names) # Get table headers (unique parameters names)
context['headers'] = category.get_unique_parameters(cascade=cascade, context['headers'] = category.get_unique_parameters(cascade=cascade,
prefetch=parts_parameters) prefetch=parts_parameters)
# Insert part information # Insert part information
context['headers'].insert(0, 'description') context['headers'].insert(0, 'description')
context['headers'].insert(0, 'part') context['headers'].insert(0, 'part')

View File

@ -247,7 +247,7 @@ class ReportTemplateBase(ReportBase):
""" """
template_string = Template(self.filename_pattern) template_string = Template(self.filename_pattern)
ctx = self.context(request) ctx = self.context(request)
context = Context(ctx) context = Context(ctx)

View File

@ -106,7 +106,7 @@ class ReportTest(InvenTreeAPITestCase):
# Filter by "enabled" status # Filter by "enabled" status
response = self.get(url, {'enabled': True}) response = self.get(url, {'enabled': True})
self.assertEqual(len(response.data), n) self.assertEqual(len(response.data), n)
response = self.get(url, {'enabled': False}) response = self.get(url, {'enabled': False})
self.assertEqual(len(response.data), 0) self.assertEqual(len(response.data), 0)
@ -118,7 +118,7 @@ class ReportTest(InvenTreeAPITestCase):
# Filter by "enabled" status # Filter by "enabled" status
response = self.get(url, {'enabled': True}) response = self.get(url, {'enabled': True})
self.assertEqual(len(response.data), 0) self.assertEqual(len(response.data), 0)
response = self.get(url, {'enabled': False}) response = self.get(url, {'enabled': False})
self.assertEqual(len(response.data), n) self.assertEqual(len(response.data), n)
@ -199,7 +199,7 @@ class BuildReportTest(ReportTest):
build = Build.objects.first() build = Build.objects.first()
response = self.get(url, {'build': build.pk}) response = self.get(url, {'build': build.pk})
self.assertEqual(type(response), StreamingHttpResponse) self.assertEqual(type(response), StreamingHttpResponse)
headers = response.headers headers = response.headers

View File

@ -91,7 +91,7 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
Instead of "deleting" the StockItem Instead of "deleting" the StockItem
(which may take a long time) (which may take a long time)
we instead schedule it for deletion at a later date. we instead schedule it for deletion at a later date.
The background worker will delete these in the future The background worker will delete these in the future
""" """
@ -134,7 +134,7 @@ class StockAdjustView(generics.CreateAPIView):
queryset = StockItem.objects.none() queryset = StockItem.objects.none()
def get_serializer_context(self): def get_serializer_context(self):
context = super().get_serializer_context() context = super().get_serializer_context()
context['request'] = self.request context['request'] = self.request
@ -348,7 +348,7 @@ class StockFilter(rest_filters.FilterSet):
queryset = queryset.exclude(customer=None) queryset = queryset.exclude(customer=None)
else: else:
queryset = queryset.filter(customer=None) queryset = queryset.filter(customer=None)
return queryset return queryset
depleted = rest_filters.BooleanFilter(label='Depleted', method='filter_depleted') depleted = rest_filters.BooleanFilter(label='Depleted', method='filter_depleted')
@ -437,7 +437,7 @@ class StockList(generics.ListCreateAPIView):
}) })
with transaction.atomic(): with transaction.atomic():
# Create an initial stock item # Create an initial stock item
item = serializer.save() item = serializer.save()

View File

@ -83,7 +83,7 @@ class ConvertStockItemForm(HelperForm):
class CreateStockItemForm(HelperForm): class CreateStockItemForm(HelperForm):
""" """
Form for creating a new StockItem Form for creating a new StockItem
TODO: Migrate this form to the modern API forms interface TODO: Migrate this form to the modern API forms interface
""" """
@ -143,7 +143,7 @@ class CreateStockItemForm(HelperForm):
class SerializeStockForm(HelperForm): class SerializeStockForm(HelperForm):
""" """
Form for serializing a StockItem. Form for serializing a StockItem.
TODO: Migrate this form to the modern API forms interface TODO: Migrate this form to the modern API forms interface
""" """

View File

@ -272,7 +272,7 @@ class StockItem(MPTTModel):
add_note = kwargs.pop('add_note', True) add_note = kwargs.pop('add_note', True)
notes = kwargs.pop('notes', '') notes = kwargs.pop('notes', '')
if self.pk: if self.pk:
# StockItem has already been saved # StockItem has already been saved

View File

@ -56,7 +56,7 @@ class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
location_name = serializers.CharField(source='location', read_only=True) location_name = serializers.CharField(source='location', read_only=True)
part_name = serializers.CharField(source='part.full_name', read_only=True) part_name = serializers.CharField(source='part.full_name', read_only=True)
quantity = InvenTreeDecimalField() quantity = InvenTreeDecimalField()
class Meta: class Meta:
@ -615,7 +615,7 @@ class StockCountSerializer(StockAdjustmentSerializer):
stock_item = item['pk'] stock_item = item['pk']
quantity = item['quantity'] quantity = item['quantity']
stock_item.stocktake( stock_item.stocktake(
quantity, quantity,
request.user, request.user,
@ -654,7 +654,7 @@ class StockRemoveSerializer(StockAdjustmentSerializer):
""" """
def save(self): def save(self):
request = self.context['request'] request = self.context['request']
data = self.validated_data data = self.validated_data
@ -707,7 +707,7 @@ class StockTransferSerializer(StockAdjustmentSerializer):
request = self.context['request'] request = self.context['request']
data = self.validated_data data = self.validated_data
items = data['items'] items = data['items']
notes = data.get('notes', '') notes = data.get('notes', '')
location = data['location'] location = data['location']

View File

@ -2,5 +2,7 @@
{% load static %} {% load static %}
{% load inventree_extras %} {% load inventree_extras %}
{% include "sidebar_item.html" with label='sublocations' text="Sublocations" icon="fa-sitemap" %} {% trans "Sublocations" as text %}
{% include "sidebar_item.html" with label='stock' text="Stock Items" icon="fa-boxes" %} {% include "sidebar_item.html" with label='sublocations' text=text icon="fa-sitemap" %}
{% trans "Stock Items" as text %}
{% include "sidebar_item.html" with label='stock' text=text icon="fa-boxes" %}

Some files were not shown because too many files have changed in this diff Show More