From 0da5957c50b94d7daa479586a7c9fe67b20d8aec Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 25 Feb 2022 21:23:20 +1100 Subject: [PATCH 1/4] template fix --- InvenTree/stock/templates/stock/location.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 39b9faedb4..c07e4f8162 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -20,6 +20,8 @@ {% endblock %} {% block actions %} +{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} + {% if location and user.is_staff and roles.stock_location.change %} {% url 'admin:stock_stocklocation_change' location.pk as url %} From d6764573c39aa11cf5bbb682333fd43b00737a97 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 25 Feb 2022 22:45:24 +1100 Subject: [PATCH 2/4] Refactor "ownership" concepts in templates - Template code was very messy - Makes it a lot "simpler" - Adds convenience functions to the StockLocation model - Adds pre-calculated data to the template - Display ownership information on the stocklocation page --- InvenTree/stock/models.py | 37 +++++++++++++++++++ InvenTree/stock/templates/stock/location.html | 35 ++++++++---------- InvenTree/stock/views.py | 15 ++++++++ 3 files changed, 68 insertions(+), 19 deletions(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 42cc5b9f7a..83e7a0db8d 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -63,6 +63,43 @@ class StockLocation(InvenTreeTree): help_text=_('Select Owner'), related_name='stock_locations') + def get_location_owner(self): + """ + Get the closest "owner" for this location. + + Start at this location, and traverse "up" the location tree until we find an owner + """ + + for loc in self.get_ancestors(include_self=True, ascending=True): + if loc.owner is not None: + return loc.owner + + return None + + def check_ownership(self, user): + """ + Check if the user "owns" (is one of the owners of) the location. + """ + + # Superuser accounts automatically "own" everything + if user.is_superuser: + return True + + ownership_enabled = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if not ownership_enabled: + # Location ownership function is not enabled, so return True + return True + + owner = self.get_location_owner() + + if owner is None: + # No owner set, for this location or any location above + # So, no ownership checks to perform! + return True + + return user in owner.get_related_owners(include_group=True) + def get_absolute_url(self): return reverse('stock-location-detail', kwargs={'pk': self.id}) diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index c07e4f8162..68a4cd7aed 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -20,7 +20,6 @@ {% endblock %} {% block actions %} -{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} {% if location and user.is_staff and roles.stock_location.change %} @@ -40,7 +39,7 @@ -{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %} +{% if user_owns_location %} {% if roles.stock.change %}
{% endif %} -{% endif %} {% endblock %} {% block details_left %} @@ -108,23 +105,23 @@ {% trans "Top level stock location" %} {% endif %} + {% if ownership_enabled and location_owner %} + + + {% trans "Location Owner" %} + + {{ location_owner }} + {% if not user_owns_location %} + + {% trans "Read Only" %} + + {% endif %} + + + {% endif %} {% endblock details_left %} -{% block details_below %} -{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} -{% if owner_control.value == "True" %} - {% authorized_owners location.owner as owners %} - - {% if location and not user in owners and not user.is_superuser %} -
- {% trans "You are not in the list of owners of this location. This stock location cannot be edited." %}
-
- {% endif %} -{% endif %} - -{% endblock details_below %} - {% block details_right %} {% if location %} diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 9aa70255b1..f9b7dfb6d0 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -63,6 +63,11 @@ class StockIndex(InvenTreeRoleMixin, ListView): context['loc_count'] = StockLocation.objects.count() context['stock_count'] = StockItem.objects.count() + # No 'ownership' checks are necessary for the top-level StockLocation view + context['user_owns_location'] = True + context['location_owner'] = None + context['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + return context @@ -76,6 +81,16 @@ class StockLocationDetail(InvenTreeRoleMixin, DetailView): queryset = StockLocation.objects.all() model = StockLocation + def get_context_data(self, **kwargs): + + context = super().get_context_data(**kwargs) + + context['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + context['location_owner'] = context['location'].get_location_owner() + context['user_owns_location'] = context['location'].check_ownership(self.request.user) + + return context + class StockItemDetail(InvenTreeRoleMixin, DetailView): """ From 7c26d8f71dc1700c88cf96ef969f6f19af637ac7 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 25 Feb 2022 22:58:00 +1100 Subject: [PATCH 3/4] Refactor ownership for StockItem model --- InvenTree/stock/models.py | 42 +++++++++++++++++++ InvenTree/stock/templates/stock/item.html | 9 +--- .../stock/templates/stock/item_base.html | 36 +++++----------- InvenTree/stock/templates/stock/location.html | 2 +- InvenTree/stock/views.py | 4 ++ 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 83e7a0db8d..a3b7383ee8 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -651,6 +651,48 @@ class StockItem(MPTTModel): help_text=_('Select Owner'), related_name='stock_items') + def get_item_owner(self): + """ + Return the closest "owner" for this StockItem. + + - If the item has an owner set, return that + - If the item is "in stock", check the StockLocation + - Otherwise, return None + """ + + if self.owner is not None: + return self.owner + + if self.in_stock and self.location is not None: + loc_owner = self.location.get_location_owner() + + if loc_owner: + return loc_owner + + return None + + def check_ownership(self, user): + """ + Check if the user "owns" (or is one of the owners of) the item + """ + + # Superuser accounts automatically "own" everything + if user.is_superuser: + return True + + ownership_enabled = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + + if not ownership_enabled: + # Location ownership function is not enabled, so return True + return True + + owner = self.get_item_owner() + + if owner is None: + return True + + return user in owner.get_related_owners(include_group=True) + def is_stale(self): """ Returns True if this Stock item is "stale". diff --git a/InvenTree/stock/templates/stock/item.html b/InvenTree/stock/templates/stock/item.html index f42a768069..113fefb9b1 100644 --- a/InvenTree/stock/templates/stock/item.html +++ b/InvenTree/stock/templates/stock/item.html @@ -18,18 +18,11 @@

