diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html
index 6992d9bac4..47ef866e2e 100644
--- a/InvenTree/part/templates/part/category.html
+++ b/InvenTree/part/templates/part/category.html
@@ -9,7 +9,7 @@
{% if category %}
- {% if roles.part.add %}
+ {% if roles.part_category.add %}
{% endif %}
{% if category %}
- {% if roles.part.change %}
+ {% if roles.part_category.change %}
{% endif %}
- {% if roles.part.delete %}
+ {% if roles.part_category.delete %}
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index d1a612b7f4..0e16bfb498 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -2088,7 +2088,7 @@ class CategoryDetail(InvenTreeRoleMixin, DetailView):
queryset = PartCategory.objects.all().prefetch_related('children')
template_name = 'part/category_partlist.html'
- role_required = 'part.view'
+ role_required = ['part_category.view', 'part.view']
def get_context_data(self, **kwargs):
@@ -2138,7 +2138,7 @@ class CategoryEdit(AjaxUpdateView):
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Part Category')
- role_required = 'part.change'
+ role_required = 'part_category.change'
def get_context_data(self, **kwargs):
context = super(CategoryEdit, self).get_context_data(**kwargs).copy()
@@ -2177,7 +2177,7 @@ class CategoryDelete(AjaxDeleteView):
context_object_name = 'category'
success_url = '/part/'
- role_required = 'part.delete'
+ role_required = 'part_category.delete'
def get_data(self):
return {
@@ -2193,7 +2193,7 @@ class CategoryCreate(AjaxCreateView):
ajax_template_name = 'modal_form.html'
form_class = part_forms.EditCategoryForm
- role_required = 'part.add'
+ role_required = 'part_category.add'
def get_context_data(self, **kwargs):
""" Add extra context data to template.
@@ -2233,7 +2233,7 @@ class CategoryCreate(AjaxCreateView):
class CategoryParameterTemplateCreate(AjaxCreateView):
""" View for creating a new PartCategoryParameterTemplate """
- role_required = 'part.add'
+ role_required = 'part_category.change'
model = PartCategoryParameterTemplate
form_class = part_forms.EditCategoryParameterTemplateForm
@@ -2336,7 +2336,7 @@ class CategoryParameterTemplateCreate(AjaxCreateView):
class CategoryParameterTemplateEdit(AjaxUpdateView):
""" View for editing a PartCategoryParameterTemplate """
- role_required = 'part.change'
+ role_required = 'part_category.change'
model = PartCategoryParameterTemplate
form_class = part_forms.EditCategoryParameterTemplateForm
@@ -2395,7 +2395,7 @@ class CategoryParameterTemplateEdit(AjaxUpdateView):
class CategoryParameterTemplateDelete(AjaxDeleteView):
""" View for deleting an existing PartCategoryParameterTemplate """
- role_required = 'part.delete'
+ role_required = 'part_category.change'
model = PartCategoryParameterTemplate
ajax_form_title = _("Delete Category Parameter Template")
diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html
index fef3428373..765a4bd903 100644
--- a/InvenTree/stock/templates/stock/location.html
+++ b/InvenTree/stock/templates/stock/location.html
@@ -8,7 +8,7 @@
{% if location %}
{{ location.name }}
- {% if user.is_staff and roles.stock.change %}
+ {% if user.is_staff and roles.stock_location.change %}
{% endif %}
@@ -18,7 +18,7 @@
{% trans "All stock items" %}
{% endif %}
- {% if roles.stock.add %}
+ {% if roles.stock_location.add %}
@@ -41,11 +41,13 @@
{% trans "Count stock" %}
+ {% endif %}
+ {% if roles.stock_location.change %}
diff --git a/InvenTree/stock/templates/stock/location_list.html b/InvenTree/stock/templates/stock/location_list.html
index 4ad30e1310..a2ea4a361a 100644
--- a/InvenTree/stock/templates/stock/location_list.html
+++ b/InvenTree/stock/templates/stock/location_list.html
@@ -1,6 +1,6 @@
{% extends "collapse.html" %}
-{% if roles.stock.view %}
+{% if roles.stock_location.view or roles.stock.view %}
{% block collapse_title %}
Sub-Locations{{ children|length }}
{% endblock %}
diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py
index ab6f64fb44..830f2c66f7 100644
--- a/InvenTree/stock/views.py
+++ b/InvenTree/stock/views.py
@@ -73,7 +73,7 @@ class StockLocationDetail(InvenTreeRoleMixin, DetailView):
template_name = 'stock/location.html'
queryset = StockLocation.objects.all()
model = StockLocation
- role_required = 'stock.view'
+ role_required = ['stock_location.view', 'stock.view']
class StockItemDetail(InvenTreeRoleMixin, DetailView):
@@ -121,7 +121,7 @@ class StockLocationEdit(AjaxUpdateView):
context_object_name = 'location'
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Stock Location')
- role_required = 'stock.change'
+ role_required = 'stock_location.change'
def get_form(self):
""" Customize form data for StockLocation editing.
@@ -146,7 +146,7 @@ class StockLocationQRCode(QRCodeView):
""" View for displaying a QR code for a StockLocation object """
ajax_form_title = _("Stock Location QR code")
- role_required = 'stock.view'
+ role_required = ['stock_location.view', 'stock.view']
def get_qr_data(self):
""" Generate QR code data for the StockLocation """
@@ -1361,7 +1361,7 @@ class StockLocationCreate(AjaxCreateView):
context_object_name = 'location'
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Create new Stock Location')
- role_required = 'stock.add'
+ role_required = 'stock_location.add'
def get_initial(self):
initials = super(StockLocationCreate, self).get_initial().copy()
@@ -1748,7 +1748,7 @@ class StockLocationDelete(AjaxDeleteView):
ajax_template_name = 'stock/location_delete.html'
context_object_name = 'location'
ajax_form_title = _('Delete Stock Location')
- role_required = 'stock.delete'
+ role_required = 'stock_location.delete'
class StockItemDelete(AjaxDeleteView):
diff --git a/InvenTree/users/admin.py b/InvenTree/users/admin.py
index 29496d02a7..c84f1310ce 100644
--- a/InvenTree/users/admin.py
+++ b/InvenTree/users/admin.py
@@ -30,6 +30,8 @@ class RuleSetInline(admin.TabularInline):
max_num = len(RuleSet.RULESET_CHOICES)
min_num = 1
extra = 0
+ # TODO: find better way to order inlines
+ ordering = ['name']
class InvenTreeGroupAdminForm(forms.ModelForm):
@@ -87,7 +89,8 @@ class RoleGroupAdmin(admin.ModelAdmin):
RuleSetInline,
]
- list_display = ('name', 'admin', 'part', 'stock', 'build', 'purchase_order', 'sales_order')
+ list_display = ('name', 'admin', 'part_category', 'part', 'stock_location',
+ 'stock_item', 'build', 'purchase_order', 'sales_order')
def get_rule_set(self, obj, rule_set_type):
''' Return list of permissions for the given ruleset '''
@@ -130,10 +133,16 @@ class RoleGroupAdmin(admin.ModelAdmin):
def admin(self, obj):
return self.get_rule_set(obj, 'admin')
+ def part_category(self, obj):
+ return self.get_rule_set(obj, 'part_category')
+
def part(self, obj):
return self.get_rule_set(obj, 'part')
- def stock(self, obj):
+ def stock_location(self, obj):
+ return self.get_rule_set(obj, 'stock_location')
+
+ def stock_item(self, obj):
return self.get_rule_set(obj, 'stock')
def build(self, obj):
diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py
index b54cddf7c4..776514fc9f 100644
--- a/InvenTree/users/models.py
+++ b/InvenTree/users/models.py
@@ -25,8 +25,10 @@ class RuleSet(models.Model):
RULESET_CHOICES = [
('admin', _('Admin')),
+ ('part_category', _('Part Categories')),
('part', _('Parts')),
- ('stock', _('Stock')),
+ ('stock_location', _('Stock Locations')),
+ ('stock', _('Stock Items')),
('build', _('Build Orders')),
('purchase_order', _('Purchase Orders')),
('sales_order', _('Sales Orders')),
@@ -48,21 +50,25 @@ class RuleSet(models.Model):
'authtoken_token',
'users_ruleset',
],
+ 'part_category': [
+ 'part_partcategory',
+ 'part_partcategoryparametertemplate',
+ ],
'part': [
'part_part',
'part_bomitem',
- 'part_partcategory',
'part_partattachment',
'part_partsellpricebreak',
'part_parttesttemplate',
'part_partparametertemplate',
'part_partparameter',
'part_partrelated',
- 'part_partcategoryparametertemplate',
+ ],
+ 'stock_location': [
+ 'stock_stocklocation',
],
'stock': [
'stock_stockitem',
- 'stock_stocklocation',
'stock_stockitemattachment',
'stock_stockitemtracking',
'stock_stockitemtestresult',