mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge pull request #1024 from SchrodingersGat/part-permissions
Permissions
This commit is contained in:
commit
606c62078f
@ -30,6 +30,8 @@ class InfoView(AjaxView):
|
||||
Use to confirm that the server is running, etc.
|
||||
"""
|
||||
|
||||
permission_classes = [permissions.AllowAny]
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
data = {
|
||||
|
@ -17,3 +17,43 @@ def status_codes(request):
|
||||
'BuildStatus': BuildStatus,
|
||||
'StockStatus': StockStatus,
|
||||
}
|
||||
|
||||
|
||||
def user_roles(request):
|
||||
"""
|
||||
Return a map of the current roles assigned to the user.
|
||||
|
||||
Roles are denoted by their simple names, and then the permission type.
|
||||
|
||||
Permissions can be access as follows:
|
||||
|
||||
- roles.part.view
|
||||
- roles.build.delete
|
||||
|
||||
Each value will return a boolean True / False
|
||||
"""
|
||||
|
||||
user = request.user
|
||||
|
||||
roles = {
|
||||
}
|
||||
|
||||
for group in user.groups.all():
|
||||
for rule in group.rule_sets.all():
|
||||
|
||||
# Ensure the role name is in the dict
|
||||
if rule.name not in roles:
|
||||
roles[rule.name] = {
|
||||
'view': user.is_superuser,
|
||||
'add': user.is_superuser,
|
||||
'change': user.is_superuser,
|
||||
'delete': user.is_superuser
|
||||
}
|
||||
|
||||
# Roles are additive across groups
|
||||
roles[rule.name]['view'] |= rule.can_view
|
||||
roles[rule.name]['add'] |= rule.can_add
|
||||
roles[rule.name]['change'] |= rule.can_change
|
||||
roles[rule.name]['delete'] |= rule.can_delete
|
||||
|
||||
return {'roles': roles}
|
||||
|
@ -15,6 +15,8 @@ from django.http import StreamingHttpResponse
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from django.contrib.auth.models import Permission
|
||||
|
||||
import InvenTree.version
|
||||
|
||||
from .settings import MEDIA_URL, STATIC_URL
|
||||
@ -441,3 +443,21 @@ def validateFilterString(value):
|
||||
results[k] = v
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def addUserPermission(user, permission):
|
||||
"""
|
||||
Shortcut function for adding a certain permission to a user.
|
||||
"""
|
||||
|
||||
perm = Permission.objects.get(codename=permission)
|
||||
user.user_permissions.add(perm)
|
||||
|
||||
|
||||
def addUserPermissions(user, permissions):
|
||||
"""
|
||||
Shortcut function for adding multiple permissions to a user.
|
||||
"""
|
||||
|
||||
for permission in permissions:
|
||||
addUserPermission(user, permission)
|
||||
|
@ -210,6 +210,7 @@ TEMPLATES = [
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
'InvenTree.context.status_codes',
|
||||
'InvenTree.context.user_roles',
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -231,6 +232,10 @@ REST_FRAMEWORK = {
|
||||
'rest_framework.authentication.SessionAuthentication',
|
||||
'rest_framework.authentication.TokenAuthentication',
|
||||
),
|
||||
'DEFAULT_PERMISSION_CLASSES': (
|
||||
'rest_framework.permissions.IsAuthenticated',
|
||||
'rest_framework.permissions.DjangoModelPermissions',
|
||||
),
|
||||
'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema'
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@ from django.views.generic.base import TemplateView
|
||||
from part.models import Part, PartCategory
|
||||
from stock.models import StockLocation, StockItem
|
||||
from common.models import InvenTreeSetting, ColorTheme
|
||||
from users.models import check_user_role
|
||||
|
||||
from .forms import DeleteForm, EditUserForm, SetPasswordForm, ColorThemeSelectForm
|
||||
from .helpers import str2bool
|
||||
@ -107,31 +108,66 @@ class TreeSerializer(views.APIView):
|
||||
return JsonResponse(response, safe=False)
|
||||
|
||||
|
||||
class AjaxMixin(PermissionRequiredMixin):
|
||||
class InvenTreeRoleMixin(PermissionRequiredMixin):
|
||||
"""
|
||||
Permission class based on user roles, not user 'permissions'.
|
||||
|
||||
To specify which role is required for the mixin,
|
||||
set the class attribute 'role_required' to something like the following:
|
||||
|
||||
role_required = 'part.add'
|
||||
role_required = [
|
||||
'part.change',
|
||||
'build.add',
|
||||
]
|
||||
"""
|
||||
|
||||
# By default, no roles are required
|
||||
# Roles must be specified
|
||||
role_required = None
|
||||
|
||||
def has_permission(self):
|
||||
"""
|
||||
Determine if the current user
|
||||
"""
|
||||
|
||||
roles_required = []
|
||||
|
||||
if type(self.role_required) is str:
|
||||
roles_required.append(self.role_required)
|
||||
elif type(self.role_required) in [list, tuple]:
|
||||
roles_required = self.role_required
|
||||
|
||||
user = self.request.user
|
||||
|
||||
# Superuser can have any permissions they desire
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
for required in roles_required:
|
||||
|
||||
(role, permission) = required.split('.')
|
||||
|
||||
# Return False if the user does not have *any* of the required roles
|
||||
if not check_user_role(user, role, permission):
|
||||
return False
|
||||
|
||||
# We did not fail any required checks
|
||||
return True
|
||||
|
||||
|
||||
class AjaxMixin(InvenTreeRoleMixin):
|
||||
""" AjaxMixin provides basic functionality for rendering a Django form to JSON.
|
||||
Handles jsonResponse rendering, and adds extra data for the modal forms to process
|
||||
on the client side.
|
||||
|
||||
Any view which inherits the AjaxMixin will need
|
||||
correct permissions set using the 'permission_required' attribute
|
||||
correct permissions set using the 'role_required' attribute
|
||||
|
||||
"""
|
||||
|
||||
# By default, allow *any* permissions
|
||||
permission_required = '*'
|
||||
|
||||
def has_permission(self):
|
||||
"""
|
||||
Override the default behaviour of has_permission from PermissionRequiredMixin.
|
||||
|
||||
Basically, if permission_required attribute = '*',
|
||||
no permissions are actually required!
|
||||
"""
|
||||
|
||||
if self.permission_required == '*':
|
||||
return True
|
||||
else:
|
||||
return super().has_permission()
|
||||
# By default, allow *any* role
|
||||
role_required = None
|
||||
|
||||
# By default, point to the modal_form template
|
||||
# (this can be overridden by a child class)
|
||||
|
@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import filters
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework import generics
|
||||
|
||||
from django.conf.urls import url, include
|
||||
|
||||
@ -28,10 +28,6 @@ class BuildList(generics.ListCreateAPIView):
|
||||
queryset = Build.objects.all()
|
||||
serializer_class = BuildSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -99,10 +95,6 @@ class BuildDetail(generics.RetrieveUpdateAPIView):
|
||||
queryset = Build.objects.all()
|
||||
serializer_class = BuildSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
|
||||
class BuildItemList(generics.ListCreateAPIView):
|
||||
""" API endpoint for accessing a list of BuildItem objects
|
||||
@ -137,10 +129,6 @@ class BuildItemList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
@ -35,7 +35,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<hr>
|
||||
<h4>
|
||||
{{ build.quantity }} x {{ build.part.full_name }}
|
||||
{% if user.is_staff and perms.build.change_build %}
|
||||
{% if user.is_staff and roles.build.change %}
|
||||
<a href="{% url 'admin:build_build_change' build.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h4>
|
||||
|
@ -7,7 +7,7 @@ from __future__ import unicode_literals
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import filters
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework import generics
|
||||
|
||||
from django.conf.urls import url, include
|
||||
from django.db.models import Q
|
||||
@ -40,10 +40,6 @@ class CompanyList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -82,10 +78,6 @@ class CompanyDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
|
||||
class SupplierPartList(generics.ListCreateAPIView):
|
||||
""" API endpoint for list view of SupplierPart object
|
||||
@ -170,10 +162,6 @@ class SupplierPartList(generics.ListCreateAPIView):
|
||||
|
||||
serializer_class = SupplierPartSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -202,7 +190,6 @@ class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
||||
queryset = SupplierPart.objects.all()
|
||||
serializer_class = SupplierPartSerializer
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
read_only_fields = [
|
||||
]
|
||||
@ -218,10 +205,6 @@ class SupplierPriceBreakList(generics.ListCreateAPIView):
|
||||
queryset = SupplierPriceBreak.objects.all()
|
||||
serializer_class = SupplierPriceBreakSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
|
@ -23,7 +23,7 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
|
||||
<hr>
|
||||
<h4>
|
||||
{{ company.name }}
|
||||
{% if user.is_staff and perms.company.change_company %}
|
||||
{% if user.is_staff and roles.company.change %}
|
||||
<a href="{% url 'admin:company_company_change' company.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h4>
|
||||
|
@ -3,6 +3,8 @@ from rest_framework import status
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from InvenTree.helpers import addUserPermissions
|
||||
|
||||
from .models import Company
|
||||
|
||||
|
||||
@ -14,7 +16,16 @@ class CompanyTest(APITestCase):
|
||||
def setUp(self):
|
||||
# Create a user for auth
|
||||
User = get_user_model()
|
||||
User.objects.create_user('testuser', 'test@testing.com', 'password')
|
||||
self.user = User.objects.create_user('testuser', 'test@testing.com', 'password')
|
||||
|
||||
perms = [
|
||||
'view_company',
|
||||
'change_company',
|
||||
'add_company',
|
||||
]
|
||||
|
||||
addUserPermissions(self.user, perms)
|
||||
|
||||
self.client.login(username='testuser', password='password')
|
||||
|
||||
Company.objects.create(name='ACME', description='Supplier', is_customer=False, is_supplier=True)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-10-04 14:02+0000\n"
|
||||
"POT-Creation-Date: 2020-10-05 13:20+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -90,7 +90,7 @@ msgstr ""
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/models.py:106 part/templates/part/params.html:20
|
||||
#: InvenTree/models.py:106 part/templates/part/params.html:22
|
||||
#: templates/js/part.html:81
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
@ -99,19 +99,19 @@ msgstr ""
|
||||
msgid "Description (optional)"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:342
|
||||
#: InvenTree/settings.py:343
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:343
|
||||
#: InvenTree/settings.py:344
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:344
|
||||
#: InvenTree/settings.py:345
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:345
|
||||
#: InvenTree/settings.py:346
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
@ -325,7 +325,7 @@ msgstr ""
|
||||
msgid "Number of parts to build"
|
||||
msgstr ""
|
||||
|
||||
#: build/models.py:128 part/templates/part/part_base.html:145
|
||||
#: build/models.py:128 part/templates/part/part_base.html:155
|
||||
msgid "Build Status"
|
||||
msgstr ""
|
||||
|
||||
@ -344,7 +344,7 @@ msgstr ""
|
||||
#: build/models.py:155 build/templates/build/detail.html:55
|
||||
#: company/templates/company/supplier_part_base.html:60
|
||||
#: company/templates/company/supplier_part_detail.html:24
|
||||
#: part/templates/part/detail.html:80 part/templates/part/part_base.html:92
|
||||
#: part/templates/part/detail.html:80 part/templates/part/part_base.html:102
|
||||
#: stock/models.py:381 stock/templates/stock/item_base.html:244
|
||||
msgid "External Link"
|
||||
msgstr ""
|
||||
@ -356,7 +356,7 @@ msgstr ""
|
||||
#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:310
|
||||
#: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15
|
||||
#: order/templates/order/purchase_order_detail.html:202
|
||||
#: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:67
|
||||
#: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:70
|
||||
#: stock/forms.py:306 stock/forms.py:338 stock/forms.py:366 stock/models.py:453
|
||||
#: stock/models.py:1404 stock/templates/stock/tabs.html:26
|
||||
#: templates/js/barcode.html:391 templates/js/bom.html:223
|
||||
@ -404,7 +404,7 @@ msgstr ""
|
||||
|
||||
#: build/templates/build/allocate.html:17
|
||||
#: company/templates/company/detail_part.html:18 order/views.py:779
|
||||
#: part/templates/part/category.html:112
|
||||
#: part/templates/part/category.html:122
|
||||
msgid "Order Parts"
|
||||
msgstr ""
|
||||
|
||||
@ -420,7 +420,7 @@ msgstr ""
|
||||
msgid "Unallocate"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/allocate.html:87 templates/stock_table.html:9
|
||||
#: build/templates/build/allocate.html:87 templates/stock_table.html:10
|
||||
msgid "New Stock Item"
|
||||
msgstr ""
|
||||
|
||||
@ -548,7 +548,7 @@ msgstr ""
|
||||
#: build/templates/build/build_base.html:34
|
||||
#: build/templates/build/complete.html:6
|
||||
#: stock/templates/stock/item_base.html:223 templates/js/build.html:39
|
||||
#: templates/navbar.html:20
|
||||
#: templates/navbar.html:25
|
||||
msgid "Build"
|
||||
msgstr ""
|
||||
|
||||
@ -687,7 +687,7 @@ msgstr ""
|
||||
|
||||
#: build/templates/build/index.html:6 build/templates/build/index.html:14
|
||||
#: order/templates/order/so_builds.html:11 order/templates/order/so_tabs.html:9
|
||||
#: part/templates/part/tabs.html:30
|
||||
#: part/templates/part/tabs.html:31 users/models.py:30
|
||||
msgid "Build Orders"
|
||||
msgstr ""
|
||||
|
||||
@ -709,7 +709,7 @@ msgstr ""
|
||||
#: build/templates/build/notes.html:33 company/templates/company/notes.html:30
|
||||
#: order/templates/order/order_notes.html:32
|
||||
#: order/templates/order/sales_order_notes.html:37
|
||||
#: part/templates/part/notes.html:32 stock/templates/stock/item_notes.html:33
|
||||
#: part/templates/part/notes.html:33 stock/templates/stock/item_notes.html:33
|
||||
msgid "Edit notes"
|
||||
msgstr ""
|
||||
|
||||
@ -1044,13 +1044,13 @@ msgid "New Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:15
|
||||
#: part/templates/part/category.html:109 part/templates/part/supplier.html:15
|
||||
#: templates/stock_table.html:11
|
||||
#: part/templates/part/category.html:117 part/templates/part/supplier.html:15
|
||||
#: templates/stock_table.html:14
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:18
|
||||
#: part/templates/part/category.html:112
|
||||
#: part/templates/part/category.html:122
|
||||
msgid "Order parts"
|
||||
msgstr ""
|
||||
|
||||
@ -1063,7 +1063,7 @@ msgid "Delete Parts"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:43
|
||||
#: part/templates/part/category.html:107 templates/js/stock.html:791
|
||||
#: part/templates/part/category.html:114 templates/js/stock.html:791
|
||||
msgid "New Part"
|
||||
msgstr ""
|
||||
|
||||
@ -1095,7 +1095,7 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/detail_stock.html:35
|
||||
#: company/templates/company/supplier_part_stock.html:33
|
||||
#: part/templates/part/category.html:106 part/templates/part/category.html:113
|
||||
#: part/templates/part/category.html:112 part/templates/part/category.html:123
|
||||
#: part/templates/part/stock.html:51 templates/stock_table.html:6
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
@ -1117,8 +1117,8 @@ msgstr ""
|
||||
#: company/templates/company/tabs.html:17
|
||||
#: order/templates/order/purchase_orders.html:7
|
||||
#: order/templates/order/purchase_orders.html:12
|
||||
#: part/templates/part/orders.html:9 part/templates/part/tabs.html:45
|
||||
#: templates/navbar.html:26
|
||||
#: part/templates/part/orders.html:9 part/templates/part/tabs.html:48
|
||||
#: templates/navbar.html:33 users/models.py:31
|
||||
msgid "Purchase Orders"
|
||||
msgstr ""
|
||||
|
||||
@ -1136,8 +1136,8 @@ msgstr ""
|
||||
#: company/templates/company/tabs.html:22
|
||||
#: order/templates/order/sales_orders.html:7
|
||||
#: order/templates/order/sales_orders.html:12
|
||||
#: part/templates/part/sales_orders.html:9 part/templates/part/tabs.html:53
|
||||
#: templates/navbar.html:33
|
||||
#: part/templates/part/sales_orders.html:9 part/templates/part/tabs.html:56
|
||||
#: templates/navbar.html:42 users/models.py:32
|
||||
msgid "Sales Orders"
|
||||
msgstr ""
|
||||
|
||||
@ -1158,7 +1158,7 @@ msgid "Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:23
|
||||
#: part/templates/part/orders.html:14
|
||||
#: part/templates/part/orders.html:14 part/templates/part/part_base.html:66
|
||||
msgid "Order part"
|
||||
msgstr ""
|
||||
|
||||
@ -1205,7 +1205,7 @@ msgid "Pricing Information"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_pricing.html:15 company/views.py:399
|
||||
#: part/templates/part/sale_prices.html:13 part/views.py:2149
|
||||
#: part/templates/part/sale_prices.html:13 part/views.py:2226
|
||||
msgid "Add Price Break"
|
||||
msgstr ""
|
||||
|
||||
@ -1241,7 +1241,7 @@ msgstr ""
|
||||
#: company/templates/company/tabs.html:12 part/templates/part/tabs.html:18
|
||||
#: stock/templates/stock/location.html:17 templates/InvenTree/search.html:155
|
||||
#: templates/js/part.html:124 templates/js/part.html:372
|
||||
#: templates/js/stock.html:452 templates/navbar.html:19
|
||||
#: templates/js/stock.html:452 templates/navbar.html:22 users/models.py:29
|
||||
msgid "Stock"
|
||||
msgstr ""
|
||||
|
||||
@ -1251,22 +1251,22 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/tabs.html:9
|
||||
#: order/templates/order/receive_parts.html:14 part/models.py:294
|
||||
#: part/templates/part/cat_link.html:7 part/templates/part/category.html:88
|
||||
#: part/templates/part/category_tabs.html:6 templates/navbar.html:18
|
||||
#: templates/stats.html:8 templates/stats.html:17
|
||||
#: part/templates/part/cat_link.html:7 part/templates/part/category.html:94
|
||||
#: part/templates/part/category_tabs.html:6 templates/navbar.html:19
|
||||
#: templates/stats.html:8 templates/stats.html:17 users/models.py:28
|
||||
msgid "Parts"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:50 part/templates/part/tabs.html:39
|
||||
#: templates/navbar.html:24
|
||||
#: company/views.py:50 part/templates/part/tabs.html:42
|
||||
#: templates/navbar.html:31
|
||||
msgid "Suppliers"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:57 templates/navbar.html:25
|
||||
#: company/views.py:57 templates/navbar.html:32
|
||||
msgid "Manufacturers"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:64 templates/navbar.html:32
|
||||
#: company/views.py:64 templates/navbar.html:41
|
||||
msgid "Customers"
|
||||
msgstr ""
|
||||
|
||||
@ -1330,15 +1330,15 @@ msgstr ""
|
||||
msgid "Delete Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:404 part/views.py:2153
|
||||
#: company/views.py:404 part/views.py:2232
|
||||
msgid "Added new price break"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:441 part/views.py:2198
|
||||
#: company/views.py:441 part/views.py:2277
|
||||
msgid "Edit Price Break"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:456 part/views.py:2212
|
||||
#: company/views.py:456 part/views.py:2293
|
||||
msgid "Delete Price Break"
|
||||
msgstr ""
|
||||
|
||||
@ -1431,7 +1431,7 @@ msgstr ""
|
||||
msgid "Date order was completed"
|
||||
msgstr ""
|
||||
|
||||
#: order/models.py:185 order/models.py:259 part/views.py:1304
|
||||
#: order/models.py:185 order/models.py:259 part/views.py:1343
|
||||
#: stock/models.py:241 stock/models.py:805
|
||||
msgid "Quantity must be greater than zero"
|
||||
msgstr ""
|
||||
@ -1600,7 +1600,7 @@ msgid "Purchase Order Attachments"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/po_tabs.html:8 order/templates/order/so_tabs.html:16
|
||||
#: part/templates/part/tabs.html:64 stock/templates/stock/tabs.html:32
|
||||
#: part/templates/part/tabs.html:67 stock/templates/stock/tabs.html:32
|
||||
msgid "Attachments"
|
||||
msgstr ""
|
||||
|
||||
@ -1616,7 +1616,7 @@ msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:38
|
||||
#: order/templates/order/purchase_order_detail.html:118
|
||||
#: part/templates/part/category.html:161 part/templates/part/category.html:202
|
||||
#: part/templates/part/category.html:171 part/templates/part/category.html:213
|
||||
#: templates/js/stock.html:803
|
||||
msgid "New Location"
|
||||
msgstr ""
|
||||
@ -1658,7 +1658,7 @@ msgid "Select parts to receive against this order"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/receive_parts.html:21
|
||||
#: part/templates/part/part_base.html:135 templates/js/part.html:388
|
||||
#: part/templates/part/part_base.html:145 templates/js/part.html:388
|
||||
msgid "On Order"
|
||||
msgstr ""
|
||||
|
||||
@ -1750,7 +1750,7 @@ msgstr ""
|
||||
msgid "Add Purchase Order Attachment"
|
||||
msgstr ""
|
||||
|
||||
#: order/views.py:102 order/views.py:149 part/views.py:86 stock/views.py:167
|
||||
#: order/views.py:102 order/views.py:149 part/views.py:90 stock/views.py:167
|
||||
msgid "Added attachment"
|
||||
msgstr ""
|
||||
|
||||
@ -1890,12 +1890,12 @@ msgstr ""
|
||||
msgid "Remove allocation"
|
||||
msgstr ""
|
||||
|
||||
#: part/bom.py:138 part/templates/part/category.html:55
|
||||
#: part/bom.py:138 part/templates/part/category.html:61
|
||||
#: part/templates/part/detail.html:87
|
||||
msgid "Default Location"
|
||||
msgstr ""
|
||||
|
||||
#: part/bom.py:139 part/templates/part/part_base.html:108
|
||||
#: part/bom.py:139 part/templates/part/part_base.html:118
|
||||
msgid "Available Stock"
|
||||
msgstr ""
|
||||
|
||||
@ -2013,7 +2013,7 @@ msgid "Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:76 part/templates/part/category.html:18
|
||||
#: part/templates/part/category.html:83 templates/stats.html:12
|
||||
#: part/templates/part/category.html:89 templates/stats.html:12
|
||||
msgid "Part Categories"
|
||||
msgstr ""
|
||||
|
||||
@ -2226,7 +2226,7 @@ msgstr ""
|
||||
msgid "BOM line checksum"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1612 part/views.py:1310 part/views.py:1362
|
||||
#: part/models.py:1612 part/views.py:1349 part/views.py:1401
|
||||
#: stock/models.py:231
|
||||
msgid "Quantity must be integer value for trackable parts"
|
||||
msgstr ""
|
||||
@ -2285,23 +2285,23 @@ msgstr ""
|
||||
msgid "Finish Editing"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:42
|
||||
#: part/templates/part/bom.html:43
|
||||
msgid "Edit BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:44
|
||||
#: part/templates/part/bom.html:45
|
||||
msgid "Validate Bill of Materials"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:46 part/views.py:1597
|
||||
#: part/templates/part/bom.html:48 part/views.py:1640
|
||||
msgid "Export Bill of Materials"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:101
|
||||
#: part/templates/part/bom.html:103
|
||||
msgid "Delete selected BOM items?"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:102
|
||||
#: part/templates/part/bom.html:104
|
||||
msgid "All selected BOM items will be deleted"
|
||||
msgstr ""
|
||||
|
||||
@ -2373,83 +2373,91 @@ msgstr ""
|
||||
msgid "Each part must already exist in the database"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/build.html:8
|
||||
msgid "Part Builds"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/build.html:14
|
||||
msgid "Start New Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:19
|
||||
msgid "All parts"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:23 part/views.py:1976
|
||||
#: part/templates/part/category.html:24 part/views.py:2043
|
||||
msgid "Create new part category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:27
|
||||
#: part/templates/part/category.html:30
|
||||
msgid "Edit part category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:30
|
||||
#: part/templates/part/category.html:35
|
||||
msgid "Delete part category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:39 part/templates/part/category.html:78
|
||||
#: part/templates/part/category.html:45 part/templates/part/category.html:84
|
||||
msgid "Category Details"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:44
|
||||
#: part/templates/part/category.html:50
|
||||
msgid "Category Path"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:49
|
||||
#: part/templates/part/category.html:55
|
||||
msgid "Category Description"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:62 part/templates/part/detail.html:64
|
||||
#: part/templates/part/category.html:68 part/templates/part/detail.html:64
|
||||
msgid "Keywords"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:68
|
||||
#: part/templates/part/category.html:74
|
||||
msgid "Subcategories"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:73
|
||||
#: part/templates/part/category.html:79
|
||||
msgid "Parts (Including subcategories)"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:106
|
||||
#: part/templates/part/category.html:112
|
||||
msgid "Export Part Data"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:107 part/views.py:491
|
||||
#: part/templates/part/category.html:114 part/views.py:511
|
||||
msgid "Create new part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:111
|
||||
#: part/templates/part/category.html:120
|
||||
msgid "Set category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:111
|
||||
#: part/templates/part/category.html:120
|
||||
msgid "Set Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:113
|
||||
#: part/templates/part/category.html:123
|
||||
msgid "Export Data"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:162
|
||||
#: part/templates/part/category.html:172
|
||||
msgid "Create new location"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:167 part/templates/part/category.html:196
|
||||
#: part/templates/part/category.html:177 part/templates/part/category.html:207
|
||||
msgid "New Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:168
|
||||
#: part/templates/part/category.html:178
|
||||
msgid "Create new category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:197
|
||||
#: part/templates/part/category.html:208
|
||||
msgid "Create new Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:203 stock/views.py:1314
|
||||
#: part/templates/part/category.html:214 stock/views.py:1314
|
||||
msgid "Create new Stock Location"
|
||||
msgstr ""
|
||||
|
||||
@ -2461,7 +2469,7 @@ msgstr ""
|
||||
msgid "Part Details"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:85
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:95
|
||||
#: templates/js/part.html:112
|
||||
msgid "IPN"
|
||||
msgstr ""
|
||||
@ -2491,7 +2499,7 @@ msgstr ""
|
||||
msgid "Default Supplier"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:102 part/templates/part/params.html:22
|
||||
#: part/templates/part/detail.html:102 part/templates/part/params.html:24
|
||||
msgid "Units"
|
||||
msgstr ""
|
||||
|
||||
@ -2616,24 +2624,25 @@ msgstr ""
|
||||
msgid "Part Parameters"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:13
|
||||
#: part/templates/part/params.html:14
|
||||
msgid "Add new parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:13 templates/InvenTree/settings/part.html:12
|
||||
#: part/templates/part/params.html:14 templates/InvenTree/settings/part.html:12
|
||||
msgid "New Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:21 stock/models.py:1391
|
||||
#: part/templates/part/params.html:23 stock/models.py:1391
|
||||
#: templates/js/stock.html:112
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:33
|
||||
#: part/templates/part/params.html:36
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:34 part/templates/part/supplier.html:17
|
||||
#: part/templates/part/params.html:39 part/templates/part/supplier.html:17
|
||||
#: users/models.py:141
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
@ -2684,39 +2693,43 @@ msgstr ""
|
||||
msgid "Show pricing information"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:70
|
||||
msgid "Part actions"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:72
|
||||
msgid "Duplicate part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:73
|
||||
msgid "Edit part"
|
||||
#: part/templates/part/part_base.html:60
|
||||
msgid "Count part stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:75
|
||||
msgid "Part actions"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:78
|
||||
msgid "Duplicate part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:81
|
||||
msgid "Edit part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:84
|
||||
msgid "Delete part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:114 templates/js/table_filters.html:65
|
||||
#: part/templates/part/part_base.html:124 templates/js/table_filters.html:65
|
||||
msgid "In Stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:121
|
||||
#: part/templates/part/part_base.html:131
|
||||
msgid "Allocated to Build Orders"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:128
|
||||
#: part/templates/part/part_base.html:138
|
||||
msgid "Allocated to Sales Orders"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:150
|
||||
#: part/templates/part/part_base.html:160
|
||||
msgid "Can Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:156
|
||||
#: part/templates/part/part_base.html:166
|
||||
msgid "Underway"
|
||||
msgstr ""
|
||||
|
||||
@ -2736,7 +2749,7 @@ msgstr ""
|
||||
msgid "Upload new image"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/sale_prices.html:9 part/templates/part/tabs.html:50
|
||||
#: part/templates/part/sale_prices.html:9 part/templates/part/tabs.html:53
|
||||
msgid "Sale Price"
|
||||
msgstr ""
|
||||
|
||||
@ -2797,11 +2810,11 @@ msgstr ""
|
||||
msgid "BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:34
|
||||
#: part/templates/part/tabs.html:37
|
||||
msgid "Used In"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:58 stock/templates/stock/item_base.html:282
|
||||
#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:282
|
||||
msgid "Tests"
|
||||
msgstr ""
|
||||
|
||||
@ -2829,176 +2842,176 @@ msgstr ""
|
||||
msgid "New Variant"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:76
|
||||
#: part/views.py:78
|
||||
msgid "Add part attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:125 templates/attachment_table.html:30
|
||||
#: part/views.py:129 templates/attachment_table.html:30
|
||||
msgid "Edit attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:129
|
||||
#: part/views.py:135
|
||||
msgid "Part attachment updated"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:144
|
||||
#: part/views.py:150
|
||||
msgid "Delete Part Attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:150
|
||||
#: part/views.py:158
|
||||
msgid "Deleted part attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:159
|
||||
#: part/views.py:167
|
||||
msgid "Create Test Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:186
|
||||
#: part/views.py:196
|
||||
msgid "Edit Test Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:200
|
||||
#: part/views.py:212
|
||||
msgid "Delete Test Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:207
|
||||
#: part/views.py:221
|
||||
msgid "Set Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:255
|
||||
#: part/views.py:271
|
||||
#, python-brace-format
|
||||
msgid "Set category for {n} parts"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:290
|
||||
#: part/views.py:306
|
||||
msgid "Create Variant"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:368
|
||||
#: part/views.py:386
|
||||
msgid "Duplicate Part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:373
|
||||
#: part/views.py:393
|
||||
msgid "Copied part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:496
|
||||
#: part/views.py:518
|
||||
msgid "Created new part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:707
|
||||
#: part/views.py:733
|
||||
msgid "Part QR Code"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:724
|
||||
#: part/views.py:752
|
||||
msgid "Upload Part Image"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:729 part/views.py:764
|
||||
#: part/views.py:760 part/views.py:797
|
||||
msgid "Updated part image"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:738
|
||||
#: part/views.py:769
|
||||
msgid "Select Part Image"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:767
|
||||
#: part/views.py:800
|
||||
msgid "Part image not found"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:778
|
||||
#: part/views.py:811
|
||||
msgid "Edit Part Properties"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:800
|
||||
#: part/views.py:835
|
||||
msgid "Validate BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:963
|
||||
#: part/views.py:1002
|
||||
msgid "No BOM file provided"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1313
|
||||
#: part/views.py:1352
|
||||
msgid "Enter a valid quantity"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1338 part/views.py:1341
|
||||
#: part/views.py:1377 part/views.py:1380
|
||||
msgid "Select valid part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1347
|
||||
#: part/views.py:1386
|
||||
msgid "Duplicate part selected"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1385
|
||||
#: part/views.py:1424
|
||||
msgid "Select a part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1391
|
||||
#: part/views.py:1430
|
||||
msgid "Selected part creates a circular BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1395
|
||||
#: part/views.py:1434
|
||||
msgid "Specify quantity"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1645
|
||||
#: part/views.py:1690
|
||||
msgid "Confirm Part Deletion"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1652
|
||||
#: part/views.py:1699
|
||||
msgid "Part was deleted"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1661
|
||||
#: part/views.py:1708
|
||||
msgid "Part Pricing"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1783
|
||||
#: part/views.py:1834
|
||||
msgid "Create Part Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1791
|
||||
#: part/views.py:1844
|
||||
msgid "Edit Part Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1798
|
||||
#: part/views.py:1853
|
||||
msgid "Delete Part Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1806
|
||||
#: part/views.py:1863
|
||||
msgid "Create Part Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1856
|
||||
#: part/views.py:1915
|
||||
msgid "Edit Part Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1870
|
||||
#: part/views.py:1931
|
||||
msgid "Delete Part Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1927
|
||||
#: part/views.py:1990
|
||||
msgid "Edit Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1962
|
||||
#: part/views.py:2027
|
||||
msgid "Delete Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1968
|
||||
#: part/views.py:2035
|
||||
msgid "Part category was deleted"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:2027
|
||||
#: part/views.py:2098
|
||||
msgid "Create BOM item"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:2093
|
||||
#: part/views.py:2166
|
||||
msgid "Edit BOM item"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:2141
|
||||
#: part/views.py:2216
|
||||
msgid "Confim BOM item deletion"
|
||||
msgstr ""
|
||||
|
||||
@ -3371,15 +3384,15 @@ msgid "Stock adjustment actions"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:98
|
||||
#: stock/templates/stock/location.html:38 templates/stock_table.html:15
|
||||
#: stock/templates/stock/location.html:38 templates/stock_table.html:19
|
||||
msgid "Count stock"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:99 templates/stock_table.html:13
|
||||
#: stock/templates/stock/item_base.html:99 templates/stock_table.html:17
|
||||
msgid "Add stock"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:100 templates/stock_table.html:14
|
||||
#: stock/templates/stock/item_base.html:100 templates/stock_table.html:18
|
||||
msgid "Remove stock"
|
||||
msgstr ""
|
||||
|
||||
@ -3819,6 +3832,14 @@ msgstr ""
|
||||
msgid "Add Stock Tracking Entry"
|
||||
msgstr ""
|
||||
|
||||
#: templates/403.html:5 templates/403.html:11
|
||||
msgid "Permission Denied"
|
||||
msgstr ""
|
||||
|
||||
#: templates/403.html:14
|
||||
msgid "You do not have permission to view this page."
|
||||
msgstr ""
|
||||
|
||||
#: templates/InvenTree/bom_invalid.html:7
|
||||
msgid "BOM Waiting Validation"
|
||||
msgstr ""
|
||||
@ -3827,6 +3848,10 @@ msgstr ""
|
||||
msgid "Pending Builds"
|
||||
msgstr ""
|
||||
|
||||
#: templates/InvenTree/index.html:4
|
||||
msgid "Index"
|
||||
msgstr ""
|
||||
|
||||
#: templates/InvenTree/latest_parts.html:7
|
||||
msgid "Latest Parts"
|
||||
msgstr ""
|
||||
@ -4392,39 +4417,39 @@ msgstr ""
|
||||
msgid "Purchasable"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:22
|
||||
#: templates/navbar.html:29
|
||||
msgid "Buy"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:30
|
||||
#: templates/navbar.html:39
|
||||
msgid "Sell"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:40
|
||||
#: templates/navbar.html:50
|
||||
msgid "Scan Barcode"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:49
|
||||
#: templates/navbar.html:59 users/models.py:27
|
||||
msgid "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:52
|
||||
#: templates/navbar.html:62
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:53
|
||||
#: templates/navbar.html:63
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:55
|
||||
#: templates/navbar.html:65
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:58
|
||||
#: templates/navbar.html:68
|
||||
msgid "About InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:59
|
||||
#: templates/navbar.html:69
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
@ -4436,38 +4461,94 @@ msgstr ""
|
||||
msgid "Export Stock Information"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:13
|
||||
#: templates/stock_table.html:17
|
||||
msgid "Add to selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:14
|
||||
#: templates/stock_table.html:18
|
||||
msgid "Remove from selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:15
|
||||
#: templates/stock_table.html:19
|
||||
msgid "Stocktake selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:16
|
||||
#: templates/stock_table.html:20
|
||||
msgid "Move selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:16
|
||||
#: templates/stock_table.html:20
|
||||
msgid "Move stock"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:17
|
||||
#: templates/stock_table.html:21
|
||||
msgid "Order selected items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:17
|
||||
#: templates/stock_table.html:21
|
||||
msgid "Order stock"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:18
|
||||
#: templates/stock_table.html:24
|
||||
msgid "Delete selected items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:18
|
||||
#: templates/stock_table.html:24
|
||||
msgid "Delete Stock"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:62
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:63
|
||||
msgid "Select which users are assigned to this group"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:124
|
||||
msgid "Personal info"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:125
|
||||
msgid "Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:128
|
||||
msgid "Important dates"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:124
|
||||
msgid "Permission set"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:132
|
||||
msgid "Group"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:135
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:135
|
||||
msgid "Permission to view items"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:137
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:137
|
||||
msgid "Permission to add items"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:139
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:139
|
||||
msgid "Permissions to edit items"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:141
|
||||
msgid "Permission to delete items"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-10-04 14:02+0000\n"
|
||||
"POT-Creation-Date: 2020-10-05 13:20+0000\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@ -90,7 +90,7 @@ msgstr ""
|
||||
msgid "User"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/models.py:106 part/templates/part/params.html:20
|
||||
#: InvenTree/models.py:106 part/templates/part/params.html:22
|
||||
#: templates/js/part.html:81
|
||||
msgid "Name"
|
||||
msgstr ""
|
||||
@ -99,19 +99,19 @@ msgstr ""
|
||||
msgid "Description (optional)"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:342
|
||||
#: InvenTree/settings.py:343
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:343
|
||||
#: InvenTree/settings.py:344
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:344
|
||||
#: InvenTree/settings.py:345
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: InvenTree/settings.py:345
|
||||
#: InvenTree/settings.py:346
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
@ -325,7 +325,7 @@ msgstr ""
|
||||
msgid "Number of parts to build"
|
||||
msgstr ""
|
||||
|
||||
#: build/models.py:128 part/templates/part/part_base.html:145
|
||||
#: build/models.py:128 part/templates/part/part_base.html:155
|
||||
msgid "Build Status"
|
||||
msgstr ""
|
||||
|
||||
@ -344,7 +344,7 @@ msgstr ""
|
||||
#: build/models.py:155 build/templates/build/detail.html:55
|
||||
#: company/templates/company/supplier_part_base.html:60
|
||||
#: company/templates/company/supplier_part_detail.html:24
|
||||
#: part/templates/part/detail.html:80 part/templates/part/part_base.html:92
|
||||
#: part/templates/part/detail.html:80 part/templates/part/part_base.html:102
|
||||
#: stock/models.py:381 stock/templates/stock/item_base.html:244
|
||||
msgid "External Link"
|
||||
msgstr ""
|
||||
@ -356,7 +356,7 @@ msgstr ""
|
||||
#: build/models.py:160 build/templates/build/tabs.html:14 company/models.py:310
|
||||
#: company/templates/company/tabs.html:33 order/templates/order/po_tabs.html:15
|
||||
#: order/templates/order/purchase_order_detail.html:202
|
||||
#: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:67
|
||||
#: order/templates/order/so_tabs.html:23 part/templates/part/tabs.html:70
|
||||
#: stock/forms.py:306 stock/forms.py:338 stock/forms.py:366 stock/models.py:453
|
||||
#: stock/models.py:1404 stock/templates/stock/tabs.html:26
|
||||
#: templates/js/barcode.html:391 templates/js/bom.html:223
|
||||
@ -404,7 +404,7 @@ msgstr ""
|
||||
|
||||
#: build/templates/build/allocate.html:17
|
||||
#: company/templates/company/detail_part.html:18 order/views.py:779
|
||||
#: part/templates/part/category.html:112
|
||||
#: part/templates/part/category.html:122
|
||||
msgid "Order Parts"
|
||||
msgstr ""
|
||||
|
||||
@ -420,7 +420,7 @@ msgstr ""
|
||||
msgid "Unallocate"
|
||||
msgstr ""
|
||||
|
||||
#: build/templates/build/allocate.html:87 templates/stock_table.html:9
|
||||
#: build/templates/build/allocate.html:87 templates/stock_table.html:10
|
||||
msgid "New Stock Item"
|
||||
msgstr ""
|
||||
|
||||
@ -548,7 +548,7 @@ msgstr ""
|
||||
#: build/templates/build/build_base.html:34
|
||||
#: build/templates/build/complete.html:6
|
||||
#: stock/templates/stock/item_base.html:223 templates/js/build.html:39
|
||||
#: templates/navbar.html:20
|
||||
#: templates/navbar.html:25
|
||||
msgid "Build"
|
||||
msgstr ""
|
||||
|
||||
@ -687,7 +687,7 @@ msgstr ""
|
||||
|
||||
#: build/templates/build/index.html:6 build/templates/build/index.html:14
|
||||
#: order/templates/order/so_builds.html:11 order/templates/order/so_tabs.html:9
|
||||
#: part/templates/part/tabs.html:30
|
||||
#: part/templates/part/tabs.html:31 users/models.py:30
|
||||
msgid "Build Orders"
|
||||
msgstr ""
|
||||
|
||||
@ -709,7 +709,7 @@ msgstr ""
|
||||
#: build/templates/build/notes.html:33 company/templates/company/notes.html:30
|
||||
#: order/templates/order/order_notes.html:32
|
||||
#: order/templates/order/sales_order_notes.html:37
|
||||
#: part/templates/part/notes.html:32 stock/templates/stock/item_notes.html:33
|
||||
#: part/templates/part/notes.html:33 stock/templates/stock/item_notes.html:33
|
||||
msgid "Edit notes"
|
||||
msgstr ""
|
||||
|
||||
@ -1044,13 +1044,13 @@ msgid "New Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:15
|
||||
#: part/templates/part/category.html:109 part/templates/part/supplier.html:15
|
||||
#: templates/stock_table.html:11
|
||||
#: part/templates/part/category.html:117 part/templates/part/supplier.html:15
|
||||
#: templates/stock_table.html:14
|
||||
msgid "Options"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:18
|
||||
#: part/templates/part/category.html:112
|
||||
#: part/templates/part/category.html:122
|
||||
msgid "Order parts"
|
||||
msgstr ""
|
||||
|
||||
@ -1063,7 +1063,7 @@ msgid "Delete Parts"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/detail_part.html:43
|
||||
#: part/templates/part/category.html:107 templates/js/stock.html:791
|
||||
#: part/templates/part/category.html:114 templates/js/stock.html:791
|
||||
msgid "New Part"
|
||||
msgstr ""
|
||||
|
||||
@ -1095,7 +1095,7 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/detail_stock.html:35
|
||||
#: company/templates/company/supplier_part_stock.html:33
|
||||
#: part/templates/part/category.html:106 part/templates/part/category.html:113
|
||||
#: part/templates/part/category.html:112 part/templates/part/category.html:123
|
||||
#: part/templates/part/stock.html:51 templates/stock_table.html:6
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
@ -1117,8 +1117,8 @@ msgstr ""
|
||||
#: company/templates/company/tabs.html:17
|
||||
#: order/templates/order/purchase_orders.html:7
|
||||
#: order/templates/order/purchase_orders.html:12
|
||||
#: part/templates/part/orders.html:9 part/templates/part/tabs.html:45
|
||||
#: templates/navbar.html:26
|
||||
#: part/templates/part/orders.html:9 part/templates/part/tabs.html:48
|
||||
#: templates/navbar.html:33 users/models.py:31
|
||||
msgid "Purchase Orders"
|
||||
msgstr ""
|
||||
|
||||
@ -1136,8 +1136,8 @@ msgstr ""
|
||||
#: company/templates/company/tabs.html:22
|
||||
#: order/templates/order/sales_orders.html:7
|
||||
#: order/templates/order/sales_orders.html:12
|
||||
#: part/templates/part/sales_orders.html:9 part/templates/part/tabs.html:53
|
||||
#: templates/navbar.html:33
|
||||
#: part/templates/part/sales_orders.html:9 part/templates/part/tabs.html:56
|
||||
#: templates/navbar.html:42 users/models.py:32
|
||||
msgid "Sales Orders"
|
||||
msgstr ""
|
||||
|
||||
@ -1158,7 +1158,7 @@ msgid "Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_base.html:23
|
||||
#: part/templates/part/orders.html:14
|
||||
#: part/templates/part/orders.html:14 part/templates/part/part_base.html:66
|
||||
msgid "Order part"
|
||||
msgstr ""
|
||||
|
||||
@ -1205,7 +1205,7 @@ msgid "Pricing Information"
|
||||
msgstr ""
|
||||
|
||||
#: company/templates/company/supplier_part_pricing.html:15 company/views.py:399
|
||||
#: part/templates/part/sale_prices.html:13 part/views.py:2149
|
||||
#: part/templates/part/sale_prices.html:13 part/views.py:2226
|
||||
msgid "Add Price Break"
|
||||
msgstr ""
|
||||
|
||||
@ -1241,7 +1241,7 @@ msgstr ""
|
||||
#: company/templates/company/tabs.html:12 part/templates/part/tabs.html:18
|
||||
#: stock/templates/stock/location.html:17 templates/InvenTree/search.html:155
|
||||
#: templates/js/part.html:124 templates/js/part.html:372
|
||||
#: templates/js/stock.html:452 templates/navbar.html:19
|
||||
#: templates/js/stock.html:452 templates/navbar.html:22 users/models.py:29
|
||||
msgid "Stock"
|
||||
msgstr ""
|
||||
|
||||
@ -1251,22 +1251,22 @@ msgstr ""
|
||||
|
||||
#: company/templates/company/tabs.html:9
|
||||
#: order/templates/order/receive_parts.html:14 part/models.py:294
|
||||
#: part/templates/part/cat_link.html:7 part/templates/part/category.html:88
|
||||
#: part/templates/part/category_tabs.html:6 templates/navbar.html:18
|
||||
#: templates/stats.html:8 templates/stats.html:17
|
||||
#: part/templates/part/cat_link.html:7 part/templates/part/category.html:94
|
||||
#: part/templates/part/category_tabs.html:6 templates/navbar.html:19
|
||||
#: templates/stats.html:8 templates/stats.html:17 users/models.py:28
|
||||
msgid "Parts"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:50 part/templates/part/tabs.html:39
|
||||
#: templates/navbar.html:24
|
||||
#: company/views.py:50 part/templates/part/tabs.html:42
|
||||
#: templates/navbar.html:31
|
||||
msgid "Suppliers"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:57 templates/navbar.html:25
|
||||
#: company/views.py:57 templates/navbar.html:32
|
||||
msgid "Manufacturers"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:64 templates/navbar.html:32
|
||||
#: company/views.py:64 templates/navbar.html:41
|
||||
msgid "Customers"
|
||||
msgstr ""
|
||||
|
||||
@ -1330,15 +1330,15 @@ msgstr ""
|
||||
msgid "Delete Supplier Part"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:404 part/views.py:2153
|
||||
#: company/views.py:404 part/views.py:2232
|
||||
msgid "Added new price break"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:441 part/views.py:2198
|
||||
#: company/views.py:441 part/views.py:2277
|
||||
msgid "Edit Price Break"
|
||||
msgstr ""
|
||||
|
||||
#: company/views.py:456 part/views.py:2212
|
||||
#: company/views.py:456 part/views.py:2293
|
||||
msgid "Delete Price Break"
|
||||
msgstr ""
|
||||
|
||||
@ -1431,7 +1431,7 @@ msgstr ""
|
||||
msgid "Date order was completed"
|
||||
msgstr ""
|
||||
|
||||
#: order/models.py:185 order/models.py:259 part/views.py:1304
|
||||
#: order/models.py:185 order/models.py:259 part/views.py:1343
|
||||
#: stock/models.py:241 stock/models.py:805
|
||||
msgid "Quantity must be greater than zero"
|
||||
msgstr ""
|
||||
@ -1600,7 +1600,7 @@ msgid "Purchase Order Attachments"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/po_tabs.html:8 order/templates/order/so_tabs.html:16
|
||||
#: part/templates/part/tabs.html:64 stock/templates/stock/tabs.html:32
|
||||
#: part/templates/part/tabs.html:67 stock/templates/stock/tabs.html:32
|
||||
msgid "Attachments"
|
||||
msgstr ""
|
||||
|
||||
@ -1616,7 +1616,7 @@ msgstr ""
|
||||
|
||||
#: order/templates/order/purchase_order_detail.html:38
|
||||
#: order/templates/order/purchase_order_detail.html:118
|
||||
#: part/templates/part/category.html:161 part/templates/part/category.html:202
|
||||
#: part/templates/part/category.html:171 part/templates/part/category.html:213
|
||||
#: templates/js/stock.html:803
|
||||
msgid "New Location"
|
||||
msgstr ""
|
||||
@ -1658,7 +1658,7 @@ msgid "Select parts to receive against this order"
|
||||
msgstr ""
|
||||
|
||||
#: order/templates/order/receive_parts.html:21
|
||||
#: part/templates/part/part_base.html:135 templates/js/part.html:388
|
||||
#: part/templates/part/part_base.html:145 templates/js/part.html:388
|
||||
msgid "On Order"
|
||||
msgstr ""
|
||||
|
||||
@ -1750,7 +1750,7 @@ msgstr ""
|
||||
msgid "Add Purchase Order Attachment"
|
||||
msgstr ""
|
||||
|
||||
#: order/views.py:102 order/views.py:149 part/views.py:86 stock/views.py:167
|
||||
#: order/views.py:102 order/views.py:149 part/views.py:90 stock/views.py:167
|
||||
msgid "Added attachment"
|
||||
msgstr ""
|
||||
|
||||
@ -1890,12 +1890,12 @@ msgstr ""
|
||||
msgid "Remove allocation"
|
||||
msgstr ""
|
||||
|
||||
#: part/bom.py:138 part/templates/part/category.html:55
|
||||
#: part/bom.py:138 part/templates/part/category.html:61
|
||||
#: part/templates/part/detail.html:87
|
||||
msgid "Default Location"
|
||||
msgstr ""
|
||||
|
||||
#: part/bom.py:139 part/templates/part/part_base.html:108
|
||||
#: part/bom.py:139 part/templates/part/part_base.html:118
|
||||
msgid "Available Stock"
|
||||
msgstr ""
|
||||
|
||||
@ -2013,7 +2013,7 @@ msgid "Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:76 part/templates/part/category.html:18
|
||||
#: part/templates/part/category.html:83 templates/stats.html:12
|
||||
#: part/templates/part/category.html:89 templates/stats.html:12
|
||||
msgid "Part Categories"
|
||||
msgstr ""
|
||||
|
||||
@ -2226,7 +2226,7 @@ msgstr ""
|
||||
msgid "BOM line checksum"
|
||||
msgstr ""
|
||||
|
||||
#: part/models.py:1612 part/views.py:1310 part/views.py:1362
|
||||
#: part/models.py:1612 part/views.py:1349 part/views.py:1401
|
||||
#: stock/models.py:231
|
||||
msgid "Quantity must be integer value for trackable parts"
|
||||
msgstr ""
|
||||
@ -2285,23 +2285,23 @@ msgstr ""
|
||||
msgid "Finish Editing"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:42
|
||||
#: part/templates/part/bom.html:43
|
||||
msgid "Edit BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:44
|
||||
#: part/templates/part/bom.html:45
|
||||
msgid "Validate Bill of Materials"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:46 part/views.py:1597
|
||||
#: part/templates/part/bom.html:48 part/views.py:1640
|
||||
msgid "Export Bill of Materials"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:101
|
||||
#: part/templates/part/bom.html:103
|
||||
msgid "Delete selected BOM items?"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/bom.html:102
|
||||
#: part/templates/part/bom.html:104
|
||||
msgid "All selected BOM items will be deleted"
|
||||
msgstr ""
|
||||
|
||||
@ -2373,83 +2373,91 @@ msgstr ""
|
||||
msgid "Each part must already exist in the database"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/build.html:8
|
||||
msgid "Part Builds"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/build.html:14
|
||||
msgid "Start New Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:19
|
||||
msgid "All parts"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:23 part/views.py:1976
|
||||
#: part/templates/part/category.html:24 part/views.py:2043
|
||||
msgid "Create new part category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:27
|
||||
#: part/templates/part/category.html:30
|
||||
msgid "Edit part category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:30
|
||||
#: part/templates/part/category.html:35
|
||||
msgid "Delete part category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:39 part/templates/part/category.html:78
|
||||
#: part/templates/part/category.html:45 part/templates/part/category.html:84
|
||||
msgid "Category Details"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:44
|
||||
#: part/templates/part/category.html:50
|
||||
msgid "Category Path"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:49
|
||||
#: part/templates/part/category.html:55
|
||||
msgid "Category Description"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:62 part/templates/part/detail.html:64
|
||||
#: part/templates/part/category.html:68 part/templates/part/detail.html:64
|
||||
msgid "Keywords"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:68
|
||||
#: part/templates/part/category.html:74
|
||||
msgid "Subcategories"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:73
|
||||
#: part/templates/part/category.html:79
|
||||
msgid "Parts (Including subcategories)"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:106
|
||||
#: part/templates/part/category.html:112
|
||||
msgid "Export Part Data"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:107 part/views.py:491
|
||||
#: part/templates/part/category.html:114 part/views.py:511
|
||||
msgid "Create new part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:111
|
||||
#: part/templates/part/category.html:120
|
||||
msgid "Set category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:111
|
||||
#: part/templates/part/category.html:120
|
||||
msgid "Set Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:113
|
||||
#: part/templates/part/category.html:123
|
||||
msgid "Export Data"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:162
|
||||
#: part/templates/part/category.html:172
|
||||
msgid "Create new location"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:167 part/templates/part/category.html:196
|
||||
#: part/templates/part/category.html:177 part/templates/part/category.html:207
|
||||
msgid "New Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:168
|
||||
#: part/templates/part/category.html:178
|
||||
msgid "Create new category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:197
|
||||
#: part/templates/part/category.html:208
|
||||
msgid "Create new Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/category.html:203 stock/views.py:1314
|
||||
#: part/templates/part/category.html:214 stock/views.py:1314
|
||||
msgid "Create new Stock Location"
|
||||
msgstr ""
|
||||
|
||||
@ -2461,7 +2469,7 @@ msgstr ""
|
||||
msgid "Part Details"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:85
|
||||
#: part/templates/part/detail.html:25 part/templates/part/part_base.html:95
|
||||
#: templates/js/part.html:112
|
||||
msgid "IPN"
|
||||
msgstr ""
|
||||
@ -2491,7 +2499,7 @@ msgstr ""
|
||||
msgid "Default Supplier"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/detail.html:102 part/templates/part/params.html:22
|
||||
#: part/templates/part/detail.html:102 part/templates/part/params.html:24
|
||||
msgid "Units"
|
||||
msgstr ""
|
||||
|
||||
@ -2616,24 +2624,25 @@ msgstr ""
|
||||
msgid "Part Parameters"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:13
|
||||
#: part/templates/part/params.html:14
|
||||
msgid "Add new parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:13 templates/InvenTree/settings/part.html:12
|
||||
#: part/templates/part/params.html:14 templates/InvenTree/settings/part.html:12
|
||||
msgid "New Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:21 stock/models.py:1391
|
||||
#: part/templates/part/params.html:23 stock/models.py:1391
|
||||
#: templates/js/stock.html:112
|
||||
msgid "Value"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:33
|
||||
#: part/templates/part/params.html:36
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/params.html:34 part/templates/part/supplier.html:17
|
||||
#: part/templates/part/params.html:39 part/templates/part/supplier.html:17
|
||||
#: users/models.py:141
|
||||
msgid "Delete"
|
||||
msgstr ""
|
||||
|
||||
@ -2684,39 +2693,43 @@ msgstr ""
|
||||
msgid "Show pricing information"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:70
|
||||
msgid "Part actions"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:72
|
||||
msgid "Duplicate part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:73
|
||||
msgid "Edit part"
|
||||
#: part/templates/part/part_base.html:60
|
||||
msgid "Count part stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:75
|
||||
msgid "Part actions"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:78
|
||||
msgid "Duplicate part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:81
|
||||
msgid "Edit part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:84
|
||||
msgid "Delete part"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:114 templates/js/table_filters.html:65
|
||||
#: part/templates/part/part_base.html:124 templates/js/table_filters.html:65
|
||||
msgid "In Stock"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:121
|
||||
#: part/templates/part/part_base.html:131
|
||||
msgid "Allocated to Build Orders"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:128
|
||||
#: part/templates/part/part_base.html:138
|
||||
msgid "Allocated to Sales Orders"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:150
|
||||
#: part/templates/part/part_base.html:160
|
||||
msgid "Can Build"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/part_base.html:156
|
||||
#: part/templates/part/part_base.html:166
|
||||
msgid "Underway"
|
||||
msgstr ""
|
||||
|
||||
@ -2736,7 +2749,7 @@ msgstr ""
|
||||
msgid "Upload new image"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/sale_prices.html:9 part/templates/part/tabs.html:50
|
||||
#: part/templates/part/sale_prices.html:9 part/templates/part/tabs.html:53
|
||||
msgid "Sale Price"
|
||||
msgstr ""
|
||||
|
||||
@ -2797,11 +2810,11 @@ msgstr ""
|
||||
msgid "BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:34
|
||||
#: part/templates/part/tabs.html:37
|
||||
msgid "Used In"
|
||||
msgstr ""
|
||||
|
||||
#: part/templates/part/tabs.html:58 stock/templates/stock/item_base.html:282
|
||||
#: part/templates/part/tabs.html:61 stock/templates/stock/item_base.html:282
|
||||
msgid "Tests"
|
||||
msgstr ""
|
||||
|
||||
@ -2829,176 +2842,176 @@ msgstr ""
|
||||
msgid "New Variant"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:76
|
||||
#: part/views.py:78
|
||||
msgid "Add part attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:125 templates/attachment_table.html:30
|
||||
#: part/views.py:129 templates/attachment_table.html:30
|
||||
msgid "Edit attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:129
|
||||
#: part/views.py:135
|
||||
msgid "Part attachment updated"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:144
|
||||
#: part/views.py:150
|
||||
msgid "Delete Part Attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:150
|
||||
#: part/views.py:158
|
||||
msgid "Deleted part attachment"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:159
|
||||
#: part/views.py:167
|
||||
msgid "Create Test Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:186
|
||||
#: part/views.py:196
|
||||
msgid "Edit Test Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:200
|
||||
#: part/views.py:212
|
||||
msgid "Delete Test Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:207
|
||||
#: part/views.py:221
|
||||
msgid "Set Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:255
|
||||
#: part/views.py:271
|
||||
#, python-brace-format
|
||||
msgid "Set category for {n} parts"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:290
|
||||
#: part/views.py:306
|
||||
msgid "Create Variant"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:368
|
||||
#: part/views.py:386
|
||||
msgid "Duplicate Part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:373
|
||||
#: part/views.py:393
|
||||
msgid "Copied part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:496
|
||||
#: part/views.py:518
|
||||
msgid "Created new part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:707
|
||||
#: part/views.py:733
|
||||
msgid "Part QR Code"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:724
|
||||
#: part/views.py:752
|
||||
msgid "Upload Part Image"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:729 part/views.py:764
|
||||
#: part/views.py:760 part/views.py:797
|
||||
msgid "Updated part image"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:738
|
||||
#: part/views.py:769
|
||||
msgid "Select Part Image"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:767
|
||||
#: part/views.py:800
|
||||
msgid "Part image not found"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:778
|
||||
#: part/views.py:811
|
||||
msgid "Edit Part Properties"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:800
|
||||
#: part/views.py:835
|
||||
msgid "Validate BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:963
|
||||
#: part/views.py:1002
|
||||
msgid "No BOM file provided"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1313
|
||||
#: part/views.py:1352
|
||||
msgid "Enter a valid quantity"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1338 part/views.py:1341
|
||||
#: part/views.py:1377 part/views.py:1380
|
||||
msgid "Select valid part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1347
|
||||
#: part/views.py:1386
|
||||
msgid "Duplicate part selected"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1385
|
||||
#: part/views.py:1424
|
||||
msgid "Select a part"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1391
|
||||
#: part/views.py:1430
|
||||
msgid "Selected part creates a circular BOM"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1395
|
||||
#: part/views.py:1434
|
||||
msgid "Specify quantity"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1645
|
||||
#: part/views.py:1690
|
||||
msgid "Confirm Part Deletion"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1652
|
||||
#: part/views.py:1699
|
||||
msgid "Part was deleted"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1661
|
||||
#: part/views.py:1708
|
||||
msgid "Part Pricing"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1783
|
||||
#: part/views.py:1834
|
||||
msgid "Create Part Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1791
|
||||
#: part/views.py:1844
|
||||
msgid "Edit Part Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1798
|
||||
#: part/views.py:1853
|
||||
msgid "Delete Part Parameter Template"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1806
|
||||
#: part/views.py:1863
|
||||
msgid "Create Part Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1856
|
||||
#: part/views.py:1915
|
||||
msgid "Edit Part Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1870
|
||||
#: part/views.py:1931
|
||||
msgid "Delete Part Parameter"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1927
|
||||
#: part/views.py:1990
|
||||
msgid "Edit Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1962
|
||||
#: part/views.py:2027
|
||||
msgid "Delete Part Category"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:1968
|
||||
#: part/views.py:2035
|
||||
msgid "Part category was deleted"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:2027
|
||||
#: part/views.py:2098
|
||||
msgid "Create BOM item"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:2093
|
||||
#: part/views.py:2166
|
||||
msgid "Edit BOM item"
|
||||
msgstr ""
|
||||
|
||||
#: part/views.py:2141
|
||||
#: part/views.py:2216
|
||||
msgid "Confim BOM item deletion"
|
||||
msgstr ""
|
||||
|
||||
@ -3371,15 +3384,15 @@ msgid "Stock adjustment actions"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:98
|
||||
#: stock/templates/stock/location.html:38 templates/stock_table.html:15
|
||||
#: stock/templates/stock/location.html:38 templates/stock_table.html:19
|
||||
msgid "Count stock"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:99 templates/stock_table.html:13
|
||||
#: stock/templates/stock/item_base.html:99 templates/stock_table.html:17
|
||||
msgid "Add stock"
|
||||
msgstr ""
|
||||
|
||||
#: stock/templates/stock/item_base.html:100 templates/stock_table.html:14
|
||||
#: stock/templates/stock/item_base.html:100 templates/stock_table.html:18
|
||||
msgid "Remove stock"
|
||||
msgstr ""
|
||||
|
||||
@ -3819,6 +3832,14 @@ msgstr ""
|
||||
msgid "Add Stock Tracking Entry"
|
||||
msgstr ""
|
||||
|
||||
#: templates/403.html:5 templates/403.html:11
|
||||
msgid "Permission Denied"
|
||||
msgstr ""
|
||||
|
||||
#: templates/403.html:14
|
||||
msgid "You do not have permission to view this page."
|
||||
msgstr ""
|
||||
|
||||
#: templates/InvenTree/bom_invalid.html:7
|
||||
msgid "BOM Waiting Validation"
|
||||
msgstr ""
|
||||
@ -3827,6 +3848,10 @@ msgstr ""
|
||||
msgid "Pending Builds"
|
||||
msgstr ""
|
||||
|
||||
#: templates/InvenTree/index.html:4
|
||||
msgid "Index"
|
||||
msgstr ""
|
||||
|
||||
#: templates/InvenTree/latest_parts.html:7
|
||||
msgid "Latest Parts"
|
||||
msgstr ""
|
||||
@ -4392,39 +4417,39 @@ msgstr ""
|
||||
msgid "Purchasable"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:22
|
||||
#: templates/navbar.html:29
|
||||
msgid "Buy"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:30
|
||||
#: templates/navbar.html:39
|
||||
msgid "Sell"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:40
|
||||
#: templates/navbar.html:50
|
||||
msgid "Scan Barcode"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:49
|
||||
#: templates/navbar.html:59 users/models.py:27
|
||||
msgid "Admin"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:52
|
||||
#: templates/navbar.html:62
|
||||
msgid "Settings"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:53
|
||||
#: templates/navbar.html:63
|
||||
msgid "Logout"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:55
|
||||
#: templates/navbar.html:65
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:58
|
||||
#: templates/navbar.html:68
|
||||
msgid "About InvenTree"
|
||||
msgstr ""
|
||||
|
||||
#: templates/navbar.html:59
|
||||
#: templates/navbar.html:69
|
||||
msgid "Statistics"
|
||||
msgstr ""
|
||||
|
||||
@ -4436,38 +4461,94 @@ msgstr ""
|
||||
msgid "Export Stock Information"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:13
|
||||
#: templates/stock_table.html:17
|
||||
msgid "Add to selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:14
|
||||
#: templates/stock_table.html:18
|
||||
msgid "Remove from selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:15
|
||||
#: templates/stock_table.html:19
|
||||
msgid "Stocktake selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:16
|
||||
#: templates/stock_table.html:20
|
||||
msgid "Move selected stock items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:16
|
||||
#: templates/stock_table.html:20
|
||||
msgid "Move stock"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:17
|
||||
#: templates/stock_table.html:21
|
||||
msgid "Order selected items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:17
|
||||
#: templates/stock_table.html:21
|
||||
msgid "Order stock"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:18
|
||||
#: templates/stock_table.html:24
|
||||
msgid "Delete selected items"
|
||||
msgstr ""
|
||||
|
||||
#: templates/stock_table.html:18
|
||||
#: templates/stock_table.html:24
|
||||
msgid "Delete Stock"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:62
|
||||
msgid "Users"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:63
|
||||
msgid "Select which users are assigned to this group"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:124
|
||||
msgid "Personal info"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:125
|
||||
msgid "Permissions"
|
||||
msgstr ""
|
||||
|
||||
#: users/admin.py:128
|
||||
msgid "Important dates"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:124
|
||||
msgid "Permission set"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:132
|
||||
msgid "Group"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:135
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:135
|
||||
msgid "Permission to view items"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:137
|
||||
msgid "Create"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:137
|
||||
msgid "Permission to add items"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:139
|
||||
msgid "Update"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:139
|
||||
msgid "Permissions to edit items"
|
||||
msgstr ""
|
||||
|
||||
#: users/models.py:141
|
||||
msgid "Permission to delete items"
|
||||
msgstr ""
|
||||
|
@ -6,7 +6,7 @@ JSON API for the Order app
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework import generics, permissions
|
||||
from rest_framework import generics
|
||||
from rest_framework import filters
|
||||
|
||||
from django.conf.urls import url, include
|
||||
@ -109,10 +109,6 @@ class POList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -162,10 +158,6 @@ class PODetail(generics.RetrieveUpdateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated
|
||||
]
|
||||
|
||||
|
||||
class POLineItemList(generics.ListCreateAPIView):
|
||||
""" API endpoint for accessing a list of POLineItem objects
|
||||
@ -188,10 +180,6 @@ class POLineItemList(generics.ListCreateAPIView):
|
||||
|
||||
return self.serializer_class(*args, **kwargs)
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
]
|
||||
@ -208,10 +196,6 @@ class POLineItemDetail(generics.RetrieveUpdateAPIView):
|
||||
queryset = PurchaseOrderLineItem
|
||||
serializer_class = POLineItemSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
|
||||
class SOAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
|
||||
"""
|
||||
@ -300,10 +284,6 @@ class SOList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -351,8 +331,6 @@ class SODetail(generics.RetrieveUpdateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
|
||||
class SOLineItemList(generics.ListCreateAPIView):
|
||||
"""
|
||||
@ -398,8 +376,6 @@ class SOLineItemList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
filter_backends = [DjangoFilterBackend]
|
||||
|
||||
filter_fields = [
|
||||
@ -414,8 +390,6 @@ class SOLineItemDetail(generics.RetrieveUpdateAPIView):
|
||||
queryset = SalesOrderLineItem.objects.all()
|
||||
serializer_class = SOLineItemSerializer
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
|
||||
class POAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
|
||||
"""
|
||||
|
@ -24,7 +24,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<hr>
|
||||
<h4>
|
||||
{{ order }}
|
||||
{% if user.is_staff and perms.order.change_purchaseorder %}
|
||||
{% if user.is_staff and roles.purchase_order.change %}
|
||||
<a href="{% url 'admin:order_purchaseorder_change' order.pk %}"><span title='{% trans "Admin view" %}' class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h4>
|
||||
|
@ -34,7 +34,7 @@ src="{% static 'img/blank_image.png' %}"
|
||||
<hr>
|
||||
<h4>
|
||||
{{ order }}
|
||||
{% if user.is_staff and perms.order.change_salesorder %}
|
||||
{% if user.is_staff and roles.sales_order.change %}
|
||||
<a href="{% url 'admin:order_salesorder_change' order.pk %}"><span title='{% trans "Admin view" %}' class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h4>
|
||||
|
@ -44,6 +44,10 @@ class PartCategoryTree(TreeSerializer):
|
||||
def get_items(self):
|
||||
return PartCategory.objects.all().prefetch_related('parts', 'children')
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
|
||||
class CategoryList(generics.ListCreateAPIView):
|
||||
""" API endpoint for accessing a list of PartCategory objects.
|
||||
@ -55,10 +59,6 @@ class CategoryList(generics.ListCreateAPIView):
|
||||
queryset = PartCategory.objects.all()
|
||||
serializer_class = part_serializers.CategorySerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
def get_queryset(self):
|
||||
"""
|
||||
Custom filtering:
|
||||
@ -119,10 +119,6 @@ class PartSalePriceList(generics.ListCreateAPIView):
|
||||
queryset = PartSellPriceBreak.objects.all()
|
||||
serializer_class = part_serializers.PartSalePriceSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend
|
||||
]
|
||||
@ -182,8 +178,6 @@ class PartTestTemplateList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.OrderingFilter,
|
||||
@ -221,10 +215,6 @@ class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
|
||||
queryset = Part.objects.all()
|
||||
serializer_class = part_serializers.PartThumbSerializerUpdate
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend
|
||||
]
|
||||
@ -246,10 +236,6 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
|
||||
try:
|
||||
@ -580,10 +566,6 @@ class PartList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -676,10 +658,6 @@ class PartParameterTemplateList(generics.ListCreateAPIView):
|
||||
queryset = PartParameterTemplate.objects.all()
|
||||
serializer_class = part_serializers.PartParameterTemplateSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
filters.OrderingFilter,
|
||||
]
|
||||
@ -699,10 +677,6 @@ class PartParameterList(generics.ListCreateAPIView):
|
||||
queryset = PartParameter.objects.all()
|
||||
serializer_class = part_serializers.PartParameterSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend
|
||||
]
|
||||
@ -796,10 +770,6 @@ class BomList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -816,10 +786,6 @@ class BomDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
queryset = BomItem.objects.all()
|
||||
serializer_class = part_serializers.BomItemSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
|
||||
class BomItemValidate(generics.UpdateAPIView):
|
||||
""" API endpoint for validating a BomItem """
|
||||
|
@ -39,10 +39,12 @@
|
||||
<button class='btn btn-default action-button' type='button' title='{% trans "New BOM Item" %}' id='bom-item-new'><span class='fas fa-plus-circle'></span></button>
|
||||
<button class='btn btn-default action-button' type='button' title='{% trans "Finish Editing" %}' id='editing-finished'><span class='fas fa-check-circle'></span></button>
|
||||
{% elif part.active %}
|
||||
{% if roles.part.change %}
|
||||
<button class='btn btn-default action-button' type='button' title='{% trans "Edit BOM" %}' id='edit-bom'><span class='fas fa-edit'></span></button>
|
||||
{% if part.is_bom_valid == False %}
|
||||
<button class='btn btn-default action-button' id='validate-bom' title='{% trans "Validate Bill of Materials" %}' type='button'><span class='fas fa-clipboard-check'></span></button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<button title='{% trans "Export Bill of Materials" %}' class='btn btn-default action-button' id='download-bom' type='button'><span class='fas fa-file-download'></span></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -1,15 +1,18 @@
|
||||
{% extends "part/part_base.html" %}
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
{% block details %}
|
||||
|
||||
{% include 'part/tabs.html' with tab='build' %}
|
||||
|
||||
<h3>Part Builds</h3>
|
||||
<h3>{% trans "Part Builds" %}</h3>
|
||||
|
||||
<div id='button-toolbar'>
|
||||
<div class='button-toolbar container-flui' style='float: right';>
|
||||
{% if part.active %}
|
||||
<button class="btn btn-success" id='start-build'>Start New Build</button>
|
||||
{% if roles.build.add %}
|
||||
<button class="btn btn-success" id='start-build'>{% trans "Start New Build" %}</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class='filter-list' id='filter-list-build'>
|
||||
<!-- Empty div for filters -->
|
||||
|
@ -9,7 +9,7 @@
|
||||
{% if category %}
|
||||
<h3>
|
||||
{{ category.name }}
|
||||
{% if user.is_staff and perms.part.change_partcategory %}
|
||||
{% if user.is_staff and roles.part.change %}
|
||||
<a href="{% url 'admin:part_partcategory_change' category.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
@ -20,17 +20,23 @@
|
||||
{% endif %}
|
||||
<p>
|
||||
<div class='btn-group action-buttons'>
|
||||
{% if roles.part.add %}
|
||||
<button class='btn btn-default' id='cat-create' title='{% trans "Create new part category" %}'>
|
||||
<span class='fas fa-plus-circle icon-green'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if category %}
|
||||
{% if roles.part.change %}
|
||||
<button class='btn btn-default' id='cat-edit' title='{% trans "Edit part category" %}'>
|
||||
<span class='fas fa-edit icon-blue'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if roles.part.delete %}
|
||||
<button class='btn btn-default' id='cat-delete' title='{% trans "Delete part category" %}'>
|
||||
<span class='fas fa-trash-alt icon-red'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</p>
|
||||
</div>
|
||||
@ -104,11 +110,15 @@
|
||||
<div class='button-toolbar container-fluid' style="float: right;">
|
||||
<div class='btn-group'>
|
||||
<button class='btn btn-default' id='part-export' title='{% trans "Export Part Data" %}'>{% trans "Export" %}</button>
|
||||
{% if roles.part.add %}
|
||||
<button class='btn btn-success' id='part-create' title='{% trans "Create new part" %}'>{% trans "New Part" %}</button>
|
||||
{% endif %}
|
||||
<div class='btn-group'>
|
||||
<button id='part-options' class='btn btn-primary dropdown-toggle' type='button' data-toggle="dropdown">{% trans "Options" %}<span class='caret'></span></button>
|
||||
<ul class='dropdown-menu'>
|
||||
{% if roles.part.change %}
|
||||
<li><a href='#' id='multi-part-category' title='{% trans "Set category" %}'>{% trans "Set Category" %}</a></li>
|
||||
{% endif %}
|
||||
<li><a href='#' id='multi-part-order' title='{% trans "Order parts" %}'>{% trans "Order Parts" %}</a></li>
|
||||
<li><a href='#' id='multi-part-export' title='{% trans "Export" %}'>{% trans "Export Data" %}</a></li>
|
||||
</ul>
|
||||
@ -180,6 +190,7 @@
|
||||
location.href = url;
|
||||
});
|
||||
|
||||
{% if roles.part.add %}
|
||||
$("#part-create").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-create' %}",
|
||||
@ -207,6 +218,7 @@
|
||||
}
|
||||
);
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% if category %}
|
||||
$("#cat-edit").click(function () {
|
||||
|
@ -29,7 +29,9 @@
|
||||
<h4>{% trans "Part Notes" %}</h4>
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
{% if roles.part.change %}
|
||||
<button title='{% trans "Edit notes" %}' class='btn btn-default action-button float-right' id='edit-notes'><span class='fas fa-edit'></span></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<hr>
|
||||
|
@ -10,7 +10,9 @@
|
||||
|
||||
<div id='button-toolbar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
{% if roles.part.add %}
|
||||
<button title='{% trans "Add new parameter" %}' class='btn btn-success' id='param-create'>{% trans "New Parameter" %}</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -30,8 +32,12 @@
|
||||
<td>
|
||||
{{ param.template.units }}
|
||||
<div class='btn-group' style='float: right;'>
|
||||
{% if roles.part.change %}
|
||||
<button title='{% trans "Edit" %}' class='btn btn-default btn-glyph param-edit' url="{% url 'part-param-edit' param.id %}" type='button'><span class='fas fa-edit'/></button>
|
||||
{% endif %}
|
||||
{% if roles.part.delete %}
|
||||
<button title='{% trans "Delete" %}' class='btn btn-default btn-glyph param-delete' url="{% url 'part-param-delete' param.id %}" type='button'><span class='fas fa-trash-alt icon-red'/></button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -48,6 +54,7 @@
|
||||
$('#param-table').inventreeTable({
|
||||
});
|
||||
|
||||
{% if roles.part.add %}
|
||||
$('#param-create').click(function() {
|
||||
launchModalForm("{% url 'part-param-create' %}?part={{ part.id }}", {
|
||||
reload: true,
|
||||
@ -59,6 +66,7 @@
|
||||
}],
|
||||
});
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$('.param-edit').click(function() {
|
||||
var button = $(this);
|
||||
|
@ -28,7 +28,7 @@
|
||||
<div class="media-body">
|
||||
<h3>
|
||||
{{ part.full_name }}
|
||||
{% if user.is_staff and perms.part.change_part %}
|
||||
{% if user.is_staff and roles.part.change %}
|
||||
<a href="{% url 'admin:part_part_change' part.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
{% if not part.active %}
|
||||
@ -56,26 +56,36 @@
|
||||
<button type='button' class='btn btn-default' id='price-button' title='{% trans "Show pricing information" %}'>
|
||||
<span id='part-price-icon' class='fas fa-dollar-sign'/>
|
||||
</button>
|
||||
<button type='button' class='btn btn-default' id='part-count' title='Count part stock'>
|
||||
{% if roles.stock.change %}
|
||||
<button type='button' class='btn btn-default' id='part-count' title='{% trans "Count part stock" %}'>
|
||||
<span class='fas fa-clipboard-list'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if part.purchaseable %}
|
||||
<button type='button' class='btn btn-default' id='part-order' title='Order part'>
|
||||
{% if roles.purchase_order.add %}
|
||||
<button type='button' class='btn btn-default' id='part-order' title='{% trans "Order part" %}'>
|
||||
<span id='part-order-icon' class='fas fa-shopping-cart'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<!-- Part actions -->
|
||||
{% if roles.part.add or roles.part.change or roles.part.delete %}
|
||||
<div class='btn-group'>
|
||||
<button id='part-actions' title='{% trans "Part actions" %}' class='btn btn-default dropdown-toggle' type='button' data-toggle='dropdown'> <span class='fas fa-shapes'></span> <span class='caret'></span></button>
|
||||
<ul class='dropdown-menu'>
|
||||
{% if roles.part.add %}
|
||||
<li><a href='#' id='part-duplicate'><span class='fas fa-copy'></span> {% trans "Duplicate part" %}</a></li>
|
||||
{% endif %}
|
||||
{% if roles.part.change %}
|
||||
<li><a href='#' id='part-edit'><span class='fas fa-edit icon-blue'></span> {% trans "Edit part" %}</a></li>
|
||||
{% if not part.active %}
|
||||
{% endif %}
|
||||
{% if not part.active and roles.part.delete %}
|
||||
<li><a href='#' id='part-delete'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete part" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<table class='table table-condensed'>
|
||||
<col width='25'>
|
||||
@ -274,6 +284,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
{% if roles.part.change %}
|
||||
$("#part-edit").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-edit' part.id %}",
|
||||
@ -282,6 +293,7 @@
|
||||
}
|
||||
);
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
$("#part-order").click(function() {
|
||||
launchModalForm("{% url 'order-parts' %}", {
|
||||
@ -292,6 +304,7 @@
|
||||
});
|
||||
});
|
||||
|
||||
{% if roles.part.add %}
|
||||
$("#part-duplicate").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-duplicate' part.id %}",
|
||||
@ -300,8 +313,9 @@
|
||||
}
|
||||
);
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% if not part.active %}
|
||||
{% if not part.active and roles.part.delete %}
|
||||
$("#part-delete").click(function() {
|
||||
launchModalForm(
|
||||
"{% url 'part-delete' part.id %}",
|
||||
|
@ -26,14 +26,17 @@
|
||||
{% if part.assembly %}
|
||||
<li{% ifequal tab 'bom' %} class="active"{% endifequal %}>
|
||||
<a href="{% url 'part-bom' part.id %}">{% trans "BOM" %}<span class="badge{% if part.is_bom_valid == False %} badge-alert{% endif %}">{{ part.bom_count }}</span></a></li>
|
||||
{% if roles.build.view %}
|
||||
<li{% ifequal tab 'build' %} class="active"{% endifequal %}>
|
||||
<a href="{% url 'part-build' part.id %}">{% trans "Build Orders" %}<span class='badge'>{{ part.builds.count }}</span></a></li>
|
||||
<a href="{% url 'part-build' part.id %}">{% trans "Build Orders" %}<span class='badge'>{{ part.builds.count }}</span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if part.component or part.used_in_count > 0 %}
|
||||
<li{% ifequal tab 'used' %} class="active"{% endifequal %}>
|
||||
<a href="{% url 'part-used-in' part.id %}">{% trans "Used In" %} {% if part.used_in_count > 0 %}<span class="badge">{{ part.used_in_count }}</span>{% endif %}</a></li>
|
||||
{% endif %}
|
||||
{% if part.purchaseable %}
|
||||
{% if part.purchaseable and roles.purchase_order.view %}
|
||||
{% if part.is_template == False %}
|
||||
<li{% ifequal tab 'suppliers' %} class="active"{% endifequal %}>
|
||||
<a href="{% url 'part-suppliers' part.id %}">{% trans "Suppliers" %}
|
||||
@ -45,7 +48,7 @@
|
||||
<a href="{% url 'part-orders' part.id %}">{% trans "Purchase Orders" %} <span class='badge'>{{ part.purchase_orders|length }}</span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if part.salable %}
|
||||
{% if part.salable and roles.sales_order.view %}
|
||||
<li {% if tab == 'sales-prices' %}class='active'{% endif %}>
|
||||
<a href="{% url 'part-sale-prices' part.id %}">{% trans "Sale Price" %}</a>
|
||||
</li>
|
||||
|
@ -3,6 +3,7 @@ from rest_framework import status
|
||||
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from part.models import Part
|
||||
from stock.models import StockItem
|
||||
@ -29,7 +30,26 @@ class PartAPITest(APITestCase):
|
||||
def setUp(self):
|
||||
# Create a user for auth
|
||||
User = get_user_model()
|
||||
User.objects.create_user('testuser', 'test@testing.com', 'password')
|
||||
self.user = User.objects.create_user(
|
||||
username='testuser',
|
||||
email='test@testing.com',
|
||||
password='password'
|
||||
)
|
||||
|
||||
# Put the user into a group with the correct permissions
|
||||
group = Group.objects.create(name='mygroup')
|
||||
self.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()
|
||||
|
||||
group.save()
|
||||
|
||||
self.client.login(username='testuser', password='password')
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from .models import Part
|
||||
|
||||
@ -23,7 +24,24 @@ class PartViewTestCase(TestCase):
|
||||
|
||||
# Create a user
|
||||
User = get_user_model()
|
||||
User.objects.create_user('username', 'user@email.com', 'password')
|
||||
self.user = User.objects.create_user(
|
||||
username='username',
|
||||
email='user@email.com',
|
||||
password='password'
|
||||
)
|
||||
|
||||
# Put the user into a group with the correct permissions
|
||||
group = Group.objects.create(name='mygroup')
|
||||
self.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()
|
||||
|
||||
self.client.login(username='username', password='password')
|
||||
|
||||
@ -140,12 +158,14 @@ class PartTests(PartViewTestCase):
|
||||
""" Tests for Part forms """
|
||||
|
||||
def test_part_edit(self):
|
||||
|
||||
response = self.client.get(reverse('part-edit', args=(1,)), HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
keys = response.context.keys()
|
||||
data = str(response.content)
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
self.assertIn('part', keys)
|
||||
self.assertIn('csrf_token', keys)
|
||||
|
||||
@ -189,6 +209,8 @@ class PartAttachmentTests(PartViewTestCase):
|
||||
response = self.client.get(reverse('part-attachment-create'), {'part': 1}, HTTP_X_REQUESTED_WITH='XMLHttpRequest')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
# TODO - Create a new attachment using this view
|
||||
|
||||
def test_invalid_create(self):
|
||||
""" test creation of an attachment for an invalid part """
|
||||
|
||||
|
@ -38,17 +38,21 @@ from .admin import PartResource
|
||||
|
||||
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||
from InvenTree.views import QRCodeView
|
||||
from InvenTree.views import InvenTreeRoleMixin
|
||||
|
||||
from InvenTree.helpers import DownloadFile, str2bool
|
||||
|
||||
|
||||
class PartIndex(ListView):
|
||||
class PartIndex(InvenTreeRoleMixin, ListView):
|
||||
""" View for displaying list of Part objects
|
||||
"""
|
||||
|
||||
model = Part
|
||||
template_name = 'part/category.html'
|
||||
context_object_name = 'parts'
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
def get_queryset(self):
|
||||
return Part.objects.all().select_related('category')
|
||||
|
||||
@ -76,6 +80,8 @@ class PartAttachmentCreate(AjaxCreateView):
|
||||
ajax_form_title = _("Add part attachment")
|
||||
ajax_template_name = "modal_form.html"
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def post_save(self):
|
||||
""" Record the user that uploaded the attachment """
|
||||
self.object.user = self.request.user
|
||||
@ -123,6 +129,8 @@ class PartAttachmentEdit(AjaxUpdateView):
|
||||
form_class = part_forms.EditPartAttachmentForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Edit attachment')
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
@ -145,6 +153,8 @@ class PartAttachmentDelete(AjaxDeleteView):
|
||||
ajax_template_name = "attachment_delete.html"
|
||||
context_object_name = "attachment"
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'danger': _('Deleted part attachment')
|
||||
@ -157,6 +167,8 @@ class PartTestTemplateCreate(AjaxCreateView):
|
||||
model = PartTestTemplate
|
||||
form_class = part_forms.EditPartTestTemplateForm
|
||||
ajax_form_title = _("Create Test Template")
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def get_initial(self):
|
||||
|
||||
@ -185,6 +197,8 @@ class PartTestTemplateEdit(AjaxUpdateView):
|
||||
form_class = part_forms.EditPartTestTemplateForm
|
||||
ajax_form_title = _("Edit Test Template")
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_form(self):
|
||||
|
||||
form = super().get_form()
|
||||
@ -199,6 +213,8 @@ class PartTestTemplateDelete(AjaxDeleteView):
|
||||
model = PartTestTemplate
|
||||
ajax_form_title = _("Delete Test Template")
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
||||
|
||||
class PartSetCategory(AjaxUpdateView):
|
||||
""" View for settings the part category for multiple parts at once """
|
||||
@ -207,6 +223,8 @@ class PartSetCategory(AjaxUpdateView):
|
||||
ajax_form_title = _('Set Part Category')
|
||||
form_class = part_forms.SetPartCategoryForm
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
category = None
|
||||
parts = []
|
||||
|
||||
@ -290,6 +308,8 @@ class MakePartVariant(AjaxCreateView):
|
||||
ajax_form_title = _('Create Variant')
|
||||
ajax_template_name = 'part/variant_part.html'
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def get_part_template(self):
|
||||
return get_object_or_404(Part, id=self.kwargs['pk'])
|
||||
|
||||
@ -368,6 +388,8 @@ class PartDuplicate(AjaxCreateView):
|
||||
ajax_form_title = _("Duplicate Part")
|
||||
ajax_template_name = "part/copy_part.html"
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'success': _('Copied part')
|
||||
@ -491,6 +513,8 @@ class PartCreate(AjaxCreateView):
|
||||
ajax_form_title = _('Create new part')
|
||||
ajax_template_name = 'part/create_part.html'
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'success': _("Created new part"),
|
||||
@ -613,6 +637,8 @@ class PartNotes(UpdateView):
|
||||
template_name = 'part/notes.html'
|
||||
model = Part
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
fields = ['notes']
|
||||
|
||||
def get_success_url(self):
|
||||
@ -634,7 +660,7 @@ class PartNotes(UpdateView):
|
||||
return ctx
|
||||
|
||||
|
||||
class PartDetail(DetailView):
|
||||
class PartDetail(InvenTreeRoleMixin, DetailView):
|
||||
""" Detail view for Part object
|
||||
"""
|
||||
|
||||
@ -642,6 +668,8 @@ class PartDetail(DetailView):
|
||||
queryset = Part.objects.all().select_related('category')
|
||||
template_name = 'part/detail.html'
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
# Add in some extra context information based on query params
|
||||
def get_context_data(self, **kwargs):
|
||||
""" Provide extra context data to template
|
||||
@ -706,6 +734,8 @@ class PartQRCode(QRCodeView):
|
||||
|
||||
ajax_form_title = _("Part QR Code")
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
def get_qr_data(self):
|
||||
""" Generate QR code data for the Part """
|
||||
|
||||
@ -722,8 +752,11 @@ class PartImageUpload(AjaxUpdateView):
|
||||
model = Part
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Upload Part Image')
|
||||
|
||||
form_class = part_forms.PartImageForm
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'success': _('Updated part image'),
|
||||
@ -737,6 +770,8 @@ class PartImageSelect(AjaxUpdateView):
|
||||
ajax_template_name = 'part/select_image.html'
|
||||
ajax_form_title = _('Select Part Image')
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
fields = [
|
||||
'image',
|
||||
]
|
||||
@ -778,6 +813,8 @@ class PartEdit(AjaxUpdateView):
|
||||
ajax_form_title = _('Edit Part Properties')
|
||||
context_object_name = 'part'
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_form(self):
|
||||
""" Create form for Part editing.
|
||||
Overrides default get_form() method to limit the choices
|
||||
@ -802,6 +839,8 @@ class BomValidate(AjaxUpdateView):
|
||||
context_object_name = 'part'
|
||||
form_class = part_forms.BomValidateForm
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_context(self):
|
||||
return {
|
||||
'part': self.get_object(),
|
||||
@ -832,7 +871,7 @@ class BomValidate(AjaxUpdateView):
|
||||
return self.renderJsonResponse(request, form, data, context=self.get_context())
|
||||
|
||||
|
||||
class BomUpload(FormView):
|
||||
class BomUpload(InvenTreeRoleMixin, FormView):
|
||||
""" View for uploading a BOM file, and handling BOM data importing.
|
||||
|
||||
The BOM upload process is as follows:
|
||||
@ -868,6 +907,8 @@ class BomUpload(FormView):
|
||||
missing_columns = []
|
||||
allowed_parts = []
|
||||
|
||||
role_required = ('part.change', 'part.add')
|
||||
|
||||
def get_success_url(self):
|
||||
part = self.get_object()
|
||||
return reverse('upload-bom', kwargs={'pk': part.id})
|
||||
@ -1466,6 +1507,8 @@ class BomUpload(FormView):
|
||||
class PartExport(AjaxView):
|
||||
""" Export a CSV file containing information on multiple parts """
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
def get_parts(self, request):
|
||||
""" Extract part list from the POST parameters.
|
||||
Parts can be supplied as:
|
||||
@ -1543,6 +1586,8 @@ class BomDownload(AjaxView):
|
||||
- File format should be passed as a query param e.g. ?format=csv
|
||||
"""
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
model = Part
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
@ -1596,6 +1641,8 @@ class BomExport(AjaxView):
|
||||
form_class = part_forms.BomExportForm
|
||||
ajax_form_title = _("Export Bill of Materials")
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
return self.renderJsonResponse(request, self.form_class())
|
||||
|
||||
@ -1645,6 +1692,8 @@ class PartDelete(AjaxDeleteView):
|
||||
ajax_form_title = _('Confirm Part Deletion')
|
||||
context_object_name = 'part'
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
||||
success_url = '/part/'
|
||||
|
||||
def get_data(self):
|
||||
@ -1661,6 +1710,8 @@ class PartPricing(AjaxView):
|
||||
ajax_form_title = _("Part Pricing")
|
||||
form_class = part_forms.PartPriceForm
|
||||
|
||||
role_required = ['sales_order.view', 'part.view']
|
||||
|
||||
def get_part(self):
|
||||
try:
|
||||
return Part.objects.get(id=self.kwargs['pk'])
|
||||
@ -1778,6 +1829,8 @@ class PartPricing(AjaxView):
|
||||
class PartParameterTemplateCreate(AjaxCreateView):
|
||||
""" View for creating a new PartParameterTemplate """
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
model = PartParameterTemplate
|
||||
form_class = part_forms.EditPartParameterTemplateForm
|
||||
ajax_form_title = _('Create Part Parameter Template')
|
||||
@ -1786,6 +1839,8 @@ class PartParameterTemplateCreate(AjaxCreateView):
|
||||
class PartParameterTemplateEdit(AjaxUpdateView):
|
||||
""" View for editing a PartParameterTemplate """
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
model = PartParameterTemplate
|
||||
form_class = part_forms.EditPartParameterTemplateForm
|
||||
ajax_form_title = _('Edit Part Parameter Template')
|
||||
@ -1794,6 +1849,8 @@ class PartParameterTemplateEdit(AjaxUpdateView):
|
||||
class PartParameterTemplateDelete(AjaxDeleteView):
|
||||
""" View for deleting an existing PartParameterTemplate """
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
||||
model = PartParameterTemplate
|
||||
ajax_form_title = _("Delete Part Parameter Template")
|
||||
|
||||
@ -1801,6 +1858,8 @@ class PartParameterTemplateDelete(AjaxDeleteView):
|
||||
class PartParameterCreate(AjaxCreateView):
|
||||
""" View for creating a new PartParameter """
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
model = PartParameter
|
||||
form_class = part_forms.EditPartParameterForm
|
||||
ajax_form_title = _('Create Part Parameter')
|
||||
@ -1851,6 +1910,8 @@ class PartParameterCreate(AjaxCreateView):
|
||||
class PartParameterEdit(AjaxUpdateView):
|
||||
""" View for editing a PartParameter """
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
model = PartParameter
|
||||
form_class = part_forms.EditPartParameterForm
|
||||
ajax_form_title = _('Edit Part Parameter')
|
||||
@ -1865,12 +1926,14 @@ class PartParameterEdit(AjaxUpdateView):
|
||||
class PartParameterDelete(AjaxDeleteView):
|
||||
""" View for deleting a PartParameter """
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
||||
model = PartParameter
|
||||
ajax_template_name = 'part/param_delete.html'
|
||||
ajax_form_title = _('Delete Part Parameter')
|
||||
|
||||
|
||||
class CategoryDetail(DetailView):
|
||||
class CategoryDetail(InvenTreeRoleMixin, DetailView):
|
||||
""" Detail view for PartCategory """
|
||||
|
||||
model = PartCategory
|
||||
@ -1878,6 +1941,8 @@ class CategoryDetail(DetailView):
|
||||
queryset = PartCategory.objects.all().prefetch_related('children')
|
||||
template_name = 'part/category_partlist.html'
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
context = super(CategoryDetail, self).get_context_data(**kwargs).copy()
|
||||
@ -1926,6 +1991,8 @@ class CategoryEdit(AjaxUpdateView):
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Edit Part Category')
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(CategoryEdit, self).get_context_data(**kwargs).copy()
|
||||
|
||||
@ -1963,6 +2030,8 @@ class CategoryDelete(AjaxDeleteView):
|
||||
context_object_name = 'category'
|
||||
success_url = '/part/'
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
'danger': _('Part category was deleted'),
|
||||
@ -1977,6 +2046,8 @@ class CategoryCreate(AjaxCreateView):
|
||||
ajax_template_name = 'modal_form.html'
|
||||
form_class = part_forms.EditCategoryForm
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
""" Add extra context data to template.
|
||||
|
||||
@ -2012,12 +2083,14 @@ class CategoryCreate(AjaxCreateView):
|
||||
return initials
|
||||
|
||||
|
||||
class BomItemDetail(DetailView):
|
||||
class BomItemDetail(InvenTreeRoleMixin, DetailView):
|
||||
""" Detail view for BomItem """
|
||||
context_object_name = 'item'
|
||||
queryset = BomItem.objects.all()
|
||||
template_name = 'part/bom-detail.html'
|
||||
|
||||
role_required = 'part.view'
|
||||
|
||||
|
||||
class BomItemCreate(AjaxCreateView):
|
||||
""" Create view for making a new BomItem object """
|
||||
@ -2026,6 +2099,8 @@ class BomItemCreate(AjaxCreateView):
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Create BOM item')
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def get_form(self):
|
||||
""" Override get_form() method to reduce Part selection options.
|
||||
|
||||
@ -2092,6 +2167,8 @@ class BomItemEdit(AjaxUpdateView):
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Edit BOM item')
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_form(self):
|
||||
""" Override get_form() method to filter part selection options
|
||||
|
||||
@ -2140,6 +2217,8 @@ class BomItemDelete(AjaxDeleteView):
|
||||
context_object_name = 'item'
|
||||
ajax_form_title = _('Confim BOM item deletion')
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
||||
|
||||
class PartSalePriceBreakCreate(AjaxCreateView):
|
||||
""" View for creating a sale price break for a part """
|
||||
@ -2147,6 +2226,8 @@ class PartSalePriceBreakCreate(AjaxCreateView):
|
||||
model = PartSellPriceBreak
|
||||
form_class = part_forms.EditPartSalePriceBreakForm
|
||||
ajax_form_title = _('Add Price Break')
|
||||
|
||||
role_required = 'part.add'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
@ -2197,6 +2278,8 @@ class PartSalePriceBreakEdit(AjaxUpdateView):
|
||||
form_class = part_forms.EditPartSalePriceBreakForm
|
||||
ajax_form_title = _('Edit Price Break')
|
||||
|
||||
role_required = 'part.change'
|
||||
|
||||
def get_form(self):
|
||||
|
||||
form = super().get_form()
|
||||
@ -2211,3 +2294,5 @@ class PartSalePriceBreakDelete(AjaxDeleteView):
|
||||
model = PartSellPriceBreak
|
||||
ajax_form_title = _("Delete Price Break")
|
||||
ajax_template_name = "modal_delete_form.html"
|
||||
|
||||
role_required = 'part.delete'
|
||||
|
@ -52,6 +52,10 @@ class StockCategoryTree(TreeSerializer):
|
||||
def get_items(self):
|
||||
return StockLocation.objects.all().prefetch_related('stock_items', 'children')
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
|
||||
class StockDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
""" API detail endpoint for Stock object
|
||||
@ -68,7 +72,6 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
||||
queryset = StockItem.objects.all()
|
||||
serializer_class = StockItemSerializer
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
def get_queryset(self, *args, **kwargs):
|
||||
|
||||
@ -289,10 +292,6 @@ class StockLocationList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -695,10 +694,6 @@ class StockList(generics.ListCreateAPIView):
|
||||
|
||||
return queryset
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -744,10 +739,6 @@ class StockItemTestResultList(generics.ListCreateAPIView):
|
||||
queryset = StockItemTestResult.objects.all()
|
||||
serializer_class = StockItemTestResultSerializer
|
||||
|
||||
permission_classes = [
|
||||
permissions.IsAuthenticated,
|
||||
]
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
filters.SearchFilter,
|
||||
@ -799,7 +790,6 @@ class StockTrackingList(generics.ListCreateAPIView):
|
||||
|
||||
queryset = StockItemTracking.objects.all()
|
||||
serializer_class = StockTrackingSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
try:
|
||||
@ -871,7 +861,6 @@ class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
|
||||
queryset = StockLocation.objects.all()
|
||||
serializer_class = LocationSerializer
|
||||
permission_classes = (permissions.IsAuthenticated,)
|
||||
|
||||
|
||||
stock_endpoints = [
|
||||
|
@ -65,7 +65,7 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
|
||||
{% else %}
|
||||
<a href='{% url "part-detail" item.part.pk %}'>{{ item.part.full_name }}</a> × {% decimal item.quantity %}
|
||||
{% endif %}
|
||||
{% if user.is_staff and perms.stock.change_stockitem %}
|
||||
{% if user.is_staff and roles.stock.change %}
|
||||
<a href="{% url 'admin:stock_stockitem_change' item.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h4>
|
||||
|
@ -8,7 +8,7 @@
|
||||
{% if location %}
|
||||
<h3>
|
||||
{{ location.name }}
|
||||
{% if user.is_staff and perms.stock.change_stocklocation %}
|
||||
{% if user.is_staff and roles.stock.change %}
|
||||
<a href="{% url 'admin:stock_stocklocation_change' location.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h3>
|
||||
|
@ -3,6 +3,8 @@ from rest_framework import status
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
from InvenTree.helpers import addUserPermissions
|
||||
|
||||
from .models import StockLocation
|
||||
|
||||
|
||||
@ -22,6 +24,20 @@ class StockAPITestCase(APITestCase):
|
||||
# Create a user for auth
|
||||
User = get_user_model()
|
||||
self.user = User.objects.create_user('testuser', 'test@testing.com', 'password')
|
||||
|
||||
# Add the necessary permissions to the user
|
||||
perms = [
|
||||
'view_stockitemtestresult',
|
||||
'change_stockitemtestresult',
|
||||
'add_stockitemtestresult',
|
||||
'add_stocklocation',
|
||||
'change_stocklocation',
|
||||
'add_stockitem',
|
||||
'change_stockitem',
|
||||
]
|
||||
|
||||
addUserPermissions(self.user, perms)
|
||||
|
||||
self.client.login(username='testuser', password='password')
|
||||
|
||||
def doPost(self, url, data={}):
|
||||
|
18
InvenTree/templates/403.html
Normal file
18
InvenTree/templates/403.html
Normal file
@ -0,0 +1,18 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block page_title %}
|
||||
InvenTree | {% trans "Permission Denied" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class='container-fluid'>
|
||||
<h3>{% trans "Permission Denied" %}</h3>
|
||||
|
||||
<div class='alert alert-danger alert-block'>
|
||||
{% trans "You do not have permission to view this page." %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,7 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% block page_title %}
|
||||
InvenTree | Index
|
||||
InvenTree | {% trans "Index" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
@ -9,24 +9,24 @@ InvenTree | Index
|
||||
<hr>
|
||||
|
||||
<div class='col-sm-6'>
|
||||
{% if perms.part.view_part %}
|
||||
{% if roles.part.view %}
|
||||
{% include "InvenTree/latest_parts.html" with collapse_id="latest_parts" %}
|
||||
{% include "InvenTree/bom_invalid.html" with collapse_id="bom_invalid" %}
|
||||
{% include "InvenTree/starred_parts.html" with collapse_id="starred" %}
|
||||
{% endif %}
|
||||
{% if perms.build.view_build %}
|
||||
{% if roles.build.view %}
|
||||
{% include "InvenTree/build_pending.html" with collapse_id="build_pending" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
{% if perms.stock.view_stockitem %}
|
||||
{% if roles.stock.view %}
|
||||
{% include "InvenTree/low_stock.html" with collapse_id="order" %}
|
||||
{% include "InvenTree/required_stock_build.html" with collapse_id="stock_to_build" %}
|
||||
{% endif %}
|
||||
{% if perms.order.view_purchaseorder %}
|
||||
{% if roles.purchase_order.view %}
|
||||
{% include "InvenTree/po_outstanding.html" with collapse_id="po_outstanding" %}
|
||||
{% endif %}
|
||||
{% if perms.order.view_salesorder %}
|
||||
{% if roles.sales_order.view %}
|
||||
{% include "InvenTree/so_outstanding.html" with collapse_id="so_outstanding" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
|
@ -15,16 +15,16 @@
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
{% if perms.part.view_part or perms.part.view_partcategory %}
|
||||
{% if roles.part.view %}
|
||||
<li><a href="{% url 'part-index' %}"><span class='fas fa-shapes icon-header'></span>{% trans "Parts" %}</a></li>
|
||||
{% endif %}
|
||||
{% if perms.stock.view_stockitem or perms.part.view_stocklocation %}
|
||||
{% if roles.stock.view %}
|
||||
<li><a href="{% url 'stock-index' %}"><span class='fas fa-boxes icon-header'></span>{% trans "Stock" %}</a></li>
|
||||
{% endif %}
|
||||
{% if perms.build.view_build %}
|
||||
{% if roles.build.view %}
|
||||
<li><a href="{% url 'build-index' %}"><span class='fas fa-tools icon-header'></span>{% trans "Build" %}</a></li>
|
||||
{% endif %}
|
||||
{% if perms.order.view_purchaseorder %}
|
||||
{% if roles.purchase_order.view %}
|
||||
<li class='nav navbar-nav'>
|
||||
<a class='dropdown-toggle' data-toggle='dropdown' href='#'><span class='fas fa-shopping-cart icon-header'></span>{% trans "Buy" %}</a>
|
||||
<ul class='dropdown-menu'>
|
||||
@ -34,7 +34,7 @@
|
||||
</ul>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if perms.order.view_salesorder %}
|
||||
{% if roles.sales_order.view %}
|
||||
<li class='nav navbar-nav'>
|
||||
<a class='dropdown-toggle' data-toggle='dropdown' href='#'><span class='fas fa-truck icon-header'></span>{% trans "Sell" %}</a>
|
||||
<ul class='dropdown-menu'>
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div>
|
||||
<input fieldname='{{ field }}' class='slidey' type="checkbox" data-offstyle='warning' data-onstyle="success" data-size='small' data-toggle="toggle" {% if disabled %}disabled {% endif %}{% if state %}checked=""{% endif %} autocomplete="off">
|
||||
<input fieldname='{{ field }}' class='slidey' type="checkbox" data-offstyle='warning' data-onstyle="success" data-size='small' data-toggle="toggle" {% if disabled or not roles.part.change %}disabled {% endif %}{% if state %}checked=""{% endif %} autocomplete="off">
|
||||
</div>
|
@ -6,19 +6,27 @@
|
||||
<button class='btn btn-default' id='stock-export' title='{% trans "Export Stock Information" %}'>{% trans "Export" %}</button>
|
||||
{% if read_only %}
|
||||
{% else %}
|
||||
{% if roles.stock.add %}
|
||||
<button class="btn btn-success" id='item-create'>{% trans "New Stock Item" %}</button>
|
||||
{% endif %}
|
||||
{% if roles.stock.change or roles.stock.delete %}
|
||||
<div class="btn-group">
|
||||
<button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
{% if roles.stock.change %}
|
||||
<li><a href="#" id='multi-item-add' title='{% trans "Add to selected stock items" %}'>{% trans "Add stock" %}</a></li>
|
||||
<li><a href="#" id='multi-item-remove' title='{% trans "Remove from selected stock items" %}'>{% trans "Remove stock" %}</a></li>
|
||||
<li><a href="#" id='multi-item-stocktake' title='{% trans "Stocktake selected stock items" %}'>{% trans "Count stock" %}</a></li>
|
||||
<li><a href='#' id='multi-item-move' title='{% trans "Move selected stock items" %}'>{% trans "Move stock" %}</a></li>
|
||||
<li><a href='#' id='multi-item-order' title='{% trans "Order selected items" %}'>{% trans "Order stock" %}</a></li>
|
||||
{% endif %}
|
||||
{% if roles.stock.delete %}
|
||||
<li><a href='#' id='multi-item-delete' title='{% trans "Delete selected items" %}'>{% trans "Delete Stock" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class='filter-list' id='filter-list-stock'>
|
||||
<!-- An empty div in which the filter list will be constructed -->
|
||||
|
23
InvenTree/users/migrations/0003_auto_20201005_2227.py
Normal file
23
InvenTree/users/migrations/0003_auto_20201005_2227.py
Normal file
@ -0,0 +1,23 @@
|
||||
# Generated by Django 3.0.7 on 2020-10-05 22:27
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0002_auto_20201004_0158'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='ruleset',
|
||||
name='can_add',
|
||||
field=models.BooleanField(default=False, help_text='Permission to add items', verbose_name='Add'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='ruleset',
|
||||
name='can_change',
|
||||
field=models.BooleanField(default=False, help_text='Permissions to edit items', verbose_name='Change'),
|
||||
),
|
||||
]
|
@ -134,9 +134,9 @@ class RuleSet(models.Model):
|
||||
|
||||
can_view = models.BooleanField(verbose_name=_('View'), default=True, help_text=_('Permission to view items'))
|
||||
|
||||
can_add = models.BooleanField(verbose_name=_('Create'), default=False, help_text=_('Permission to add items'))
|
||||
can_add = models.BooleanField(verbose_name=_('Add'), default=False, help_text=_('Permission to add items'))
|
||||
|
||||
can_change = models.BooleanField(verbose_name=_('Update'), default=False, help_text=_('Permissions to edit items'))
|
||||
can_change = models.BooleanField(verbose_name=_('Change'), default=False, help_text=_('Permissions to edit items'))
|
||||
|
||||
can_delete = models.BooleanField(verbose_name=_('Delete'), default=False, help_text=_('Permission to delete items'))
|
||||
|
||||
@ -178,6 +178,10 @@ class RuleSet(models.Model):
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
if self.group:
|
||||
# Update the group too!
|
||||
self.group.save()
|
||||
|
||||
def get_models(self):
|
||||
"""
|
||||
Return the database tables / models that this ruleset covers.
|
||||
@ -334,9 +338,37 @@ def create_missing_rule_sets(sender, instance, **kwargs):
|
||||
then we can now use these RuleSet values to update the
|
||||
group permissions.
|
||||
"""
|
||||
created = kwargs.get('created', False)
|
||||
# To trigger the group permissions update: update_fields should not be None
|
||||
update_fields = kwargs.get('update_fields', None)
|
||||
|
||||
if created or update_fields:
|
||||
update_group_roles(instance)
|
||||
update_group_roles(instance)
|
||||
|
||||
|
||||
def check_user_role(user, role, permission):
|
||||
"""
|
||||
Check if a user has a particular role:permission combination.
|
||||
|
||||
If the user is a superuser, this will return True
|
||||
"""
|
||||
|
||||
if user.is_superuser:
|
||||
return True
|
||||
|
||||
for group in user.groups.all():
|
||||
|
||||
for rule in group.rule_sets.all():
|
||||
|
||||
if rule.name == role:
|
||||
|
||||
if permission == 'add' and rule.can_add:
|
||||
return True
|
||||
|
||||
if permission == 'change' and rule.can_change:
|
||||
return True
|
||||
|
||||
if permission == 'view' and rule.can_view:
|
||||
return True
|
||||
|
||||
if permission == 'delete' and rule.can_delete:
|
||||
return True
|
||||
|
||||
# No matching permissions found
|
||||
return False
|
||||
|
Loading…
Reference in New Issue
Block a user