{% trans "Stock Tracking Information" %}

{% include "spacer.html" %}
- {% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} - {% if owner_control.value == "True" %} - {% authorized_owners item.owner as owners %} - {% endif %} - - {% if owner_control.value == "False" or owner_control.value == "True" and user in owners %} - {% if roles.stock.change and not item.is_building %} + {% if user_owns_item and roles.stock.change and not item.is_building %} {% endif %} - {% endif %}
diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 9979468357..c52d101afb 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -59,14 +59,7 @@ - - -{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} -{% if owner_control.value == "True" %} - {% authorized_owners item.owner as owners %} -{% endif %} - -{% if owner_control.value == "False" or owner_control.value == "True" and user in owners or user.is_superuser %} +{% if user_owns_item %} {% if roles.stock.change and not item.is_building %}
@@ -219,24 +212,8 @@
-{% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} -{% if owner_control.value == "True" %} - {% authorized_owners item.owner as owners %} -{% endif %} -
- {% setting_object 'STOCK_OWNERSHIP_CONTROL' as owner_control %} - {% if owner_control.value == "True" %} - {% authorized_owners item.owner as owners %} - - {% if not user in owners and not user.is_superuser %} -
- {% trans "You are not in the list of owners of this item. This stock item cannot be edited." %}
-
- {% endif %} - {% endif %} - {% if item.is_building %}
{% trans "This stock item is in production and cannot be edited." %}
@@ -419,11 +396,18 @@ {% endif %} - {% if item.owner %} + {% if ownership_enabled and item_owner %} {% trans "Owner" %} - {{ item.owner }} + + {{ item_owner }} + {% if not user_owns_item %} + + {% trans "Read only" %} + + {% endif %} + {% endif %} diff --git a/InvenTree/stock/templates/stock/location.html b/InvenTree/stock/templates/stock/location.html index 68a4cd7aed..4c98db529b 100644 --- a/InvenTree/stock/templates/stock/location.html +++ b/InvenTree/stock/templates/stock/location.html @@ -113,7 +113,7 @@ {{ location_owner }} {% if not user_owns_location %} - {% trans "Read Only" %} + {% trans "Read only" %} {% endif %} diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index f9b7dfb6d0..d263a498de 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -141,6 +141,10 @@ class StockItemDetail(InvenTreeRoleMixin, DetailView): # We only support integer serial number progression pass + data['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL') + data['item_owner'] = self.object.get_item_owner() + data['user_owns_item'] = self.object.check_ownership(self.request.user) + return data def get(self, request, *args, **kwargs): From 65e4ee5793563d0f451b9c9ac6e37ec21e010bee Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 25 Feb 2022 23:01:02 +1100 Subject: [PATCH 4/4] PEP style fixes --- InvenTree/stock/models.py | 6 +++--- InvenTree/stock/views.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index a3b7383ee8..64b47da6d3 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -74,8 +74,8 @@ class StockLocation(InvenTreeTree): if loc.owner is not None: return loc.owner - return None - + return None + def check_ownership(self, user): """ Check if the user "owns" (is one of the owners of) the location. @@ -97,7 +97,7 @@ class StockLocation(InvenTreeTree): # No owner set, for this location or any location above # So, no ownership checks to perform! return True - + return user in owner.get_related_owners(include_group=True) def get_absolute_url(self): diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index d263a498de..d1fde25b0a 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -82,7 +82,7 @@ class StockLocationDetail(InvenTreeRoleMixin, DetailView): model = StockLocation def get_context_data(self, **kwargs): - + context = super().get_context_data(**kwargs) context['ownership_enabled'] = common.models.InvenTreeSetting.get_setting('STOCK_OWNERSHIP_CONTROL')