From 21f209f7cce0f12c306f5e37dbd681dda05072cd Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 16 Feb 2024 08:47:05 +1100 Subject: [PATCH] Forms actions fix (#6493) * Handle case where OPTIONS.actions is not present * Specify stock.change permission * Hide table button based on user permission * Fix for permission check class --- InvenTree/InvenTree/permissions.py | 4 ++++ InvenTree/stock/api.py | 2 ++ InvenTree/stock/templates/stock/item.html | 1 + InvenTree/templates/js/translated/forms.js | 6 +++++- InvenTree/templates/js/translated/stock.js | 5 ++++- 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/InvenTree/InvenTree/permissions.py b/InvenTree/InvenTree/permissions.py index dee5e0256d..da472b8637 100644 --- a/InvenTree/InvenTree/permissions.py +++ b/InvenTree/InvenTree/permissions.py @@ -69,6 +69,10 @@ class RolePermission(permissions.BasePermission): # The required role may be defined for the view class if role := getattr(view, 'role_required', None): + # If the role is specified as "role.permission", split it + if '.' in role: + role, permission = role.split('.') + return users.models.check_user_role(user, role, permission) try: diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index ac46683243..c601f6478a 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -123,6 +123,8 @@ class StockDetail(RetrieveUpdateDestroyAPI): class StockItemContextMixin: """Mixin class for adding StockItem object to serializer context.""" + role_required = 'stock.change' + queryset = StockItem.objects.none() def get_serializer_context(self): diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index d811aa17f3..4512d13d5d 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -196,6 +196,7 @@ stock_item: {{ item.pk }}, part: {{ item.part.pk }}, quantity: {{ item.quantity|unlocalize }}, + can_edit: {% js_bool roles.stock.change %}, } ); diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index e5b075942e..6d448a0d14 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -346,7 +346,11 @@ function constructForm(url, options={}) { getApiEndpointOptions(url, function(OPTIONS) { // Copy across entire actions struct - options.actions = OPTIONS.actions.POST || OPTIONS.actions.PUT || OPTIONS.actions.PATCH || OPTIONS.actions.DELETE || {}; + if (OPTIONS && OPTIONS.actions) { + options.actions = OPTIONS.actions.POST || OPTIONS.actions.PUT || OPTIONS.actions.PATCH || OPTIONS.actions.DELETE || {}; + } else { + options.actions = {}; + } // Extract any custom 'context' information from the OPTIONS data options.context = OPTIONS.context || {}; diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index 88613f09bc..6790ee20a8 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -3101,11 +3101,14 @@ function loadInstalledInTable(table, options) { field: 'buttons', title: '', switchable: false, + visible: options.can_edit, formatter: function(value, row) { let pk = row.pk; let html = ''; - html += makeIconButton('fa-unlink', 'button-uninstall', pk, '{% trans "Uninstall Stock Item" %}'); + if (options.can_edit) { + html += makeIconButton('fa-unlink', 'button-uninstall', pk, '{% trans "Uninstall Stock Item" %}'); + } return wrapButtons(html); }