From 2c5bb6b126c73423eb4357a19a058f114b0ddf85 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 9 May 2019 18:43:22 +1000 Subject: [PATCH 1/7] Hide supplier_part field if the part cannot be purchased --- InvenTree/stock/views.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 314d3c99f6..cd9fb615af 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -143,9 +143,13 @@ class StockItemEdit(AjaxUpdateView): item = self.get_object() - query = form.fields['supplier_part'].queryset - query = query.filter(part=item.part.id) - form.fields['supplier_part'].queryset = query + # If the part cannot be purchased, hide the supplier_part field + if not item.part.purchaseable: + form.fields['supplier_part'].widget = HiddenInput() + else: + query = form.fields['supplier_part'].queryset + query = query.filter(part=item.part.id) + form.fields['supplier_part'].queryset = query return form @@ -206,6 +210,11 @@ class StockItemCreate(AjaxCreateView): part = Part.objects.get(id=part_id) parts = form.fields['supplier_part'].queryset parts = parts.filter(part=part.id) + + # If the part is NOT purchaseable, hide the supplier_part field + if not part.purchaseable: + form.fields['supplier_part'].widget = HiddenInput() + form.fields['supplier_part'].queryset = parts # If there is one (and only one) supplier part available, pre-select it From 45223fb6074c517f0f19dc0da0fe77d4093ceb8c Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 9 May 2019 20:30:23 +1000 Subject: [PATCH 2/7] Display count badges in sidenav tree Uses the 'tags' parameter as according to the docs - https://github.com/jonmiles/bootstrap-treeview - Part - Stock --- InvenTree/InvenTree/models.py | 11 +++++++++++ InvenTree/InvenTree/views.py | 5 +++++ InvenTree/part/models.py | 11 +++++------ InvenTree/static/script/inventree/sidenav.js | 3 ++- InvenTree/stock/models.py | 12 ++++++++++++ 5 files changed, 35 insertions(+), 7 deletions(-) diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index c9c9e2966b..a28b27a5eb 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -41,6 +41,17 @@ class InvenTreeTree(models.Model): null=True, related_name='children') + @property + def item_count(self): + """ Return the number of items which exist *under* this node in the tree. + + Here an 'item' is considered to be the 'leaf' at the end of each branch, + and the exact nature here will depend on the class implementation. + + The default implementation returns zero + """ + return 0 + def getUniqueParents(self, unique=None): """ Return a flat set of all parent items that exist above this node. If any parents are repeated (which would be very bad!), the process is halted diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index d2d8e48e35..967b3b16c4 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -33,6 +33,7 @@ class TreeSerializer(views.APIView): data = { 'text': item.name, 'href': item.get_absolute_url(), + 'tags': [item.item_count], } if item.has_children: @@ -51,12 +52,16 @@ class TreeSerializer(views.APIView): nodes = [] + top_count = 0 + for item in top_items: nodes.append(self.itemToJson(item)) + top_count += item.item_count top = { 'text': self.title, 'nodes': nodes, + 'tags': [top_count], } response = { diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index deaf2a799a..35a2e8336d 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -47,18 +47,17 @@ class PartCategory(InvenTreeTree): verbose_name = "Part Category" verbose_name_plural = "Part Categories" + @property + def item_count(self): + return self.partcount + @property def partcount(self): """ Return the total part count under this category (including children of child categories) """ - count = self.parts.count() - - for child in self.children.all(): - count += child.partcount - - return count + return len(Part.objects.filter(category__in=self.getUniqueChildren())) @property def has_parts(self): diff --git a/InvenTree/static/script/inventree/sidenav.js b/InvenTree/static/script/inventree/sidenav.js index 1beefd0ee1..93ee6f05ff 100644 --- a/InvenTree/static/script/inventree/sidenav.js +++ b/InvenTree/static/script/inventree/sidenav.js @@ -9,7 +9,8 @@ function loadTree(url, tree, data) { if (response.tree) { $(tree).treeview({ data: response.tree, - enableLinks: true + enableLinks: true, + showTags: true, }); var saved_exp = sessionStorage.getItem('inventree-sidenav-expanded-items').split(","); diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 537397f66b..f5ecb1be33 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -48,6 +48,18 @@ class StockLocation(InvenTreeTree): } ) + @property + def stock_item_count(self): + """ Return the number of StockItem objects which live in or under this category + """ + + return len(StockItem.objects.filter(location__in=self.getUniqueChildren())) + + @property + def item_count(self): + + return self.stock_item_count + @receiver(pre_delete, sender=StockLocation, dispatch_uid='stocklocation_delete_log') def before_delete_stock_location(sender, instance, using, **kwargs): From bff0f30b13d4ec6cdc48d10f13a152c0ba8b5505 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 9 May 2019 21:41:44 +1000 Subject: [PATCH 3/7] Save tree state 'per tree' - Separate save state for Part and Stock tree --- InvenTree/part/models.py | 3 +- .../part/templates/part/part_app_base.html | 6 +++- InvenTree/static/css/inventree.css | 8 +++++ InvenTree/static/script/inventree/sidenav.js | 36 +++++++++++++++---- .../stock/templates/stock/stock_app_base.html | 7 +++- 5 files changed, 51 insertions(+), 9 deletions(-) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 35a2e8336d..86d65b23b2 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -57,7 +57,8 @@ class PartCategory(InvenTreeTree): (including children of child categories) """ - return len(Part.objects.filter(category__in=self.getUniqueChildren())) + return len(Part.objects.filter(category__in=self.getUniqueChildren(), + active=True)) @property def has_parts(self): diff --git a/InvenTree/part/templates/part/part_app_base.html b/InvenTree/part/templates/part/part_app_base.html index 43b12377f6..cb1fbdef90 100644 --- a/InvenTree/part/templates/part/part_app_base.html +++ b/InvenTree/part/templates/part/part_app_base.html @@ -34,7 +34,11 @@ InvenTree | Part List {% block js_ready %} {{ block.super }} loadTree("{% url 'api-part-tree' %}", - "#part-tree"); + "#part-tree", + { + name: 'part', + } + ); $("#toggle-part-tree").click(function() { toggleSideNav("#sidenav"); diff --git a/InvenTree/static/css/inventree.css b/InvenTree/static/css/inventree.css index a6272dc76f..8b1458ef06 100644 --- a/InvenTree/static/css/inventree.css +++ b/InvenTree/static/css/inventree.css @@ -19,6 +19,14 @@ font-size: 11px; } +.treeview .badge { + font-size: 10px; +} + +.treeview .list-group-item { + padding: 6px 12px; +} + /* Force select2 elements in modal forms to be full width */ .select-full-width { width: 100%; diff --git a/InvenTree/static/script/inventree/sidenav.js b/InvenTree/static/script/inventree/sidenav.js index 93ee6f05ff..f3f0b25171 100644 --- a/InvenTree/static/script/inventree/sidenav.js +++ b/InvenTree/static/script/inventree/sidenav.js @@ -1,4 +1,26 @@ -function loadTree(url, tree, data) { +function loadTree(url, tree, options={}) { + /* Load the side-nav tree view + + Args: + url: URL to request tree data + tree: html ref to treeview + options: + data: data object to pass to the AJAX request + selected: ID of currently selected item + name: name of the tree + */ + + var data = {}; + + if (options.data) { + data = options.data; + } + + var key = "inventree-sidenav-items-"; + + if (options.name) { + key += options.name; + } $.ajax({ url: url, @@ -13,11 +35,13 @@ function loadTree(url, tree, data) { showTags: true, }); - var saved_exp = sessionStorage.getItem('inventree-sidenav-expanded-items').split(","); + if (sessionStorage.getItem(key)) { + var saved_exp = sessionStorage.getItem(key).split(","); - // Automatically expand the desired notes - for (var q = 0; q < saved_exp.length; q++) { - $(tree).treeview('expandNode', parseInt(saved_exp[q])); + // Automatically expand the desired notes + for (var q = 0; q < saved_exp.length; q++) { + $(tree).treeview('expandNode', parseInt(saved_exp[q])); + } } // Setup a callback whenever a node is toggled @@ -33,7 +57,7 @@ function loadTree(url, tree, data) { } // Save the expanded nodes - sessionStorage.setItem('inventree-sidenav-expanded-items', exp); + sessionStorage.setItem(key, exp); }); } }, diff --git a/InvenTree/stock/templates/stock/stock_app_base.html b/InvenTree/stock/templates/stock/stock_app_base.html index 26393626bb..805147f6ad 100644 --- a/InvenTree/stock/templates/stock/stock_app_base.html +++ b/InvenTree/stock/templates/stock/stock_app_base.html @@ -33,7 +33,12 @@ InvenTree | Stock initSideNav(); {{ block.super }} loadTree("{% url 'api-stock-tree' %}", - "#stock-tree"); + "#stock-tree", + { + name: 'stock', + selected: 'elab', + } + ); $("#toggle-stock-tree").click(function() { toggleSideNav("#sidenav"); From 34225193faf74d79b0870b6cb6d6ffe12d7d4dab Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Thu, 9 May 2019 21:46:11 +1000 Subject: [PATCH 4/7] Improve tree toggle icon --- InvenTree/part/templates/part/cat_link.html | 2 +- InvenTree/stock/templates/stock/loc_link.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/cat_link.html b/InvenTree/part/templates/part/cat_link.html index 181f10ebb6..1cb16d530e 100644 --- a/InvenTree/part/templates/part/cat_link.html +++ b/InvenTree/part/templates/part/cat_link.html @@ -1,7 +1,7 @@