From 4d49cb029f3873172e323635845f2b631d81f186 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 5 Oct 2020 23:49:32 +1100 Subject: [PATCH 01/26] Change part views to require permissions Also adds custom 403 page --- InvenTree/part/views.py | 93 ++++++++++++++++++++++-- InvenTree/templates/403.html | 18 +++++ InvenTree/templates/InvenTree/index.html | 4 +- 3 files changed, 108 insertions(+), 7 deletions(-) create mode 100644 InvenTree/templates/403.html diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index dc8d07f5cd..84628eff04 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -12,6 +12,7 @@ from django.shortcuts import HttpResponseRedirect from django.utils.translation import gettext_lazy as _ from django.urls import reverse, reverse_lazy from django.views.generic import DetailView, ListView, FormView, UpdateView +from django.contrib.auth.mixins import PermissionRequiredMixin from django.forms.models import model_to_dict from django.forms import HiddenInput, CheckboxInput from django.conf import settings @@ -42,12 +43,13 @@ from InvenTree.views import QRCodeView from InvenTree.helpers import DownloadFile, str2bool -class PartIndex(ListView): +class PartIndex(PermissionRequiredMixin, ListView): """ View for displaying list of Part objects """ model = Part template_name = 'part/category.html' context_object_name = 'parts' + permission_required = ('part.view_part', 'part.view_partcategory') def get_queryset(self): return Part.objects.all().select_related('category') @@ -76,6 +78,8 @@ class PartAttachmentCreate(AjaxCreateView): ajax_form_title = _("Add part attachment") ajax_template_name = "modal_form.html" + permission_required = 'part.create_partattachment' + def post_save(self): """ Record the user that uploaded the attachment """ self.object.user = self.request.user @@ -123,6 +127,8 @@ class PartAttachmentEdit(AjaxUpdateView): form_class = part_forms.EditPartAttachmentForm ajax_template_name = 'modal_form.html' ajax_form_title = _('Edit attachment') + + permission_required = 'part.change_partattachment' def get_data(self): return { @@ -145,6 +151,8 @@ class PartAttachmentDelete(AjaxDeleteView): ajax_template_name = "attachment_delete.html" context_object_name = "attachment" + permission_required = 'part.delete_partattachment' + def get_data(self): return { 'danger': _('Deleted part attachment') @@ -157,6 +165,8 @@ class PartTestTemplateCreate(AjaxCreateView): model = PartTestTemplate form_class = part_forms.EditPartTestTemplateForm ajax_form_title = _("Create Test Template") + + permission_required = 'part.create_parttesttemplate' def get_initial(self): @@ -185,6 +195,8 @@ class PartTestTemplateEdit(AjaxUpdateView): form_class = part_forms.EditPartTestTemplateForm ajax_form_title = _("Edit Test Template") + permission_required = 'part.change_parttesttemplate' + def get_form(self): form = super().get_form() @@ -199,6 +211,8 @@ class PartTestTemplateDelete(AjaxDeleteView): model = PartTestTemplate ajax_form_title = _("Delete Test Template") + permission_required = 'part.delete_parttesttemplate' + class PartSetCategory(AjaxUpdateView): """ View for settings the part category for multiple parts at once """ @@ -207,6 +221,8 @@ class PartSetCategory(AjaxUpdateView): ajax_form_title = _('Set Part Category') form_class = part_forms.SetPartCategoryForm + permission_required = 'part.change_part' + category = None parts = [] @@ -290,6 +306,8 @@ class MakePartVariant(AjaxCreateView): ajax_form_title = _('Create Variant') ajax_template_name = 'part/variant_part.html' + permission_required = 'part.create_part' + def get_part_template(self): return get_object_or_404(Part, id=self.kwargs['pk']) @@ -368,6 +386,8 @@ class PartDuplicate(AjaxCreateView): ajax_form_title = _("Duplicate Part") ajax_template_name = "part/copy_part.html" + permission_required = 'part.create_part' + def get_data(self): return { 'success': _('Copied part') @@ -491,6 +511,8 @@ class PartCreate(AjaxCreateView): ajax_form_title = _('Create new part') ajax_template_name = 'part/create_part.html' + permission_required = 'part.create_part' + def get_data(self): return { 'success': _("Created new part"), @@ -613,6 +635,8 @@ class PartNotes(UpdateView): template_name = 'part/notes.html' model = Part + permission_required = 'part.update_part' + fields = ['notes'] def get_success_url(self): @@ -634,7 +658,7 @@ class PartNotes(UpdateView): return ctx -class PartDetail(DetailView): +class PartDetail(PermissionRequiredMixin, DetailView): """ Detail view for Part object """ @@ -642,6 +666,8 @@ class PartDetail(DetailView): queryset = Part.objects.all().select_related('category') template_name = 'part/detail.html' + permission_required = 'part.view_part' + # Add in some extra context information based on query params def get_context_data(self, **kwargs): """ Provide extra context data to template @@ -706,6 +732,8 @@ class PartQRCode(QRCodeView): ajax_form_title = _("Part QR Code") + permission_required = 'part.view_part' + def get_qr_data(self): """ Generate QR code data for the Part """ @@ -722,8 +750,11 @@ class PartImageUpload(AjaxUpdateView): model = Part ajax_template_name = 'modal_form.html' ajax_form_title = _('Upload Part Image') + form_class = part_forms.PartImageForm + permission_required = 'part.update_part' + def get_data(self): return { 'success': _('Updated part image'), @@ -737,6 +768,8 @@ class PartImageSelect(AjaxUpdateView): ajax_template_name = 'part/select_image.html' ajax_form_title = _('Select Part Image') + permission_required = 'part.update_part' + fields = [ 'image', ] @@ -778,6 +811,8 @@ class PartEdit(AjaxUpdateView): ajax_form_title = _('Edit Part Properties') context_object_name = 'part' + permission_required = 'part.update_part' + def get_form(self): """ Create form for Part editing. Overrides default get_form() method to limit the choices @@ -802,6 +837,8 @@ class BomValidate(AjaxUpdateView): context_object_name = 'part' form_class = part_forms.BomValidateForm + permission_required = ('part.update_part') + def get_context(self): return { 'part': self.get_object(), @@ -832,7 +869,7 @@ class BomValidate(AjaxUpdateView): return self.renderJsonResponse(request, form, data, context=self.get_context()) -class BomUpload(FormView): +class BomUpload(PermissionRequiredMixin, FormView): """ View for uploading a BOM file, and handling BOM data importing. The BOM upload process is as follows: @@ -868,6 +905,8 @@ class BomUpload(FormView): missing_columns = [] allowed_parts = [] + permission_required = ('part.update_part', 'part.create_bomitem') + def get_success_url(self): part = self.get_object() return reverse('upload-bom', kwargs={'pk': part.id}) @@ -1466,6 +1505,8 @@ class BomUpload(FormView): class PartExport(AjaxView): """ Export a CSV file containing information on multiple parts """ + permission_required = 'part.view_part' + def get_parts(self, request): """ Extract part list from the POST parameters. Parts can be supplied as: @@ -1543,6 +1584,8 @@ class BomDownload(AjaxView): - File format should be passed as a query param e.g. ?format=csv """ + permission_required = ('part.view_part', 'part.view_bomitem') + model = Part def get(self, request, *args, **kwargs): @@ -1596,6 +1639,8 @@ class BomExport(AjaxView): form_class = part_forms.BomExportForm ajax_form_title = _("Export Bill of Materials") + permission_required = ('part.view_part', 'part.view_bomitem') + def get(self, request, *args, **kwargs): return self.renderJsonResponse(request, self.form_class()) @@ -1645,6 +1690,8 @@ class PartDelete(AjaxDeleteView): ajax_form_title = _('Confirm Part Deletion') context_object_name = 'part' + permission_required = 'part.delete_part' + success_url = '/part/' def get_data(self): @@ -1661,6 +1708,8 @@ class PartPricing(AjaxView): ajax_form_title = _("Part Pricing") form_class = part_forms.PartPriceForm + permission_required = ('company.view_supplierpricebreak', 'part.view_part') + def get_part(self): try: return Part.objects.get(id=self.kwargs['pk']) @@ -1778,6 +1827,8 @@ class PartPricing(AjaxView): class PartParameterTemplateCreate(AjaxCreateView): """ View for creating a new PartParameterTemplate """ + permission_required = 'part.create_partparametertemplate' + model = PartParameterTemplate form_class = part_forms.EditPartParameterTemplateForm ajax_form_title = _('Create Part Parameter Template') @@ -1786,6 +1837,8 @@ class PartParameterTemplateCreate(AjaxCreateView): class PartParameterTemplateEdit(AjaxUpdateView): """ View for editing a PartParameterTemplate """ + permission_required = 'part.change_partparametertemplate' + model = PartParameterTemplate form_class = part_forms.EditPartParameterTemplateForm ajax_form_title = _('Edit Part Parameter Template') @@ -1794,6 +1847,8 @@ class PartParameterTemplateEdit(AjaxUpdateView): class PartParameterTemplateDelete(AjaxDeleteView): """ View for deleting an existing PartParameterTemplate """ + permission_required = 'part.delete_partparametertemplate' + model = PartParameterTemplate ajax_form_title = _("Delete Part Parameter Template") @@ -1801,6 +1856,8 @@ class PartParameterTemplateDelete(AjaxDeleteView): class PartParameterCreate(AjaxCreateView): """ View for creating a new PartParameter """ + permission_required = 'part.create_partparameter' + model = PartParameter form_class = part_forms.EditPartParameterForm ajax_form_title = _('Create Part Parameter') @@ -1851,6 +1908,8 @@ class PartParameterCreate(AjaxCreateView): class PartParameterEdit(AjaxUpdateView): """ View for editing a PartParameter """ + permission_required = 'part.change_partparameter' + model = PartParameter form_class = part_forms.EditPartParameterForm ajax_form_title = _('Edit Part Parameter') @@ -1865,12 +1924,14 @@ class PartParameterEdit(AjaxUpdateView): class PartParameterDelete(AjaxDeleteView): """ View for deleting a PartParameter """ + permission_required = 'part.delete_partparameter' + model = PartParameter ajax_template_name = 'part/param_delete.html' ajax_form_title = _('Delete Part Parameter') -class CategoryDetail(DetailView): +class CategoryDetail(PermissionRequiredMixin, DetailView): """ Detail view for PartCategory """ model = PartCategory @@ -1878,6 +1939,8 @@ class CategoryDetail(DetailView): queryset = PartCategory.objects.all().prefetch_related('children') template_name = 'part/category_partlist.html' + permission_required = 'part.view_partcategory' + def get_context_data(self, **kwargs): context = super(CategoryDetail, self).get_context_data(**kwargs).copy() @@ -1926,6 +1989,8 @@ class CategoryEdit(AjaxUpdateView): ajax_template_name = 'modal_form.html' ajax_form_title = _('Edit Part Category') + permission_required = 'part.change_partcategory' + def get_context_data(self, **kwargs): context = super(CategoryEdit, self).get_context_data(**kwargs).copy() @@ -1963,6 +2028,8 @@ class CategoryDelete(AjaxDeleteView): context_object_name = 'category' success_url = '/part/' + permission_required = 'part.delete_partcategory' + def get_data(self): return { 'danger': _('Part category was deleted'), @@ -1977,6 +2044,8 @@ class CategoryCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' form_class = part_forms.EditCategoryForm + permission_required = 'part.create_partcategory' + def get_context_data(self, **kwargs): """ Add extra context data to template. @@ -2012,12 +2081,14 @@ class CategoryCreate(AjaxCreateView): return initials -class BomItemDetail(DetailView): +class BomItemDetail(PermissionRequiredMixin, DetailView): """ Detail view for BomItem """ context_object_name = 'item' queryset = BomItem.objects.all() template_name = 'part/bom-detail.html' + permission_required = 'part.view_bomitem' + class BomItemCreate(AjaxCreateView): """ Create view for making a new BomItem object """ @@ -2026,6 +2097,8 @@ class BomItemCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' ajax_form_title = _('Create BOM item') + permission_required = 'part.create_bomitem' + def get_form(self): """ Override get_form() method to reduce Part selection options. @@ -2092,6 +2165,8 @@ class BomItemEdit(AjaxUpdateView): ajax_template_name = 'modal_form.html' ajax_form_title = _('Edit BOM item') + permission_required = 'part.change_bomitem' + def get_form(self): """ Override get_form() method to filter part selection options @@ -2140,6 +2215,8 @@ class BomItemDelete(AjaxDeleteView): context_object_name = 'item' ajax_form_title = _('Confim BOM item deletion') + permission_required = 'part.delete_bomitem' + class PartSalePriceBreakCreate(AjaxCreateView): """ View for creating a sale price break for a part """ @@ -2147,6 +2224,8 @@ class PartSalePriceBreakCreate(AjaxCreateView): model = PartSellPriceBreak form_class = part_forms.EditPartSalePriceBreakForm ajax_form_title = _('Add Price Break') + + permission_required = 'part.create_partsellpricebreak' def get_data(self): return { @@ -2197,6 +2276,8 @@ class PartSalePriceBreakEdit(AjaxUpdateView): form_class = part_forms.EditPartSalePriceBreakForm ajax_form_title = _('Edit Price Break') + permission_required = 'part.change_partsellpricebreak' + def get_form(self): form = super().get_form() @@ -2211,3 +2292,5 @@ class PartSalePriceBreakDelete(AjaxDeleteView): model = PartSellPriceBreak ajax_form_title = _("Delete Price Break") ajax_template_name = "modal_delete_form.html" + + permission_required = 'part.delete_partsalepricebreak' diff --git a/InvenTree/templates/403.html b/InvenTree/templates/403.html new file mode 100644 index 0000000000..372bd9fe27 --- /dev/null +++ b/InvenTree/templates/403.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% load i18n %} + +{% block page_title %} +InvenTree | {% trans "Permission Denied" %} +{% endblock %} + +{% block content %} + +
+

{% trans "Permission Denied" %}

+ +
+ {% trans "You do not have permission to view this page." %} +
+
+ +{% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/InvenTree/index.html b/InvenTree/templates/InvenTree/index.html index a9e4ae92ea..b20d61116d 100644 --- a/InvenTree/templates/InvenTree/index.html +++ b/InvenTree/templates/InvenTree/index.html @@ -1,7 +1,7 @@ {% extends "base.html" %} - +{% load i18n %} {% block page_title %} -InvenTree | Index +InvenTree | {% trans "Index" %} {% endblock %} {% block content %} From 66bdce3d04d1a08be9b87e7016ce6100b8dbf4b2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 5 Oct 2020 23:53:24 +1100 Subject: [PATCH 02/26] Hide elements on the PartCategory page, based on permissions --- InvenTree/part/templates/part/category.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index 9a6fb9d7e5..44c8cd7df9 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -20,17 +20,23 @@ {% endif %}

+ {% if perms.part.create_partcategory %} + {% endif %} {% if category %} + {% if perms.part.change_partcategory %} + {% endif %} + {% if perms.part.delete_partcategory %} {% endif %} + {% endif %}

@@ -104,11 +110,15 @@
+ {% if perms.part.create_part %} + {% endif %}
@@ -180,6 +190,7 @@ location.href = url; }); + {% if perms.part.create_part %} $("#part-create").click(function() { launchModalForm( "{% url 'part-create' %}", @@ -207,6 +218,7 @@ } ); }); + {% endif %} {% if category %} $("#cat-edit").click(function () { From afadd51a1437e15cad23ff51a45bb0cb76c7bb41 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 00:19:44 +1100 Subject: [PATCH 03/26] Fix permissions in views.py Silly, "add" not "create" --- InvenTree/part/views.py | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 84628eff04..47a77078be 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -78,7 +78,7 @@ class PartAttachmentCreate(AjaxCreateView): ajax_form_title = _("Add part attachment") ajax_template_name = "modal_form.html" - permission_required = 'part.create_partattachment' + permission_required = 'part.add_partattachment' def post_save(self): """ Record the user that uploaded the attachment """ @@ -166,7 +166,7 @@ class PartTestTemplateCreate(AjaxCreateView): form_class = part_forms.EditPartTestTemplateForm ajax_form_title = _("Create Test Template") - permission_required = 'part.create_parttesttemplate' + permission_required = 'part.add_parttesttemplate' def get_initial(self): @@ -306,7 +306,7 @@ class MakePartVariant(AjaxCreateView): ajax_form_title = _('Create Variant') ajax_template_name = 'part/variant_part.html' - permission_required = 'part.create_part' + permission_required = 'part.add_part' def get_part_template(self): return get_object_or_404(Part, id=self.kwargs['pk']) @@ -386,7 +386,7 @@ class PartDuplicate(AjaxCreateView): ajax_form_title = _("Duplicate Part") ajax_template_name = "part/copy_part.html" - permission_required = 'part.create_part' + permission_required = 'part.add_part' def get_data(self): return { @@ -511,7 +511,7 @@ class PartCreate(AjaxCreateView): ajax_form_title = _('Create new part') ajax_template_name = 'part/create_part.html' - permission_required = 'part.create_part' + permission_required = 'part.add_part' def get_data(self): return { @@ -905,7 +905,7 @@ class BomUpload(PermissionRequiredMixin, FormView): missing_columns = [] allowed_parts = [] - permission_required = ('part.update_part', 'part.create_bomitem') + permission_required = ('part.update_part', 'part.add_bomitem') def get_success_url(self): part = self.get_object() @@ -1827,7 +1827,7 @@ class PartPricing(AjaxView): class PartParameterTemplateCreate(AjaxCreateView): """ View for creating a new PartParameterTemplate """ - permission_required = 'part.create_partparametertemplate' + permission_required = 'part.add_partparametertemplate' model = PartParameterTemplate form_class = part_forms.EditPartParameterTemplateForm @@ -1856,7 +1856,7 @@ class PartParameterTemplateDelete(AjaxDeleteView): class PartParameterCreate(AjaxCreateView): """ View for creating a new PartParameter """ - permission_required = 'part.create_partparameter' + permission_required = 'part.add_partparameter' model = PartParameter form_class = part_forms.EditPartParameterForm @@ -2044,7 +2044,7 @@ class CategoryCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' form_class = part_forms.EditCategoryForm - permission_required = 'part.create_partcategory' + permission_required = 'part.add_partcategory' def get_context_data(self, **kwargs): """ Add extra context data to template. @@ -2097,7 +2097,7 @@ class BomItemCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' ajax_form_title = _('Create BOM item') - permission_required = 'part.create_bomitem' + permission_required = 'part.add_bomitem' def get_form(self): """ Override get_form() method to reduce Part selection options. @@ -2225,7 +2225,7 @@ class PartSalePriceBreakCreate(AjaxCreateView): form_class = part_forms.EditPartSalePriceBreakForm ajax_form_title = _('Add Price Break') - permission_required = 'part.create_partsellpricebreak' + permission_required = 'part.add_partsellpricebreak' def get_data(self): return { From ba4c829b10137ca1a37cf7d1dde0b73ce8d0cb57 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 00:20:45 +1100 Subject: [PATCH 04/26] Add permission requirements in various part templates --- InvenTree/part/templates/part/bom.html | 2 ++ InvenTree/part/templates/part/build.html | 7 +++++-- InvenTree/part/templates/part/category.html | 6 +++--- InvenTree/part/templates/part/notes.html | 2 ++ InvenTree/part/templates/part/params.html | 8 +++++++ InvenTree/part/templates/part/part_base.html | 22 ++++++++++++++++---- InvenTree/part/templates/part/tabs.html | 9 +++++--- InvenTree/templates/slide.html | 2 +- InvenTree/templates/stock_table.html | 8 +++++++ 9 files changed, 53 insertions(+), 13 deletions(-) diff --git a/InvenTree/part/templates/part/bom.html b/InvenTree/part/templates/part/bom.html index 2653a42575..610e60847a 100644 --- a/InvenTree/part/templates/part/bom.html +++ b/InvenTree/part/templates/part/bom.html @@ -39,10 +39,12 @@ {% elif part.active %} + {% if perms.part.change_part %} {% if part.is_bom_valid == False %} {% endif %} + {% endif %} {% endif %}
diff --git a/InvenTree/part/templates/part/build.html b/InvenTree/part/templates/part/build.html index 442693ddeb..ad51d33ab2 100644 --- a/InvenTree/part/templates/part/build.html +++ b/InvenTree/part/templates/part/build.html @@ -1,15 +1,18 @@ {% extends "part/part_base.html" %} {% load static %} +{% load i18n %} {% block details %} {% include 'part/tabs.html' with tab='build' %} -

Part Builds

+

{% trans "Part Builds" %}

{% if part.active %} - + {% if perms.build.add_build %} + + {% endif %} {% endif %}
diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index 44c8cd7df9..25417a1d9b 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -20,7 +20,7 @@ {% endif %}

- {% if perms.part.create_partcategory %} + {% if perms.part.add_partcategory %} @@ -110,7 +110,7 @@
- {% if perms.part.create_part %} + {% if perms.part.add_part %} {% endif %}
@@ -190,7 +190,7 @@ location.href = url; }); - {% if perms.part.create_part %} + {% if perms.part.add_part %} $("#part-create").click(function() { launchModalForm( "{% url 'part-create' %}", diff --git a/InvenTree/part/templates/part/notes.html b/InvenTree/part/templates/part/notes.html index afa215600d..68711c881e 100644 --- a/InvenTree/part/templates/part/notes.html +++ b/InvenTree/part/templates/part/notes.html @@ -29,7 +29,9 @@

{% trans "Part Notes" %}

+ {% if perms.part.change_part %} + {% endif %}

diff --git a/InvenTree/part/templates/part/params.html b/InvenTree/part/templates/part/params.html index 8ad4f61252..9984830242 100644 --- a/InvenTree/part/templates/part/params.html +++ b/InvenTree/part/templates/part/params.html @@ -10,7 +10,9 @@
+ {% if perms.part.add_partparameter %} + {% endif %}
@@ -30,8 +32,12 @@ {{ param.template.units }}
+ {% if perms.part.change_partparameter %} + {% endif %} + {% if perms.part.delete_partparameter %} + {% endif %}
@@ -48,6 +54,7 @@ $('#param-table').inventreeTable({ }); + {% if perms.part.add_partparameter %} $('#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); diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 1de4976740..6103cb5369 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -56,26 +56,36 @@ - + {% endif %} {% if part.purchaseable %} - {% endif %} {% endif %} + {% endif %} + {% if perms.part.add_part or perms.part.change_part or perms.part.delete_part %}
+ {% endif %}
@@ -274,6 +284,7 @@ }); }); + {% if perms.part.change_part %} $("#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 perms.part.add_part %} $("#part-duplicate").click(function() { launchModalForm( "{% url 'part-duplicate' part.id %}", @@ -300,8 +313,9 @@ } ); }); + {% endif %} - {% if not part.active %} + {% if not part.active and perms.part.delete_part %} $("#part-delete").click(function() { launchModalForm( "{% url 'part-delete' part.id %}", diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index 1eab299ed5..02d8672979 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -26,14 +26,17 @@ {% if part.assembly %} {% trans "BOM" %}{{ part.bom_count }} + {% if perms.build.view_build %} - {% trans "Build Orders" %}{{ part.builds.count }} + {% trans "Build Orders" %}{{ part.builds.count }} + + {% endif %} {% endif %} {% if part.component or part.used_in_count > 0 %} {% trans "Used In" %} {% if part.used_in_count > 0 %}{{ part.used_in_count }}{% endif %} {% endif %} - {% if part.purchaseable %} + {% if part.purchaseable and perms.order.view_purchaseorder %} {% if part.is_template == False %} {% trans "Suppliers" %} @@ -45,7 +48,7 @@ {% trans "Purchase Orders" %} {{ part.purchase_orders|length }} {% endif %} - {% if part.salable %} + {% if part.salable and perms.order.view_salesorder %}
  • {% trans "Sale Price" %}
  • diff --git a/InvenTree/templates/slide.html b/InvenTree/templates/slide.html index e2fe2e932f..45535786c9 100644 --- a/InvenTree/templates/slide.html +++ b/InvenTree/templates/slide.html @@ -1,3 +1,3 @@
    - +
    \ No newline at end of file diff --git a/InvenTree/templates/stock_table.html b/InvenTree/templates/stock_table.html index c41d2181c9..be26b1a976 100644 --- a/InvenTree/templates/stock_table.html +++ b/InvenTree/templates/stock_table.html @@ -6,19 +6,27 @@ {% if read_only %} {% else %} + {% if perms.stock.add_stockitem %} + {% endif %} + {% if perms.stock.change_stockitem or perms.stock.delete_stockitem %}
    {% endif %} + {% endif %}
    From 8ee16d6f98600e4a618254f1dc0a79ab7113c2ed Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 00:20:57 +1100 Subject: [PATCH 05/26] update translation files --- InvenTree/locale/de/LC_MESSAGES/django.po | 405 ++++++++++++++-------- InvenTree/locale/en/LC_MESSAGES/django.po | 397 ++++++++++++--------- InvenTree/locale/es/LC_MESSAGES/django.po | 397 ++++++++++++--------- 3 files changed, 731 insertions(+), 468 deletions(-) diff --git a/InvenTree/locale/de/LC_MESSAGES/django.po b/InvenTree/locale/de/LC_MESSAGES/django.po index 493d4a8b06..7632fadc4c 100644 --- a/InvenTree/locale/de/LC_MESSAGES/django.po +++ b/InvenTree/locale/de/LC_MESSAGES/django.po @@ -6,7 +6,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-10-04 14:02+0000\n" +"POT-Creation-Date: 2020-10-05 13:20+0000\n" "PO-Revision-Date: 2020-05-03 11:32+0200\n" "Last-Translator: Christian Schlüter \n" "Language-Team: C \n" @@ -96,7 +96,7 @@ msgstr "Datei-Kommentar" msgid "User" msgstr "Benutzer" -#: 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 "Name" @@ -107,19 +107,19 @@ msgstr "Name" msgid "Description (optional)" msgstr "Firmenbeschreibung" -#: InvenTree/settings.py:342 +#: InvenTree/settings.py:343 msgid "English" msgstr "Englisch" -#: InvenTree/settings.py:343 +#: InvenTree/settings.py:344 msgid "German" msgstr "Deutsch" -#: InvenTree/settings.py:344 +#: InvenTree/settings.py:345 msgid "French" msgstr "Französisch" -#: InvenTree/settings.py:345 +#: InvenTree/settings.py:346 msgid "Polish" msgstr "Polnisch" @@ -345,7 +345,7 @@ msgstr "Bau-Anzahl" msgid "Number of parts to build" msgstr "Anzahl der zu bauenden Teile" -#: 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 "Bau-Status" @@ -364,7 +364,7 @@ msgstr "Chargennummer für diese Bau-Ausgabe" #: 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 "Externer Link" @@ -376,7 +376,7 @@ msgstr "Link zu einer externen URL" #: 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 @@ -425,7 +425,7 @@ msgstr "Lagerobjekt-Anzahl dem Bau zuweisen" #: 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 "Teile bestellen" @@ -441,7 +441,7 @@ msgstr "Automatisches Zuweisen" msgid "Unallocate" msgstr "Zuweisung aufheben" -#: 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 "Neues Lagerobjekt" @@ -574,7 +574,7 @@ msgstr "Lagerobjekt wurde zugewiesen" #: 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 "Bau" @@ -717,7 +717,7 @@ msgstr "Fertig" #: 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 "Bauaufträge" @@ -739,7 +739,7 @@ msgstr "Speichern" #: 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 "Bermerkungen bearbeiten" @@ -1085,13 +1085,13 @@ msgid "New Supplier Part" msgstr "Neues Zulieferer-Teil" #: 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 "Optionen" #: company/templates/company/detail_part.html:18 -#: part/templates/part/category.html:112 +#: part/templates/part/category.html:122 #, fuzzy #| msgid "Order part" msgid "Order parts" @@ -1108,7 +1108,7 @@ msgid "Delete Parts" msgstr "Teile löschen" #: 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 "Neues Teil" @@ -1140,7 +1140,7 @@ msgstr "Zuliefererbestand" #: 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 "Exportieren" @@ -1163,8 +1163,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 "Bestellungen" @@ -1182,8 +1182,8 @@ msgstr "Neue Bestellung" #: 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 "Bestellungen" @@ -1204,7 +1204,7 @@ msgid "Supplier Part" msgstr "Zulieferer-Teil" #: 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 "Teil bestellen" @@ -1251,7 +1251,7 @@ msgid "Pricing Information" msgstr "Preisinformationen ansehen" #: 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 "Preisstaffel hinzufügen" @@ -1293,7 +1293,7 @@ msgstr "Bepreisung" #: 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 "Lagerbestand" @@ -1303,22 +1303,22 @@ msgstr "Bestellungen" #: 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 "Teile" -#: 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 "Zulieferer" -#: company/views.py:57 templates/navbar.html:25 +#: company/views.py:57 templates/navbar.html:32 msgid "Manufacturers" msgstr "Hersteller" -#: company/views.py:64 templates/navbar.html:32 +#: company/views.py:64 templates/navbar.html:41 msgid "Customers" msgstr "Kunden" @@ -1382,17 +1382,17 @@ msgstr "Neues Zuliefererteil anlegen" msgid "Delete Supplier Part" msgstr "Zuliefererteil entfernen" -#: company/views.py:404 part/views.py:2153 +#: company/views.py:404 part/views.py:2232 #, fuzzy #| msgid "Add Price Break" msgid "Added new price break" msgstr "Preisstaffel hinzufügen" -#: company/views.py:441 part/views.py:2198 +#: company/views.py:441 part/views.py:2277 msgid "Edit Price Break" msgstr "Preisstaffel bearbeiten" -#: company/views.py:456 part/views.py:2212 +#: company/views.py:456 part/views.py:2293 msgid "Delete Price Break" msgstr "Preisstaffel löschen" @@ -1497,7 +1497,7 @@ msgstr "" msgid "Date order was completed" msgstr "Bestellung als vollständig markieren" -#: 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 "Anzahl muss größer Null sein" @@ -1667,7 +1667,7 @@ msgid "Purchase Order Attachments" msgstr "Bestellanhänge" #: 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 "Anhänge" @@ -1683,7 +1683,7 @@ msgstr "Bestellpositionen" #: 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 "Neuer Standort" @@ -1725,7 +1725,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 "bestellt" @@ -1823,7 +1823,7 @@ msgstr "Bestellungspositionen" msgid "Add Purchase Order Attachment" msgstr "Bestellanhang hinzufügen" -#: 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 "Anhang hinzugefügt" @@ -1963,12 +1963,12 @@ msgstr "Zuordnung bearbeiten" msgid "Remove allocation" msgstr "Zuordnung entfernen" -#: 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 "Standard-Lagerort" -#: 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 "Verfügbarer Lagerbestand" @@ -2100,7 +2100,7 @@ msgid "Part Category" msgstr "Teilkategorie" #: 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 "Teile-Kategorien" @@ -2339,7 +2339,7 @@ msgstr "Notizen zum Stücklisten-Objekt" msgid "BOM line checksum" msgstr "Prüfsumme der Stückliste" -#: 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 #, fuzzy #| msgid "Overage must be an integer value or a percentage" @@ -2402,25 +2402,25 @@ msgstr "Neue Stücklistenposition" msgid "Finish Editing" msgstr "Bearbeitung beenden" -#: part/templates/part/bom.html:42 +#: part/templates/part/bom.html:43 msgid "Edit BOM" msgstr "Stückliste bearbeiten" -#: part/templates/part/bom.html:44 +#: part/templates/part/bom.html:45 msgid "Validate Bill of Materials" msgstr "Stückliste validieren" -#: 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 "Stückliste exportieren" -#: part/templates/part/bom.html:101 +#: part/templates/part/bom.html:103 #, fuzzy #| msgid "Remove selected BOM items" msgid "Delete selected BOM items?" msgstr "Ausgewählte Stücklistenpositionen entfernen" -#: part/templates/part/bom.html:102 +#: part/templates/part/bom.html:104 #, fuzzy #| msgid "Remove selected BOM items" msgid "All selected BOM items will be deleted" @@ -2510,101 +2510,113 @@ msgstr "Neues Bild hochladen" msgid "Each part must already exist in the database" msgstr "" +#: part/templates/part/build.html:8 +#, fuzzy +#| msgid "Parent Build" +msgid "Part Builds" +msgstr "Eltern-Bau" + +#: part/templates/part/build.html:14 +#, fuzzy +#| msgid "Start new Build" +msgid "Start New Build" +msgstr "Neuen Bau beginnen" + #: part/templates/part/category.html:19 msgid "All parts" msgstr "Alle Teile" -#: 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 "Teilkategorie anlegen" -#: part/templates/part/category.html:27 +#: part/templates/part/category.html:30 #, fuzzy #| msgid "Edit Part Category" msgid "Edit part category" msgstr "Teilkategorie bearbeiten" -#: part/templates/part/category.html:30 +#: part/templates/part/category.html:35 #, fuzzy #| msgid "Select part category" msgid "Delete part category" msgstr "Teilekategorie wählen" -#: 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 "Kategorie-Details" -#: part/templates/part/category.html:44 +#: part/templates/part/category.html:50 msgid "Category Path" msgstr "Pfad zur Kategorie" -#: part/templates/part/category.html:49 +#: part/templates/part/category.html:55 msgid "Category Description" msgstr "Kategorie-Beschreibung" -#: 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 "Schlüsselwörter" -#: part/templates/part/category.html:68 +#: part/templates/part/category.html:74 msgid "Subcategories" msgstr "Unter-Kategorien" -#: part/templates/part/category.html:73 +#: part/templates/part/category.html:79 msgid "Parts (Including subcategories)" msgstr "Teile (inklusive Unter-Kategorien)" -#: 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 "Neues Teil anlegen" -#: part/templates/part/category.html:111 +#: part/templates/part/category.html:120 #, fuzzy #| msgid "Part category" msgid "Set category" msgstr "Teile-Kategorie" -#: part/templates/part/category.html:111 +#: part/templates/part/category.html:120 #, fuzzy #| msgid "Set Part Category" msgid "Set Category" msgstr "Teilkategorie auswählen" -#: part/templates/part/category.html:113 +#: part/templates/part/category.html:123 #, fuzzy #| msgid "Export" msgid "Export Data" msgstr "Exportieren" -#: part/templates/part/category.html:162 +#: part/templates/part/category.html:172 #, fuzzy #| msgid "Create New Location" msgid "Create new location" msgstr "Neuen Standort anlegen" -#: part/templates/part/category.html:167 part/templates/part/category.html:196 +#: part/templates/part/category.html:177 part/templates/part/category.html:207 #, fuzzy #| msgid "Category" msgid "New Category" msgstr "Kategorie" -#: part/templates/part/category.html:168 +#: part/templates/part/category.html:178 #, fuzzy #| msgid "Create new part category" msgid "Create new category" msgstr "Teilkategorie anlegen" -#: part/templates/part/category.html:197 +#: part/templates/part/category.html:208 #, fuzzy #| msgid "Create new part category" msgid "Create new Part Category" msgstr "Teilkategorie anlegen" -#: 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 "Neuen Lager-Standort erstellen" @@ -2618,7 +2630,7 @@ msgstr "Parameter Wert" msgid "Part Details" msgstr "Teile-Details" -#: 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 "IPN (Interne Produktnummer)" @@ -2652,7 +2664,7 @@ msgstr "Kategorie" msgid "Default Supplier" msgstr "Standard-Zulieferer" -#: 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 "Einheiten" @@ -2785,24 +2797,25 @@ msgstr "Teil bestellen" msgid "Part Parameters" msgstr "Teilparameter" -#: part/templates/part/params.html:13 +#: part/templates/part/params.html:14 msgid "Add new parameter" msgstr "Parameter hinzufügen" -#: 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 "Neuer Parameter" -#: 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 "Wert" -#: part/templates/part/params.html:33 +#: part/templates/part/params.html:36 msgid "Edit" msgstr "Bearbeiten" -#: 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 "Löschen" @@ -2859,47 +2872,53 @@ msgstr "" msgid "Show pricing information" msgstr "Kosteninformationen ansehen" -#: part/templates/part/part_base.html:70 +#: part/templates/part/part_base.html:60 +#, fuzzy +#| msgid "Count stock" +msgid "Count part stock" +msgstr "Bestand zählen" + +#: part/templates/part/part_base.html:75 #, fuzzy #| msgid "Source Location" msgid "Part actions" msgstr "Quell-Standort" -#: part/templates/part/part_base.html:72 +#: part/templates/part/part_base.html:78 #, fuzzy #| msgid "Duplicate Part" msgid "Duplicate part" msgstr "Teil duplizieren" -#: part/templates/part/part_base.html:73 +#: part/templates/part/part_base.html:81 #, fuzzy #| msgid "Edit Template" msgid "Edit part" msgstr "Vorlage bearbeiten" -#: part/templates/part/part_base.html:75 +#: part/templates/part/part_base.html:84 #, fuzzy #| msgid "Delete Parts" msgid "Delete part" msgstr "Teile löschen" -#: 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 "Auf Lager" -#: part/templates/part/part_base.html:121 +#: part/templates/part/part_base.html:131 msgid "Allocated to Build Orders" msgstr "Zu Bauaufträgen zugeordnet" -#: part/templates/part/part_base.html:128 +#: part/templates/part/part_base.html:138 msgid "Allocated to Sales Orders" msgstr "Zu Aufträgen zugeordnet" -#: part/templates/part/part_base.html:150 +#: part/templates/part/part_base.html:160 msgid "Can Build" msgstr "Herstellbar?" -#: part/templates/part/part_base.html:156 +#: part/templates/part/part_base.html:166 msgid "Underway" msgstr "unterwegs" @@ -2923,7 +2942,7 @@ msgstr "Aus vorhandenen Bildern auswählen" msgid "Upload new image" msgstr "Neues Bild hochladen" -#: 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 #, fuzzy #| msgid "Price" msgid "Sale Price" @@ -2994,11 +3013,11 @@ msgstr "Varianten" msgid "BOM" msgstr "Stückliste" -#: part/templates/part/tabs.html:34 +#: part/templates/part/tabs.html:37 msgid "Used In" msgstr "Benutzt in" -#: 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 "" @@ -3032,184 +3051,184 @@ msgstr "Neues Teil hinzufügen" msgid "New Variant" msgstr "Varianten" -#: part/views.py:76 +#: part/views.py:78 msgid "Add part attachment" msgstr "Teilanhang hinzufügen" -#: part/views.py:125 templates/attachment_table.html:30 +#: part/views.py:129 templates/attachment_table.html:30 msgid "Edit attachment" msgstr "Anhang bearbeiten" -#: part/views.py:129 +#: part/views.py:135 msgid "Part attachment updated" msgstr "Teilanhang aktualisiert" -#: part/views.py:144 +#: part/views.py:150 msgid "Delete Part Attachment" msgstr "Teilanhang löschen" -#: part/views.py:150 +#: part/views.py:158 msgid "Deleted part attachment" msgstr "Teilanhang gelöscht" -#: part/views.py:159 +#: part/views.py:167 #, fuzzy #| msgid "Create Part Parameter Template" msgid "Create Test Template" msgstr "Teilparametervorlage anlegen" -#: part/views.py:186 +#: part/views.py:196 #, fuzzy #| msgid "Edit Template" msgid "Edit Test Template" msgstr "Vorlage bearbeiten" -#: part/views.py:200 +#: part/views.py:212 #, fuzzy #| msgid "Delete Template" msgid "Delete Test Template" msgstr "Vorlage löschen" -#: part/views.py:207 +#: part/views.py:221 msgid "Set Part Category" msgstr "Teilkategorie auswählen" -#: part/views.py:255 +#: part/views.py:271 #, python-brace-format msgid "Set category for {n} parts" msgstr "Kategorie für {n} Teile setzen" -#: part/views.py:290 +#: part/views.py:306 msgid "Create Variant" msgstr "Variante anlegen" -#: part/views.py:368 +#: part/views.py:386 msgid "Duplicate Part" msgstr "Teil duplizieren" -#: part/views.py:373 +#: part/views.py:393 msgid "Copied part" msgstr "Teil kopiert" -#: part/views.py:496 +#: part/views.py:518 msgid "Created new part" msgstr "Neues Teil angelegt" -#: part/views.py:707 +#: part/views.py:733 msgid "Part QR Code" msgstr "Teil-QR-Code" -#: part/views.py:724 +#: part/views.py:752 msgid "Upload Part Image" msgstr "Teilbild hochladen" -#: part/views.py:729 part/views.py:764 +#: part/views.py:760 part/views.py:797 msgid "Updated part image" msgstr "Teilbild aktualisiert" -#: part/views.py:738 +#: part/views.py:769 msgid "Select Part Image" msgstr "Teilbild auswählen" -#: part/views.py:767 +#: part/views.py:800 msgid "Part image not found" msgstr "Teilbild nicht gefunden" -#: part/views.py:778 +#: part/views.py:811 msgid "Edit Part Properties" msgstr "Teileigenschaften bearbeiten" -#: part/views.py:800 +#: part/views.py:835 msgid "Validate BOM" msgstr "BOM validieren" -#: part/views.py:963 +#: part/views.py:1002 msgid "No BOM file provided" msgstr "Keine Stückliste angegeben" -#: part/views.py:1313 +#: part/views.py:1352 msgid "Enter a valid quantity" msgstr "Bitte eine gültige Anzahl eingeben" -#: part/views.py:1338 part/views.py:1341 +#: part/views.py:1377 part/views.py:1380 msgid "Select valid part" msgstr "Bitte ein gültiges Teil auswählen" -#: part/views.py:1347 +#: part/views.py:1386 msgid "Duplicate part selected" msgstr "Teil doppelt ausgewählt" -#: part/views.py:1385 +#: part/views.py:1424 msgid "Select a part" msgstr "Teil auswählen" -#: part/views.py:1391 +#: part/views.py:1430 #, fuzzy #| msgid "Select part to be used in BOM" msgid "Selected part creates a circular BOM" msgstr "Teil für die Nutzung in der Stückliste auswählen" -#: part/views.py:1395 +#: part/views.py:1434 msgid "Specify quantity" msgstr "Anzahl angeben" -#: part/views.py:1645 +#: part/views.py:1690 msgid "Confirm Part Deletion" msgstr "Löschen des Teils bestätigen" -#: part/views.py:1652 +#: part/views.py:1699 msgid "Part was deleted" msgstr "Teil wurde gelöscht" -#: part/views.py:1661 +#: part/views.py:1708 msgid "Part Pricing" msgstr "Teilbepreisung" -#: part/views.py:1783 +#: part/views.py:1834 msgid "Create Part Parameter Template" msgstr "Teilparametervorlage anlegen" -#: part/views.py:1791 +#: part/views.py:1844 msgid "Edit Part Parameter Template" msgstr "Teilparametervorlage bearbeiten" -#: part/views.py:1798 +#: part/views.py:1853 msgid "Delete Part Parameter Template" msgstr "Teilparametervorlage löschen" -#: part/views.py:1806 +#: part/views.py:1863 msgid "Create Part Parameter" msgstr "Teilparameter anlegen" -#: part/views.py:1856 +#: part/views.py:1915 msgid "Edit Part Parameter" msgstr "Teilparameter bearbeiten" -#: part/views.py:1870 +#: part/views.py:1931 msgid "Delete Part Parameter" msgstr "Teilparameter löschen" -#: part/views.py:1927 +#: part/views.py:1990 msgid "Edit Part Category" msgstr "Teilkategorie bearbeiten" -#: part/views.py:1962 +#: part/views.py:2027 msgid "Delete Part Category" msgstr "Teilkategorie löschen" -#: part/views.py:1968 +#: part/views.py:2035 msgid "Part category was deleted" msgstr "Teilekategorie wurde gelöscht" -#: part/views.py:2027 +#: part/views.py:2098 msgid "Create BOM item" msgstr "BOM-Position anlegen" -#: part/views.py:2093 +#: part/views.py:2166 msgid "Edit BOM item" msgstr "BOM-Position beaarbeiten" -#: part/views.py:2141 +#: part/views.py:2216 msgid "Confim BOM item deletion" msgstr "Löschung von BOM-Position bestätigen" @@ -3653,15 +3672,15 @@ msgid "Stock adjustment actions" msgstr "Bestands-Anpassung bestätigen" #: 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 "Bestand zählen" -#: 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 "Bestand hinzufügen" -#: 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 "Bestand entfernen" @@ -4191,6 +4210,14 @@ msgstr "Lagerbestands-Tracking-Eintrag bearbeiten" msgid "Add Stock Tracking Entry" msgstr "Lagerbestands-Tracking-Eintrag hinzufügen" +#: 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 "" @@ -4201,6 +4228,10 @@ msgstr "" msgid "Pending Builds" msgstr "Eltern-Bau" +#: templates/InvenTree/index.html:4 +msgid "Index" +msgstr "" + #: templates/InvenTree/latest_parts.html:7 #, fuzzy #| msgid "Parent Part" @@ -4884,39 +4915,39 @@ msgstr "Favorit" msgid "Purchasable" msgstr "Käuflich" -#: templates/navbar.html:22 +#: templates/navbar.html:29 msgid "Buy" msgstr "Kaufen" -#: templates/navbar.html:30 +#: templates/navbar.html:39 msgid "Sell" msgstr "Verkaufen" -#: 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 "Admin" -#: templates/navbar.html:52 +#: templates/navbar.html:62 msgid "Settings" msgstr "Einstellungen" -#: templates/navbar.html:53 +#: templates/navbar.html:63 msgid "Logout" msgstr "Ausloggen" -#: templates/navbar.html:55 +#: templates/navbar.html:65 msgid "Login" msgstr "Einloggen" -#: templates/navbar.html:58 +#: templates/navbar.html:68 msgid "About InvenTree" msgstr "Über InvenBaum" -#: templates/navbar.html:59 +#: templates/navbar.html:69 msgid "Statistics" msgstr "Statistiken" @@ -4930,54 +4961,124 @@ msgstr "Suche" msgid "Export Stock Information" msgstr "Lagerobjekt-Standort bearbeiten" -#: templates/stock_table.html:13 +#: templates/stock_table.html:17 #, fuzzy #| msgid "Added stock to {n} items" msgid "Add to selected stock items" msgstr "Vorrat zu {n} Lagerobjekten hinzugefügt" -#: templates/stock_table.html:14 +#: templates/stock_table.html:18 #, fuzzy #| msgid "Remove selected BOM items" msgid "Remove from selected stock items" msgstr "Ausgewählte Stücklistenpositionen entfernen" -#: templates/stock_table.html:15 +#: templates/stock_table.html:19 #, fuzzy #| msgid "Delete Stock Item" msgid "Stocktake selected stock items" msgstr "Lagerobjekt löschen" -#: templates/stock_table.html:16 +#: templates/stock_table.html:20 #, fuzzy #| msgid "Delete Stock Item" msgid "Move selected stock items" msgstr "Lagerobjekt löschen" -#: templates/stock_table.html:16 +#: templates/stock_table.html:20 msgid "Move stock" msgstr "Bestand bewegen" -#: templates/stock_table.html:17 +#: templates/stock_table.html:21 #, fuzzy #| msgid "Remove selected BOM items" msgid "Order selected items" msgstr "Ausgewählte Stücklistenpositionen entfernen" -#: templates/stock_table.html:17 +#: templates/stock_table.html:21 msgid "Order stock" msgstr "Bestand bestellen" -#: templates/stock_table.html:18 +#: templates/stock_table.html:24 #, fuzzy #| msgid "Delete line item" msgid "Delete selected items" msgstr "Position löschen" -#: templates/stock_table.html:18 +#: templates/stock_table.html:24 msgid "Delete Stock" msgstr "Bestand löschen" +#: users/admin.py:62 +#, fuzzy +#| msgid "User" +msgid "Users" +msgstr "Benutzer" + +#: users/admin.py:63 +msgid "Select which users are assigned to this group" +msgstr "" + +#: users/admin.py:124 +#, fuzzy +#| msgid "External Link" +msgid "Personal info" +msgstr "Externer Link" + +#: users/admin.py:125 +#, fuzzy +#| msgid "Revision" +msgid "Permissions" +msgstr "Revision" + +#: users/admin.py:128 +#, fuzzy +#| msgid "Import BOM data" +msgid "Important dates" +msgstr "Stückliste importieren" + +#: 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 +#, fuzzy +#| msgid "Created" +msgid "Create" +msgstr "Erstellt" + +#: users/models.py:137 +msgid "Permission to add items" +msgstr "" + +#: users/models.py:139 +#, fuzzy +#| msgid "Last Updated" +msgid "Update" +msgstr "Zuletzt aktualisiert" + +#: users/models.py:139 +msgid "Permissions to edit items" +msgstr "" + +#: users/models.py:141 +#, fuzzy +#| msgid "Remove selected BOM items" +msgid "Permission to delete items" +msgstr "Ausgewählte Stücklistenpositionen entfernen" + #~ msgid "Belongs To" #~ msgstr "Gehört zu" diff --git a/InvenTree/locale/en/LC_MESSAGES/django.po b/InvenTree/locale/en/LC_MESSAGES/django.po index 0b5425db6e..0f38022f92 100644 --- a/InvenTree/locale/en/LC_MESSAGES/django.po +++ b/InvenTree/locale/en/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-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 \n" "Language-Team: LANGUAGE \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 "" diff --git a/InvenTree/locale/es/LC_MESSAGES/django.po b/InvenTree/locale/es/LC_MESSAGES/django.po index 0b5425db6e..0f38022f92 100644 --- a/InvenTree/locale/es/LC_MESSAGES/django.po +++ b/InvenTree/locale/es/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-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 \n" "Language-Team: LANGUAGE \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 "" From 16d720b62c08b5e22289d019f4ea27d949e26bca Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 00:36:55 +1100 Subject: [PATCH 06/26] Update permission requirements for API - Automatically use model permissions by default! - --- InvenTree/InvenTree/settings.py | 4 ++++ InvenTree/build/api.py | 14 +----------- InvenTree/company/api.py | 19 +---------------- InvenTree/order/api.py | 28 +----------------------- InvenTree/part/api.py | 38 --------------------------------- InvenTree/stock/api.py | 15 ------------- 6 files changed, 7 insertions(+), 111 deletions(-) diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 21b8a0ead1..75f6f58993 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -231,6 +231,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' } diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py index c0faee6c15..d4e458c506 100644 --- a/InvenTree/build/api.py +++ b/InvenTree/build/api.py @@ -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, ] diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index 0e54d7d7fb..548ac96016 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -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, ] diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index a7915878c5..4a9dbfa2ac 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -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): """ diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 6834503466..d4aeec5bd9 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -55,10 +55,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 +115,6 @@ class PartSalePriceList(generics.ListCreateAPIView): queryset = PartSellPriceBreak.objects.all() serializer_class = part_serializers.PartSalePriceSerializer - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend ] @@ -182,8 +174,6 @@ class PartTestTemplateList(generics.ListCreateAPIView): return queryset - permission_classes = [permissions.IsAuthenticated] - filter_backends = [ DjangoFilterBackend, filters.OrderingFilter, @@ -221,10 +211,6 @@ class PartThumbsUpdate(generics.RetrieveUpdateAPIView): queryset = Part.objects.all() serializer_class = part_serializers.PartThumbSerializerUpdate - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend ] @@ -246,10 +232,6 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView): return queryset - permission_classes = [ - permissions.IsAuthenticated, - ] - def get_serializer(self, *args, **kwargs): try: @@ -580,10 +562,6 @@ class PartList(generics.ListCreateAPIView): return queryset - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend, filters.SearchFilter, @@ -676,10 +654,6 @@ class PartParameterTemplateList(generics.ListCreateAPIView): queryset = PartParameterTemplate.objects.all() serializer_class = part_serializers.PartParameterTemplateSerializer - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ filters.OrderingFilter, ] @@ -699,10 +673,6 @@ class PartParameterList(generics.ListCreateAPIView): queryset = PartParameter.objects.all() serializer_class = part_serializers.PartParameterSerializer - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend ] @@ -796,10 +766,6 @@ class BomList(generics.ListCreateAPIView): return queryset - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend, filters.SearchFilter, @@ -816,10 +782,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 """ diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 55bc62a44e..790de7d879 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -68,7 +68,6 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView): queryset = StockItem.objects.all() serializer_class = StockItemSerializer - permission_classes = (permissions.IsAuthenticated,) def get_queryset(self, *args, **kwargs): @@ -289,10 +288,6 @@ class StockLocationList(generics.ListCreateAPIView): return queryset - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend, filters.SearchFilter, @@ -695,10 +690,6 @@ class StockList(generics.ListCreateAPIView): return queryset - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend, filters.SearchFilter, @@ -744,10 +735,6 @@ class StockItemTestResultList(generics.ListCreateAPIView): queryset = StockItemTestResult.objects.all() serializer_class = StockItemTestResultSerializer - permission_classes = [ - permissions.IsAuthenticated, - ] - filter_backends = [ DjangoFilterBackend, filters.SearchFilter, @@ -799,7 +786,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 +857,6 @@ class LocationDetail(generics.RetrieveUpdateDestroyAPIView): queryset = StockLocation.objects.all() serializer_class = LocationSerializer - permission_classes = (permissions.IsAuthenticated,) stock_endpoints = [ From 3f59ce3f93030c37afe37e2f69a547f927ddd70d Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 01:30:36 +1100 Subject: [PATCH 07/26] Update unit tests - requires the user to actually have the necessary permissions! --- InvenTree/InvenTree/api.py | 2 ++ InvenTree/InvenTree/helpers.py | 20 +++++++++++++++++++ InvenTree/company/test_api.py | 13 ++++++++++++- InvenTree/part/test_api.py | 29 +++++++++++++++++++++++++++- InvenTree/part/test_views.py | 35 ++++++++++++++++++++++++++++++++-- InvenTree/part/views.py | 12 ++++++------ InvenTree/stock/test_api.py | 16 ++++++++++++++++ 7 files changed, 117 insertions(+), 10 deletions(-) diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index 44e7ec383f..1bdfb79ceb 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -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 = { diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 9b470902b1..13b770539c 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -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) diff --git a/InvenTree/company/test_api.py b/InvenTree/company/test_api.py index bf4cc6643e..643608542d 100644 --- a/InvenTree/company/test_api.py +++ b/InvenTree/company/test_api.py @@ -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) diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py index 3b116fa445..9fdc2688cb 100644 --- a/InvenTree/part/test_api.py +++ b/InvenTree/part/test_api.py @@ -9,6 +9,7 @@ from stock.models import StockItem from company.models import Company from InvenTree.status_codes import StockStatus +from InvenTree.helpers import addUserPermissions class PartAPITest(APITestCase): @@ -29,7 +30,33 @@ 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' + ) + + # Add the permissions required to access the API endpoints + perms = [ + 'view_part', + 'add_part', + 'change_part', + 'delete_part', + 'view_partcategory', + 'add_partcategory', + 'change_partcategory', + 'view_bomitem', + 'add_bomitem', + 'change_bomitem', + 'view_partattachment', + 'change_partattachment', + 'add_partattachment', + 'view_parttesttemplate', + 'add_parttesttemplate', + 'change_parttesttemplate', + ] + + addUserPermissions(self.user, perms) self.client.login(username='testuser', password='password') diff --git a/InvenTree/part/test_views.py b/InvenTree/part/test_views.py index bc09784a47..b1ae991a0c 100644 --- a/InvenTree/part/test_views.py +++ b/InvenTree/part/test_views.py @@ -4,6 +4,8 @@ from django.test import TestCase from django.urls import reverse from django.contrib.auth import get_user_model +from InvenTree.helpers import addUserPermissions + from .models import Part @@ -23,7 +25,32 @@ 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' + ) + + # Add the permissions required to access the pages + perms = [ + 'view_part', + 'add_part', + 'change_part', + 'delete_part', + 'view_partcategory', + 'add_partcategory', + 'change_partcategory', + 'view_bomitem', + 'add_bomitem', + 'change_bomitem', + 'view_partattachment', + 'change_partattachment', + 'add_partattachment', + ] + + addUserPermissions(self.user, perms) + + self.user.save() self.client.login(username='username', password='password') @@ -140,12 +167,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 +218,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 """ diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py index 47a77078be..8e0980c35e 100644 --- a/InvenTree/part/views.py +++ b/InvenTree/part/views.py @@ -635,7 +635,7 @@ class PartNotes(UpdateView): template_name = 'part/notes.html' model = Part - permission_required = 'part.update_part' + permission_required = 'part.change_part' fields = ['notes'] @@ -753,7 +753,7 @@ class PartImageUpload(AjaxUpdateView): form_class = part_forms.PartImageForm - permission_required = 'part.update_part' + permission_required = 'part.change_part' def get_data(self): return { @@ -768,7 +768,7 @@ class PartImageSelect(AjaxUpdateView): ajax_template_name = 'part/select_image.html' ajax_form_title = _('Select Part Image') - permission_required = 'part.update_part' + permission_required = 'part.change_part' fields = [ 'image', @@ -811,7 +811,7 @@ class PartEdit(AjaxUpdateView): ajax_form_title = _('Edit Part Properties') context_object_name = 'part' - permission_required = 'part.update_part' + permission_required = 'part.change_part' def get_form(self): """ Create form for Part editing. @@ -837,7 +837,7 @@ class BomValidate(AjaxUpdateView): context_object_name = 'part' form_class = part_forms.BomValidateForm - permission_required = ('part.update_part') + permission_required = ('part.change_part') def get_context(self): return { @@ -905,7 +905,7 @@ class BomUpload(PermissionRequiredMixin, FormView): missing_columns = [] allowed_parts = [] - permission_required = ('part.update_part', 'part.add_bomitem') + permission_required = ('part.change_part', 'part.add_bomitem') def get_success_url(self): part = self.get_object() diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index a522bc5415..8348a3e331 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -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={}): From c910307ce5aeabf6f79eba77c661e819fffc1bfc Mon Sep 17 00:00:00 2001 From: eeintech Date: Mon, 5 Oct 2020 10:04:54 -0500 Subject: [PATCH 08/26] Only saving Group model rulesets on instance creation and when inlines are saved --- InvenTree/users/admin.py | 8 ++------ InvenTree/users/models.py | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/InvenTree/users/admin.py b/InvenTree/users/admin.py index 86d4bb1a86..765bc0d795 100644 --- a/InvenTree/users/admin.py +++ b/InvenTree/users/admin.py @@ -97,15 +97,11 @@ class RoleGroupAdmin(admin.ModelAdmin): # Save inlines before model # https://stackoverflow.com/a/14860703/12794913 def save_model(self, request, obj, form, change): - if obj is not None: - # Save model immediately only if in 'Add role' view - super().save_model(request, obj, form, change) - else: - pass # don't actually save the parent instance + pass # don't actually save the parent instance def save_formset(self, request, form, formset, change): formset.save() # this will save the children - form.instance.save() # form.instance is the parent + form.instance.save(update_fields=['name']) # form.instance is the parent class InvenTreeUserAdmin(UserAdmin): diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 5fe86e15fa..990d3770c0 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -155,8 +155,15 @@ class RuleSet(models.Model): model=model ) - def __str__(self): - return self.name + def __str__(self, debug=False): + """ Ruleset string representation """ + if debug: + # Makes debugging easier + return f'{str(self.group).ljust(15)}: {self.name.title().ljust(15)} | ' \ + f'v: {str(self.can_view).ljust(5)} | a: {str(self.can_add).ljust(5)} | ' \ + f'c: {str(self.can_change).ljust(5)} | d: {str(self.can_delete).ljust(5)}' + else: + return self.name def save(self, *args, **kwargs): @@ -327,5 +334,8 @@ 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) + update_fields = kwargs.get('update_fields', None) - update_group_roles(instance) + if created or update_fields: + update_group_roles(instance) From d980da72472a430219988be89fabcfd50993535f Mon Sep 17 00:00:00 2001 From: eeintech Date: Mon, 5 Oct 2020 10:52:47 -0500 Subject: [PATCH 09/26] Fixed permission assign test unit --- InvenTree/users/admin.py | 1 + InvenTree/users/models.py | 1 + InvenTree/users/tests.py | 6 ++++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/InvenTree/users/admin.py b/InvenTree/users/admin.py index 765bc0d795..e4974a3f7a 100644 --- a/InvenTree/users/admin.py +++ b/InvenTree/users/admin.py @@ -101,6 +101,7 @@ class RoleGroupAdmin(admin.ModelAdmin): def save_formset(self, request, form, formset, change): formset.save() # this will save the children + # update_fields is required to trigger permissions update form.instance.save(update_fields=['name']) # form.instance is the parent diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 990d3770c0..df8e91d293 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -335,6 +335,7 @@ def create_missing_rule_sets(sender, instance, **kwargs): 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: diff --git a/InvenTree/users/tests.py b/InvenTree/users/tests.py index d14ffc4950..e277422f71 100644 --- a/InvenTree/users/tests.py +++ b/InvenTree/users/tests.py @@ -137,7 +137,8 @@ class RuleSetModelTest(TestCase): rule.save() - group.save() + # update_fields is required to trigger permissions update + group.save(update_fields=['name']) # There should now be three permissions for each rule set self.assertEqual(group.permissions.count(), 3 * len(permission_set)) @@ -151,7 +152,8 @@ class RuleSetModelTest(TestCase): rule.save() - group.save() + # update_fields is required to trigger permissions update + group.save(update_fields=['name']) # There should now not be any permissions assigned to this group self.assertEqual(group.permissions.count(), 0) From 8b2189dacad374b39478a1452a9a72d186a1b840 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 09:27:22 +1100 Subject: [PATCH 10/26] Add global context 'roles' - Access via template e.g. {% if roles.part.view %} - Always True if the user is a superuser --- InvenTree/InvenTree/context.py | 33 +++++++++++++++++++++++++++++++++ InvenTree/InvenTree/settings.py | 1 + 2 files changed, 34 insertions(+) diff --git a/InvenTree/InvenTree/context.py b/InvenTree/InvenTree/context.py index 7de41eef15..71aee5c2c5 100644 --- a/InvenTree/InvenTree/context.py +++ b/InvenTree/InvenTree/context.py @@ -17,3 +17,36 @@ 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(): + roles[rule.name] = { + 'view': rule.can_view or user.is_superuser, + 'add': rule.can_add or user.is_superuser, + 'change': rule.can_change or user.is_superuser, + 'delete': rule.can_delete or user.is_superuser, + } + + print("Roles:") + print(roles) + + return {'roles': roles} diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index 75f6f58993..c6f8b40069 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -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', ], }, }, From 556ffa1099197a0f41d734dbdcc4782e2e40b5ee Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 09:28:05 +1100 Subject: [PATCH 11/26] Change label for permissions to match django permission names --- .../migrations/0003_auto_20201005_2227.py | 23 +++++++++++++++++++ InvenTree/users/models.py | 4 ++-- 2 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 InvenTree/users/migrations/0003_auto_20201005_2227.py diff --git a/InvenTree/users/migrations/0003_auto_20201005_2227.py b/InvenTree/users/migrations/0003_auto_20201005_2227.py new file mode 100644 index 0000000000..92d7e341fa --- /dev/null +++ b/InvenTree/users/migrations/0003_auto_20201005_2227.py @@ -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'), + ), + ] diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py index 5fe86e15fa..3bd976f0a4 100644 --- a/InvenTree/users/models.py +++ b/InvenTree/users/models.py @@ -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')) From 23aee234f0002b35f93945994562ccb3037d5d92 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 09:32:05 +1100 Subject: [PATCH 12/26] Change index page to use roles rather than perms to determine user permissions --- InvenTree/templates/InvenTree/index.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/InvenTree/templates/InvenTree/index.html b/InvenTree/templates/InvenTree/index.html index b20d61116d..8e59d51d2b 100644 --- a/InvenTree/templates/InvenTree/index.html +++ b/InvenTree/templates/InvenTree/index.html @@ -9,24 +9,24 @@ InvenTree | {% trans "Index" %}
    - {% 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 %}
    - {% 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 %}
    From fa21d66c411aadf71031ba415b02ea12e64fd3cf Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 09:54:37 +1100 Subject: [PATCH 13/26] Fix logic for global context object 'roles' - User may be a part of multiple groups - Roles are additive across groups --- InvenTree/InvenTree/context.py | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/InvenTree/InvenTree/context.py b/InvenTree/InvenTree/context.py index 71aee5c2c5..f9d856f566 100644 --- a/InvenTree/InvenTree/context.py +++ b/InvenTree/InvenTree/context.py @@ -35,18 +35,25 @@ def user_roles(request): user = request.user - roles = {} + roles = { + } for group in user.groups.all(): for rule in group.rule_sets.all(): - roles[rule.name] = { - 'view': rule.can_view or user.is_superuser, - 'add': rule.can_add or user.is_superuser, - 'change': rule.can_change or user.is_superuser, - 'delete': rule.can_delete or user.is_superuser, - } - print("Roles:") - print(roles) + # 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} From d2e2e7511f70e90c2e6f3787bc66bc5ea65a9df3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 6 Oct 2020 10:07:39 +1100 Subject: [PATCH 14/26] Update templates: Change perms to roles --- InvenTree/part/templates/part/bom.html | 2 +- InvenTree/part/templates/part/build.html | 2 +- InvenTree/part/templates/part/category.html | 14 +++++++------- InvenTree/part/templates/part/notes.html | 2 +- InvenTree/part/templates/part/params.html | 8 ++++---- InvenTree/part/templates/part/part_base.html | 16 ++++++++-------- InvenTree/part/templates/part/tabs.html | 2 +- InvenTree/templates/navbar.html | 10 +++++----- InvenTree/templates/stock_table.html | 8 ++++---- 9 files changed, 32 insertions(+), 32 deletions(-) diff --git a/InvenTree/part/templates/part/bom.html b/InvenTree/part/templates/part/bom.html index 610e60847a..c996edc2db 100644 --- a/InvenTree/part/templates/part/bom.html +++ b/InvenTree/part/templates/part/bom.html @@ -39,7 +39,7 @@ {% elif part.active %} - {% if perms.part.change_part %} + {% if roles.part.change %} {% if part.is_bom_valid == False %} diff --git a/InvenTree/part/templates/part/build.html b/InvenTree/part/templates/part/build.html index ad51d33ab2..bfd72a2f70 100644 --- a/InvenTree/part/templates/part/build.html +++ b/InvenTree/part/templates/part/build.html @@ -10,7 +10,7 @@
    {% if part.active %} - {% if perms.build.add_build %} + {% if roles.build.add %} {% endif %} {% endif %} diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index 25417a1d9b..d73aba0291 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -9,7 +9,7 @@ {% if category %}

    {{ category.name }} - {% if user.is_staff and perms.part.change_partcategory %} + {% if user.is_staff and roles.part.change %} {% endif %}

    @@ -20,18 +20,18 @@ {% endif %}

    - {% if perms.part.add_partcategory %} + {% if roles.part.add %} {% endif %} {% if category %} - {% if perms.part.change_partcategory %} + {% if roles.part.change %} {% endif %} - {% if perms.part.delete_partcategory %} + {% if roles.part.delete %} @@ -110,13 +110,13 @@
    - {% if perms.part.add_part %} + {% if roles.part.add %} {% endif %}
    - {% if perms.part.change_part %} + {% if roles.part.change %} {% endif %}
    diff --git a/InvenTree/part/templates/part/params.html b/InvenTree/part/templates/part/params.html index 9984830242..ba8aa0566d 100644 --- a/InvenTree/part/templates/part/params.html +++ b/InvenTree/part/templates/part/params.html @@ -10,7 +10,7 @@
    - {% if perms.part.add_partparameter %} + {% if roles.part.add %} {% endif %}
    @@ -32,10 +32,10 @@
    {{ param.template.units }}
    - {% if perms.part.change_partparameter %} + {% if roles.part.change %} {% endif %} - {% if perms.part.delete_partparameter %} + {% if roles.part.delete %} {% endif %}
    @@ -54,7 +54,7 @@ $('#param-table').inventreeTable({ }); - {% if perms.part.add_partparameter %} + {% if roles.part.add %} $('#param-create').click(function() { launchModalForm("{% url 'part-param-create' %}?part={{ part.id }}", { reload: true, diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 6103cb5369..750f3f3806 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -56,13 +56,13 @@ - {% if perms.stock.change_stockitem %} + {% if roles.stock.change %} {% endif %} {% if part.purchaseable %} - {% if perms.order.add_purchaseorder %} + {% if roles.purchase_order.add %} @@ -70,17 +70,17 @@ {% endif %} {% endif %} - {% if perms.part.add_part or perms.part.change_part or perms.part.delete_part %} + {% if roles.part.add or roles.part.change or roles.part.delete %}
    @@ -284,7 +284,7 @@ }); }); - {% if perms.part.change_part %} + {% if roles.part.change %} $("#part-edit").click(function() { launchModalForm( "{% url 'part-edit' part.id %}", @@ -304,7 +304,7 @@ }); }); - {% if perms.part.add_part %} + {% if roles.part.add %} $("#part-duplicate").click(function() { launchModalForm( "{% url 'part-duplicate' part.id %}", diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index 02d8672979..e36675eeab 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -26,7 +26,7 @@ {% if part.assembly %} {% trans "BOM" %}{{ part.bom_count }} - {% if perms.build.view_build %} + {% if roles.build.view %} {% trans "Build Orders" %}{{ part.builds.count }} diff --git a/InvenTree/templates/navbar.html b/InvenTree/templates/navbar.html index 2224d85bc4..148a96c583 100644 --- a/InvenTree/templates/navbar.html +++ b/InvenTree/templates/navbar.html @@ -15,16 +15,16 @@