From 1c97aaf87a4474df73198b2b6d46f702ff802b66 Mon Sep 17 00:00:00 2001
From: Oliver Walters
Date: Tue, 6 Oct 2020 19:46:53 +1100
Subject: [PATCH 1/5] Set permissions for order views
---
InvenTree/InvenTree/views.py | 8 ++-
.../templates/order/sales_order_base.html | 2 +-
InvenTree/order/views.py | 53 ++++++++++++++++---
InvenTree/users/models.py | 4 ++
4 files changed, 58 insertions(+), 9 deletions(-)
diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py
index 198903db9a..bb7c1e6f5d 100644
--- a/InvenTree/InvenTree/views.py
+++ b/InvenTree/InvenTree/views.py
@@ -22,7 +22,7 @@ from django.views.generic.base import TemplateView
from part.models import Part, PartCategory
from stock.models import StockLocation, StockItem
from common.models import InvenTreeSetting, ColorTheme
-from users.models import check_user_role
+from users.models import check_user_role, RuleSet
from .forms import DeleteForm, EditUserForm, SetPasswordForm, ColorThemeSelectForm
from .helpers import str2bool
@@ -147,7 +147,13 @@ class InvenTreeRoleMixin(PermissionRequiredMixin):
for required in roles_required:
(role, permission) = required.split('.')
+
+ if role not in RuleSet.RULESET_NAMES:
+ raise ValueError(f"Role '{role}' is not a valid role")
+ if permission not in RuleSet.RULESET_PERMISSIONS:
+ raise ValueError(f"Permission '{permission}' is not a valid permission")
+
# Return False if the user does not have *any* of the required roles
if not check_user_role(user, role, permission):
return False
diff --git a/InvenTree/order/templates/order/sales_order_base.html b/InvenTree/order/templates/order/sales_order_base.html
index 0572104e09..52856cadbd 100644
--- a/InvenTree/order/templates/order/sales_order_base.html
+++ b/InvenTree/order/templates/order/sales_order_base.html
@@ -44,7 +44,7 @@ src="{% static 'img/blank_image.png' %}"
-
+
{% if order.status == SalesOrderStatus.PENDING %}
diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py
index 5d140fb77c..28b8416b65 100644
--- a/InvenTree/order/views.py
+++ b/InvenTree/order/views.py
@@ -28,19 +28,22 @@ from . import forms as order_forms
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.helpers import DownloadFile, str2bool
+from InvenTree.views import InvenTreeRoleMixin
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus, StockStatus
logger = logging.getLogger(__name__)
-class PurchaseOrderIndex(ListView):
+class PurchaseOrderIndex(InvenTreeRoleMixin, ListView):
""" List view for all purchase orders """
model = PurchaseOrder
template_name = 'order/purchase_orders.html'
context_object_name = 'orders'
+ role_required = 'purchase_order.view'
+
def get_queryset(self):
""" Retrieve the list of purchase orders,
ensure that the most recent ones are returned first. """
@@ -55,19 +58,21 @@ class PurchaseOrderIndex(ListView):
return ctx
-class SalesOrderIndex(ListView):
+class SalesOrderIndex(InvenTreeRoleMixin, ListView):
model = SalesOrder
template_name = 'order/sales_orders.html'
context_object_name = 'orders'
+ role_required = 'sales_order.view'
-class PurchaseOrderDetail(DetailView):
+class PurchaseOrderDetail(InvenTreeRoleMixin, DetailView):
""" Detail view for a PurchaseOrder object """
context_object_name = 'order'
queryset = PurchaseOrder.objects.all().prefetch_related('lines')
template_name = 'order/purchase_order_detail.html'
+ role_required = 'purchase_order.view'
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
@@ -75,12 +80,13 @@ class PurchaseOrderDetail(DetailView):
return ctx
-class SalesOrderDetail(DetailView):
+class SalesOrderDetail(InvenTreeRoleMixin, DetailView):
""" Detail view for a SalesOrder object """
context_object_name = 'order'
queryset = SalesOrder.objects.all().prefetch_related('lines')
template_name = 'order/sales_order_detail.html'
+ role_required = 'sales_order.view'
class PurchaseOrderAttachmentCreate(AjaxCreateView):
@@ -92,6 +98,7 @@ class PurchaseOrderAttachmentCreate(AjaxCreateView):
form_class = order_forms.EditPurchaseOrderAttachmentForm
ajax_form_title = _("Add Purchase Order Attachment")
ajax_template_name = "modal_form.html"
+ role_required = 'purchase_order.add'
def post_save(self, **kwargs):
self.object.user = self.request.user
@@ -139,6 +146,7 @@ class SalesOrderAttachmentCreate(AjaxCreateView):
model = SalesOrderAttachment
form_class = order_forms.EditSalesOrderAttachmentForm
ajax_form_title = _('Add Sales Order Attachment')
+ role_required = 'sales_order.add'
def post_save(self, **kwargs):
self.object.user = self.request.user
@@ -174,6 +182,7 @@ class PurchaseOrderAttachmentEdit(AjaxUpdateView):
model = PurchaseOrderAttachment
form_class = order_forms.EditPurchaseOrderAttachmentForm
ajax_form_title = _("Edit Attachment")
+ role_required = 'purchase_order.change'
def get_data(self):
return {
@@ -195,6 +204,7 @@ class SalesOrderAttachmentEdit(AjaxUpdateView):
model = SalesOrderAttachment
form_class = order_forms.EditSalesOrderAttachmentForm
ajax_form_title = _("Edit Attachment")
+ role_required = 'sales_order.change'
def get_data(self):
return {
@@ -216,6 +226,7 @@ class PurchaseOrderAttachmentDelete(AjaxDeleteView):
ajax_form_title = _("Delete Attachment")
ajax_template_name = "order/delete_attachment.html"
context_object_name = "attachment"
+ role_required = 'purchase_order.delete'
def get_data(self):
return {
@@ -230,6 +241,7 @@ class SalesOrderAttachmentDelete(AjaxDeleteView):
ajax_form_title = _("Delete Attachment")
ajax_template_name = "order/delete_attachment.html"
context_object_name = "attachment"
+ role_required = 'sales_order.delete'
def get_data(self):
return {
@@ -237,12 +249,13 @@ class SalesOrderAttachmentDelete(AjaxDeleteView):
}
-class PurchaseOrderNotes(UpdateView):
+class PurchaseOrderNotes(InvenTreeRoleMixin, UpdateView):
""" View for updating the 'notes' field of a PurchaseOrder """
context_object_name = 'order'
template_name = 'order/order_notes.html'
model = PurchaseOrder
+ role_required = 'purchase_order.change'
fields = ['notes']
@@ -259,12 +272,13 @@ class PurchaseOrderNotes(UpdateView):
return ctx
-class SalesOrderNotes(UpdateView):
+class SalesOrderNotes(InvenTreeRoleMixin, UpdateView):
""" View for editing the 'notes' field of a SalesORder """
context_object_name = 'order'
template_name = 'order/sales_order_notes.html'
model = SalesOrder
+ role_required = 'sales_order.view'
fields = ['notes']
@@ -286,6 +300,7 @@ class PurchaseOrderCreate(AjaxCreateView):
model = PurchaseOrder
ajax_form_title = _("Create Purchase Order")
form_class = order_forms.EditPurchaseOrderForm
+ role_required = 'purchase_order.add'
def get_initial(self):
initials = super().get_initial().copy()
@@ -317,6 +332,7 @@ class SalesOrderCreate(AjaxCreateView):
model = SalesOrder
ajax_form_title = _("Create Sales Order")
form_class = order_forms.EditSalesOrderForm
+ role_required = 'sales_order.add'
def get_initial(self):
initials = super().get_initial().copy()
@@ -347,6 +363,7 @@ class PurchaseOrderEdit(AjaxUpdateView):
model = PurchaseOrder
ajax_form_title = _('Edit Purchase Order')
form_class = order_forms.EditPurchaseOrderForm
+ role_required = 'purchase_order.change'
def get_form(self):
@@ -367,6 +384,7 @@ class SalesOrderEdit(AjaxUpdateView):
model = SalesOrder
ajax_form_title = _('Edit Sales Order')
form_class = order_forms.EditSalesOrderForm
+ role_required = 'sales_order.change'
def get_form(self):
form = super().get_form()
@@ -384,6 +402,7 @@ class PurchaseOrderCancel(AjaxUpdateView):
ajax_form_title = _('Cancel Order')
ajax_template_name = 'order/order_cancel.html'
form_class = order_forms.CancelPurchaseOrderForm
+ role_required = 'purchase_order.change'
def post(self, request, *args, **kwargs):
""" Mark the PO as 'CANCELLED' """
@@ -417,6 +436,7 @@ class SalesOrderCancel(AjaxUpdateView):
ajax_form_title = _("Cancel sales order")
ajax_template_name = "order/sales_order_cancel.html"
form_class = order_forms.CancelSalesOrderForm
+ role_required = 'sales_order.change'
def post(self, request, *args, **kwargs):
@@ -451,6 +471,7 @@ class PurchaseOrderIssue(AjaxUpdateView):
ajax_form_title = _('Issue Order')
ajax_template_name = "order/order_issue.html"
form_class = order_forms.IssuePurchaseOrderForm
+ role_required = 'purchase_order.change'
def post(self, request, *args, **kwargs):
""" Mark the purchase order as 'PLACED' """
@@ -486,6 +507,7 @@ class PurchaseOrderComplete(AjaxUpdateView):
ajax_template_name = "order/order_complete.html"
ajax_form_title = _("Complete Order")
context_object_name = 'order'
+ role_required = 'purchase_order.change'
def get_context_data(self):
@@ -520,6 +542,7 @@ class SalesOrderShip(AjaxUpdateView):
context_object_name = 'order'
ajax_template_name = 'order/sales_order_ship.html'
ajax_form_title = _('Ship Order')
+ role_required = 'sales_order.change'
def post(self, request, *args, **kwargs):
@@ -563,6 +586,7 @@ class PurchaseOrderExport(AjaxView):
"""
model = PurchaseOrder
+ role_required = 'purchase_order.view'
def get(self, request, *args, **kwargs):
@@ -594,6 +618,7 @@ class PurchaseOrderReceive(AjaxUpdateView):
form_class = order_forms.ReceivePurchaseOrderForm
ajax_form_title = _("Receive Parts")
ajax_template_name = "order/receive_parts.html"
+ role_required = 'purchase_order.change'
# Where the parts will be going (selected in POST request)
destination = None
@@ -779,6 +804,11 @@ class OrderParts(AjaxView):
ajax_form_title = _("Order Parts")
ajax_template_name = 'order/order_wizard/select_parts.html'
+ role_required = [
+ 'part.view',
+ 'purchase_order.change',
+ ]
+
# List of Parts we wish to order
parts = []
suppliers = []
@@ -1085,6 +1115,7 @@ class POLineItemCreate(AjaxCreateView):
context_object_name = 'line'
form_class = order_forms.EditPurchaseOrderLineItemForm
ajax_form_title = _('Add Line Item')
+ role_required = 'purchase_order.add'
def post(self, request, *arg, **kwargs):
@@ -1199,6 +1230,7 @@ class SOLineItemCreate(AjaxCreateView):
context_order_name = 'line'
form_class = order_forms.EditSalesOrderLineItemForm
ajax_form_title = _('Add Line Item')
+ role_required = 'sales_order.add'
def get_form(self, *args, **kwargs):
@@ -1250,6 +1282,7 @@ class SOLineItemEdit(AjaxUpdateView):
model = SalesOrderLineItem
form_class = order_forms.EditSalesOrderLineItemForm
ajax_form_title = _('Edit Line Item')
+ role_required = 'sales_order.change'
def get_form(self):
form = super().get_form()
@@ -1268,6 +1301,7 @@ class POLineItemEdit(AjaxUpdateView):
form_class = order_forms.EditPurchaseOrderLineItemForm
ajax_template_name = 'modal_form.html'
ajax_form_title = _('Edit Line Item')
+ role_required = 'purchase_order.change'
def get_form(self):
form = super().get_form()
@@ -1285,7 +1319,8 @@ class POLineItemDelete(AjaxDeleteView):
model = PurchaseOrderLineItem
ajax_form_title = _('Delete Line Item')
ajax_template_name = 'order/po_lineitem_delete.html'
-
+ role_required = 'purchase_order.delete'
+
def get_data(self):
return {
'danger': _('Deleted line item'),
@@ -1297,6 +1332,7 @@ class SOLineItemDelete(AjaxDeleteView):
model = SalesOrderLineItem
ajax_form_title = _("Delete Line Item")
ajax_template_name = "order/so_lineitem_delete.html"
+ role_required = 'sales_order.delete'
def get_data(self):
return {
@@ -1310,6 +1346,7 @@ class SalesOrderAllocationCreate(AjaxCreateView):
model = SalesOrderAllocation
form_class = order_forms.EditSalesOrderAllocationForm
ajax_form_title = _('Allocate Stock to Order')
+ role_required = 'sales_order.add'
def get_initial(self):
initials = super().get_initial().copy()
@@ -1379,6 +1416,7 @@ class SalesOrderAllocationEdit(AjaxUpdateView):
model = SalesOrderAllocation
form_class = order_forms.EditSalesOrderAllocationForm
ajax_form_title = _('Edit Allocation Quantity')
+ role_required = 'sales_order.change'
def get_form(self):
form = super().get_form()
@@ -1396,3 +1434,4 @@ class SalesOrderAllocationDelete(AjaxDeleteView):
ajax_form_title = _("Remove allocation")
context_object_name = 'allocation'
ajax_template_name = "order/so_allocation_delete.html"
+ role_required = 'sales_order.delete'
diff --git a/InvenTree/users/models.py b/InvenTree/users/models.py
index fcf137bb01..d3c713d07d 100644
--- a/InvenTree/users/models.py
+++ b/InvenTree/users/models.py
@@ -36,6 +36,10 @@ class RuleSet(models.Model):
choice[0] for choice in RULESET_CHOICES
]
+ RULESET_PERMISSIONS = [
+ 'view', 'add', 'change', 'delete',
+ ]
+
RULESET_MODELS = {
'admin': [
'auth_group',
From 9abb211cdf35331e5f525d9a70ec9cf3af794148 Mon Sep 17 00:00:00 2001
From: Oliver Walters
Date: Tue, 6 Oct 2020 20:09:55 +1100
Subject: [PATCH 2/5] Update template permissions
---
.../order/templates/order/order_base.html | 18 ++++++++++--------
.../order/templates/order/order_notes.html | 2 ++
.../order/templates/order/po_attachments.html | 1 -
.../templates/order/purchase_order_detail.html | 6 +++---
.../order/templates/order/purchase_orders.html | 2 ++
.../templates/order/sales_order_base.html | 8 +++++---
.../order/templates/order/sales_orders.html | 2 ++
7 files changed, 24 insertions(+), 15 deletions(-)
diff --git a/InvenTree/order/templates/order/order_base.html b/InvenTree/order/templates/order/order_base.html
index 71e3455722..3b199c1840 100644
--- a/InvenTree/order/templates/order/order_base.html
+++ b/InvenTree/order/templates/order/order_base.html
@@ -32,29 +32,31 @@ src="{% static 'img/blank_image.png' %}"
-
+ {% if roles.purchase_order.change %}
+
-
-
-
{% if order.status == PurchaseOrderStatus.PENDING and order.lines.count > 0 %}
-
+
{% elif order.status == PurchaseOrderStatus.PLACED %}
-
+
-
+
{% endif %}
{% if order.status == PurchaseOrderStatus.PENDING or order.status == PurchaseOrderStatus.PLACED %}
-
+
{% endif %}
+ {% endif %}
+
+
+
diff --git a/InvenTree/order/templates/order/order_notes.html b/InvenTree/order/templates/order/order_notes.html
index 237098e10d..0b45a5da4b 100644
--- a/InvenTree/order/templates/order/order_notes.html
+++ b/InvenTree/order/templates/order/order_notes.html
@@ -28,9 +28,11 @@
{% trans "Order Notes" %}
+ {% if roles.purchase_order.change %}
+ {% endif %}
diff --git a/InvenTree/order/templates/order/po_attachments.html b/InvenTree/order/templates/order/po_attachments.html
index f5421e8760..d60d605771 100644
--- a/InvenTree/order/templates/order/po_attachments.html
+++ b/InvenTree/order/templates/order/po_attachments.html
@@ -14,7 +14,6 @@
{% include "attachment_table.html" with attachments=order.attachments.all %}
-
{% endblock %}
{% block js_ready %}
diff --git a/InvenTree/order/templates/order/purchase_order_detail.html b/InvenTree/order/templates/order/purchase_order_detail.html
index 9b9eddb887..642ff96d4a 100644
--- a/InvenTree/order/templates/order/purchase_order_detail.html
+++ b/InvenTree/order/templates/order/purchase_order_detail.html
@@ -12,7 +12,7 @@
- {% if order.status == PurchaseOrderStatus.PENDING %}
+ {% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.change %}
{% trans "Add Line Item" %}
{% endif %}
@@ -209,12 +209,12 @@ $("#po-table").inventreeTable({
var pk = row.pk;
- {% if order.status == PurchaseOrderStatus.PENDING %}
+ {% if order.status == PurchaseOrderStatus.PENDING and roles.purchase_order.delete %}
html += makeIconButton('fa-edit icon-blue', 'button-line-edit', pk, '{% trans "Edit line item" %}');
html += makeIconButton('fa-trash-alt icon-red', 'button-line-delete', pk, '{% trans "Delete line item" %}');
{% endif %}
- {% if order.status == PurchaseOrderStatus.PLACED %}
+ {% if order.status == PurchaseOrderStatus.PLACED and roles.purchase_order.change %}
if (row.received < row.quantity) {
html += makeIconButton('fa-clipboard-check', 'button-line-receive', pk, '{% trans "Receive line item" %}');
}
diff --git a/InvenTree/order/templates/order/purchase_orders.html b/InvenTree/order/templates/order/purchase_orders.html
index 1019092151..d02af36ff5 100644
--- a/InvenTree/order/templates/order/purchase_orders.html
+++ b/InvenTree/order/templates/order/purchase_orders.html
@@ -14,7 +14,9 @@ InvenTree | {% trans "Purchase Orders" %}