From b850beb687def415973239f40a568a207ee72392 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 Apr 2020 21:24:36 +1000 Subject: [PATCH 1/5] Add ability to filter by 'starred' status --- .../InvenTree/static/script/inventree/part.js | 2 +- InvenTree/part/api.py | 54 +++++++++++++------ InvenTree/part/templates/part/category.html | 16 +++--- 3 files changed, 46 insertions(+), 26 deletions(-) diff --git a/InvenTree/InvenTree/static/script/inventree/part.js b/InvenTree/InvenTree/static/script/inventree/part.js index 05b209b9b8..927afca2e6 100644 --- a/InvenTree/InvenTree/static/script/inventree/part.js +++ b/InvenTree/InvenTree/static/script/inventree/part.js @@ -87,7 +87,7 @@ function loadPartTable(table, url, options={}) { * buttons: If provided, link buttons to selection status of this table */ - var params = options.parms || {}; + var params = options.params || {}; var filters = loadTableFilters("parts"); diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 7d193fa1ee..629136af05 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -153,6 +153,7 @@ class PartList(generics.ListCreateAPIView): The Part object list can be filtered by: - category: Filter by PartCategory reference - cascade: If true, include parts from sub-categories + - starred: Is the part "starred" by the current user? - is_template: Is the part a template part? - variant_of: Filter by variant_of Part reference - assembly: Filter by assembly field @@ -295,31 +296,50 @@ class PartList(generics.ListCreateAPIView): def get_queryset(self): - # Does the user wish to filter by category? - cat_id = self.request.query_params.get('category', None) # Start with all objects parts_list = Part.objects.all() - cascade = str2bool(self.request.query_params.get('cascade', False)) + # Filter by 'starred' parts? + starred = str2bool(self.request.query_params.get('starred', None)) + + if starred is not None: + starred_parts = [star.part.pk for star in self.request.user.starred_parts.all()] + + if starred: + parts_list = parts_list.filter(pk__in=starred_parts) + else: + parts_list = parts_list.exclude(pk__in=starred_parts) + + cascade = str2bool(self.request.query_params.get('cascade', None)) + + # Does the user wish to filter by category? + cat_id = self.request.query_params.get('category', None) if cat_id is None: - # Top-level parts - if not cascade: - parts_list = parts_list.filter(category=None) - + # No category filtering if category is not specified + pass + else: - try: - category = PartCategory.objects.get(pk=cat_id) + # Category has been specified! + if isNull(cat_id): + # A 'null' category is the top-level category + if cascade is False: + # Do not cascade, only list parts in the top-level category + parts_list = parts_list.filter(category=None) - # If '?cascade=true' then include parts which exist in sub-categories - if cascade: - parts_list = parts_list.filter(category__in=category.getUniqueChildren()) - # Just return parts directly in the requested category - else: - parts_list = parts_list.filter(category=cat_id) - except (ValueError, PartCategory.DoesNotExist): - pass + else: + try: + category = PartCategory.objects.get(pk=cat_id) + + # If '?cascade=true' then include parts which exist in sub-categories + if cascade: + parts_list = parts_list.filter(category__in=category.getUniqueChildren()) + # Just return parts directly in the requested category + else: + parts_list = parts_list.filter(category=cat_id) + except (ValueError, PartCategory.DoesNotExist): + pass # Ensure that related models are pre-loaded to reduce DB trips parts_list = self.get_serializer_class().setup_eager_loading(parts_list) diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index cc663a63c4..63a60bd71e 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -200,11 +200,11 @@ {% if category %} $("#cat-edit").click(function () { launchModalForm( - "{% url 'category-edit' category.id %}", - { - reload: true - }, - ); + "{% url 'category-edit' category.id %}", + { + reload: true + }, + ); return false; }); @@ -227,9 +227,9 @@ "#part-table", "{% url 'api-part-list' %}", { - query: { - {% if category %} - category: {{ category.id }}, + params: { + {% if category %}category: {{ category.id }}, + {% else %}category: "null", {% endif %} }, buttons: ['#part-options'], From 124fab3eeedea4b50ffea0d527b249868390e470 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 Apr 2020 21:30:17 +1000 Subject: [PATCH 2/5] Display a part as 'starred' in the part table --- InvenTree/InvenTree/static/script/inventree/part.js | 4 ++++ InvenTree/part/api.py | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/InvenTree/InvenTree/static/script/inventree/part.js b/InvenTree/InvenTree/static/script/inventree/part.js index 927afca2e6..05328dbbd8 100644 --- a/InvenTree/InvenTree/static/script/inventree/part.js +++ b/InvenTree/InvenTree/static/script/inventree/part.js @@ -147,6 +147,10 @@ function loadPartTable(table, url, options={}) { display += ``; } + if (row.starred) { + display += ``; + } + /* if (row.component) { display = display + ``; diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 629136af05..6615b4df43 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -258,12 +258,18 @@ class PartList(generics.ListCreateAPIView): # Filter items which have an 'in_stock' level higher than 'minimum_stock' data = data.filter(Q(in_stock__gte=F('minimum_stock'))) + # Get a list of the parts that this user has starred + starred_parts = [star.part.pk for star in self.request.user.starred_parts.all()] + # Reduce the number of lookups we need to do for the part categories categories = {} for item in data: if item['image']: + # Is this part 'starred' for the current user? + item['starred'] = item['pk'] in starred_parts + img = item['image'] # Use the 'thumbnail' image here instead of the full-size image From 90ac3a5a8ac3ce574dd35f9a4fbba27399f1c0b3 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 Apr 2020 21:30:34 +1000 Subject: [PATCH 3/5] Add custom user filter for 'starred' status --- InvenTree/templates/table_filters.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/InvenTree/templates/table_filters.html b/InvenTree/templates/table_filters.html index e3dd78be19..f976e977a6 100644 --- a/InvenTree/templates/table_filters.html +++ b/InvenTree/templates/table_filters.html @@ -89,6 +89,10 @@ function getAvailableTableFilters(tableKey) { type: 'bool', title: '{% trans "Component" %}', }, + starred: { + type: 'bool', + title: '{% trans "Starred" %}', + }, }; } From 0e55911a6ba38f47fdea6123eb87d6e6f16f5f8f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 Apr 2020 22:07:14 +1000 Subject: [PATCH 4/5] Index page rendering is now a lot faster - Hide some elements which are currently very expensive to compute - --- InvenTree/InvenTree/views.py | 7 ++-- InvenTree/part/api.py | 4 +- InvenTree/templates/InvenTree/index.html | 38 ++++++++++++------- InvenTree/templates/InvenTree/low_stock.html | 15 ++++++++ .../templates/InvenTree/parts_to_order.html | 15 -------- .../templates/InvenTree/starred_parts.html | 12 +++--- InvenTree/templates/base.html | 1 + 7 files changed, 53 insertions(+), 39 deletions(-) create mode 100644 InvenTree/templates/InvenTree/low_stock.html delete mode 100644 InvenTree/templates/InvenTree/parts_to_order.html diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index e1258385a5..59833d3e6b 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -494,15 +494,16 @@ class IndexView(TemplateView): context = super(TemplateView, self).get_context_data(**kwargs) - context['starred'] = [star.part for star in self.request.user.starred_parts.all()] + # TODO - Re-implement this when a less expensive method is worked out + # context['starred'] = [star.part for star in self.request.user.starred_parts.all()] # Generate a list of orderable parts which have stock below their minimum values # TODO - Is there a less expensive way to get these from the database - context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()] + # context['to_order'] = [part for part in Part.objects.filter(purchaseable=True) if part.need_to_restock()] # Generate a list of assembly parts which have stock below their minimum values # TODO - Is there a less expensive way to get these from the database - context['to_build'] = [part for part in Part.objects.filter(assembly=True) if part.need_to_restock()] + # context['to_build'] = [part for part in Part.objects.filter(assembly=True) if part.need_to_restock()] return context diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 6615b4df43..e36d4a568b 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -301,7 +301,9 @@ class PartList(generics.ListCreateAPIView): return Response(data) def get_queryset(self): - + """ + Implement custom filtering for the Part list API + """ # Start with all objects parts_list = Part.objects.all() diff --git a/InvenTree/templates/InvenTree/index.html b/InvenTree/templates/InvenTree/index.html index c4eb5990cc..570378e55d 100644 --- a/InvenTree/templates/InvenTree/index.html +++ b/InvenTree/templates/InvenTree/index.html @@ -9,13 +9,7 @@ InvenTree | Index
{% include "InvenTree/starred_parts.html" with collapse_id="starred" %} -{% if to_order %} -{% include "InvenTree/parts_to_order.html" with collapse_id="order" %} -{% endif %} - -{% if to_build %} -{% include "InvenTree/parts_to_build.html" with collapse_id="build" %} -{% endif %} +{% include "InvenTree/low_stock.html" with collapse_id="order" %} {% endblock %} @@ -25,15 +19,31 @@ InvenTree | Index {% block js_ready %} -console.log("abcde?"); - {{ block.super }} -//TODO: These calls to bootstrapTable() are failing, for some reason? -//$("#to-build-table").bootstrapTable(); -//$("#to-order-table").bootstrapTable(); -//$("#starred-parts-table").bootstrapTable(); +loadPartTable("#starred-parts-table", "{% url 'api-part-list' %}", { + params: { + "starred": true, + } +}); + +loadPartTable("#low-stock-table", "{% url 'api-part-list' %}", { + params: { + "low_stock": true, + } +}); + +$("#starred-parts-table").on('load-success.bs.table', function() { + var count = $("#starred-parts-table").bootstrapTable('getData').length; + + $("#starred-parts-count").html(count); +}); + +$("#low-stock-table").on('load-success.bs.table', function() { + var count = $("#low-stock-table").bootstrapTable('getData').length; + + $("#low-stock-count").html(count); +}); -console.log("Got to here..."); {% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/InvenTree/low_stock.html b/InvenTree/templates/InvenTree/low_stock.html new file mode 100644 index 0000000000..edafab1756 --- /dev/null +++ b/InvenTree/templates/InvenTree/low_stock.html @@ -0,0 +1,15 @@ +{% extends "collapse.html" %} + +{% load i18n %} + +{% block collapse_title %} + +{% trans "Low Stock" %}0 +{% endblock %} + +{% block collapse_content %} + + +
+ +{% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/InvenTree/parts_to_order.html b/InvenTree/templates/InvenTree/parts_to_order.html deleted file mode 100644 index 5d2c3472b4..0000000000 --- a/InvenTree/templates/InvenTree/parts_to_order.html +++ /dev/null @@ -1,15 +0,0 @@ -{% extends "collapse.html" %} -{% block collapse_title %} - -Parts to Order{{ to_order | length }} -{% endblock %} - -{% block collapse_heading %} -There are {{ to_order | length }} parts which need to be ordered. -{% endblock %} - -{% block collapse_content %} - -{% include "required_part_table.html" with parts=to_order table_id="to-order-table" %} - -{% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/InvenTree/starred_parts.html b/InvenTree/templates/InvenTree/starred_parts.html index 091afde064..f13987e3c5 100644 --- a/InvenTree/templates/InvenTree/starred_parts.html +++ b/InvenTree/templates/InvenTree/starred_parts.html @@ -1,15 +1,15 @@ {% extends "collapse.html" %} + +{% load i18n %} + {% block collapse_title %} -Starred Parts{{ starred | length }} -{% endblock %} - -{% block collapse_heading %} -You have {{ starred | length }} favourite parts +{% trans "Starred Parts" %}0 {% endblock %} {% block collapse_content %} -{% include "required_part_table.html" with parts=starred table_id="starred-parts-table" %} + +
{% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html index 3cae9fd37b..8559e6d5f1 100644 --- a/InvenTree/templates/base.html +++ b/InvenTree/templates/base.html @@ -102,6 +102,7 @@ InvenTree + From 47530b7d2a25afd1ebad28a0cc51aa37bad00846 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 13 Apr 2020 22:21:20 +1000 Subject: [PATCH 5/5] Improvements for "SupplierPartCreate" form --- .../templates/company/detail_part.html | 19 ++++++++++++++++--- InvenTree/company/views.py | 19 +++++++++++-------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/InvenTree/company/templates/company/detail_part.html b/InvenTree/company/templates/company/detail_part.html index 537f7b07c3..2364f36b61 100644 --- a/InvenTree/company/templates/company/detail_part.html +++ b/InvenTree/company/templates/company/detail_part.html @@ -33,16 +33,29 @@ "{% url 'supplier-part-create' %}", { data: { - supplier: {{ company.id }} + {% if company.is_supplier %}supplier: {{ company.id }},{% endif %} + {% if company.is_manufacturer %}manufacturer: {{ company.id }},{% endif %} }, reload: true, secondary: [ { field: 'part', - label: 'New Part', - title: 'Create New Part', + label: '{% trans "New Part" %}', + title: '{% trans "Create new Part" %}', url: "{% url 'part-create' %}" }, + { + field: 'supplier', + label: "{% trans 'New Supplier' %}", + title: "{% trans 'Create new Supplier' %}", + url: "{% url 'supplier-create' %}", + }, + { + field: 'manufacturer', + label: '{% trans "New Manufacturer" %}', + title: '{% trans "Create new Manufacturer" %}', + url: "{% url 'manufacturer-create' %}", + }, ] }); }); diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index d1fc9b643f..ae88629505 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -273,10 +273,6 @@ class SupplierPartCreate(AjaxCreateView): Hide some fields if they are not appropriate in context """ form = super(AjaxCreateView, self).get_form() - - if form.initial.get('supplier', None): - # Hide the supplier field - form.fields['supplier'].widget = HiddenInput() if form.initial.get('part', None): # Hide the part field @@ -292,20 +288,27 @@ class SupplierPartCreate(AjaxCreateView): """ initials = super(SupplierPartCreate, self).get_initial().copy() + manufacturer_id = self.get_param('manufacturer') supplier_id = self.get_param('supplier') part_id = self.get_param('part') if supplier_id: try: initials['supplier'] = Company.objects.get(pk=supplier_id) - except Company.DoesNotExist: - initials['supplier'] = None + except (ValueError, Company.DoesNotExist): + pass + + if manufacturer_id: + try: + initials['manufacturer'] = Company.objects.get(pk=manufacturer_id) + except (ValueError, Company.DoesNotExist): + pass if part_id: try: initials['part'] = Part.objects.get(pk=part_id) - except Part.DoesNotExist: - initials['part'] = None + except (ValueError, Part.DoesNotExist): + pass return initials