From 013a85e6f78c9faad2e427d237f75c3ed28c7a6a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 13:53:11 +1000 Subject: [PATCH 1/8] Better dispaly of stock status - Send status text in JSON - Only display status if it is not "OK" --- InvenTree/static/script/inventree/stock.js | 5 ++++- InvenTree/stock/api.py | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/InvenTree/static/script/inventree/stock.js b/InvenTree/static/script/inventree/stock.js index 65b0c55da5..facbaa65f5 100644 --- a/InvenTree/static/script/inventree/stock.js +++ b/InvenTree/static/script/inventree/stock.js @@ -153,7 +153,10 @@ function loadStockTable(table, options) { var text = renderLink(val, '/stock/item/' + row.pk + '/'); - text = text + "" + row.status_text + ""; + if (row.status_text != 'OK') { + text = text + "" + row.status_text + ""; + } + return text; } }, diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index c1c2b8e1cf..dd899ebe0a 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -306,6 +306,8 @@ class StockList(generics.ListCreateAPIView): else: item['location__path'] = None + item['status_text'] = StockItem.ITEM_STATUS_CODES[item['status']] + return Response(data) def get_queryset(self): From 6e7f354a238ec2d358b588d01770270d8eebfe7e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 13:53:54 +1000 Subject: [PATCH 2/8] Add a 'LOST' status code for stockitem --- InvenTree/stock/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 4efa347ecd..4434741186 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -260,12 +260,14 @@ class StockItem(models.Model): ITEM_ATTENTION = 50 ITEM_DAMAGED = 55 ITEM_DESTROYED = 60 + ITEM_LOST = 70 ITEM_STATUS_CODES = { ITEM_OK: _("OK"), ITEM_ATTENTION: _("Attention needed"), ITEM_DAMAGED: _("Damaged"), - ITEM_DESTROYED: _("Destroyed") + ITEM_DESTROYED: _("Destroyed"), + ITEM_LOST: _("Lost") } status = models.PositiveIntegerField( From 3427f81a346c76af35cd5e32f4f6902f80720cb9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 14:47:40 +1000 Subject: [PATCH 3/8] Improve part information display - Better terminology --- InvenTree/part/templates/part/detail.html | 58 +++++++++++++---------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 9765a2574e..4522d47236 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -90,30 +90,6 @@ Units {{ part.units }} - - -
- - - - - - - - - - - - - - - - - - - - - {% if part.minimum_stock > 0 %} @@ -122,6 +98,40 @@ {% endif %}
Buildable{% include "yesnolabel.html" with value=part.buildable %}
Consumable{% include "yesnolabel.html" with value=part.consumable %}
Trackable{% include "yesnolabel.html" with value=part.trackable %}
Purchaseable{% include "yesnolabel.html" with value=part.purchaseable %}
Salable{% include "yesnolabel.html" with value=part.salable %}
Minimum Stock
+
+ + {% if part.buildable %} + + + + + {% endif %} + {% if part.consumable %} + + + + + {% endif %} + {% if part.trackable %} + + + + + {% endif %} + {% if part.purchaseable %} + + + + + {% endif %} + {% if part.salable %} + + + + + {% endif %} +
AssemblyThis part can be assembled from other parts
ComponentThis part can be used in assemblies
TrackableStock for this part will be tracked by (serial or batch)
PurchaseableThis part can be purchased from external suppliers
SalableThis part can be sold to customers
+
{% if part.notes %} From fd2e2a71f9ba6c48308638881c63735e65dde450 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 19:15:05 +1000 Subject: [PATCH 4/8] Add search results for part-category and stock-location --- InvenTree/part/api.py | 2 +- InvenTree/stock/api.py | 5 ++ InvenTree/templates/InvenTree/search.html | 48 +++++++++++++++++++ .../InvenTree/search_part_category.html | 14 ++++++ .../InvenTree/search_stock_location.html | 14 ++++++ InvenTree/templates/InvenTree/searching.html | 2 +- 6 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 InvenTree/templates/InvenTree/search_part_category.html create mode 100644 InvenTree/templates/InvenTree/search_stock_location.html diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 500b4a2bdf..acfb02144d 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -51,7 +51,7 @@ class CategoryList(generics.ListCreateAPIView): filter_backends = [ DjangoFilterBackend, - # filters.SearchFilter, + filters.SearchFilter, filters.OrderingFilter, ] diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index dd899ebe0a..b1c746cdd6 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -236,6 +236,11 @@ class StockLocationList(generics.ListCreateAPIView): 'parent', ] + search_fields = [ + 'name', + 'description', + ] + class StockList(generics.ListCreateAPIView): """ API endpoint for list view of Stock objects diff --git a/InvenTree/templates/InvenTree/search.html b/InvenTree/templates/InvenTree/search.html index f8631dfbd4..2f88d3caed 100644 --- a/InvenTree/templates/InvenTree/search.html +++ b/InvenTree/templates/InvenTree/search.html @@ -17,10 +17,14 @@ InvenTree | Search Results


+{% include "InvenTree/search_part_category.html" with collapse_id="categories" %} + {% include "InvenTree/search_parts.html" with collapse_id='parts' %} {% include "InvenTree/search_supplier_parts.html" with collapse_id='supplier_parts' %} +{% include "InvenTree/search_stock_location.html" with collapse_id="locations" %} + {% endblock %} {% block js_load %} @@ -50,10 +54,54 @@ InvenTree | Search Results }); } + onSearchResults("#category-results-table", "#category-results-count"); + + onSearchResults("#location-results-table", "#location-results-count"); + onSearchResults('#part-results-table', '#part-result-count'); onSearchResults('#supplier-part-results-table', '#supplier-part-result-count'); + $("#category-results-table").bootstrapTable({ + url: "{% url 'api-part-category-list' %}", + queryParams: { + search: "{{ query }}", + }, + columns: [ + { + field: 'name', + title: 'Name', + formatter: function(value, row, index, field) { + return renderLink(value, '/part/category/' + row.pk + '/'); + }, + }, + { + field: 'description', + title: 'Description', + }, + ], + }); + + $("#location-results-table").bootstrapTable({ + url: "{% url 'api-location-list' %}", + queryParams: { + search: "{{ query }}", + }, + columns: [ + { + field: 'name', + title: 'Name', + formatter: function(value, row, index, field) { + return renderLink(value, '/stock/location/' + row.pk + '/'); + }, + }, + { + field: 'description', + title: 'Description', + }, + ], + }); + loadPartTable("#part-results-table", "{% url 'api-part-list' %}", { diff --git a/InvenTree/templates/InvenTree/search_part_category.html b/InvenTree/templates/InvenTree/search_part_category.html new file mode 100644 index 0000000000..899aca094c --- /dev/null +++ b/InvenTree/templates/InvenTree/search_part_category.html @@ -0,0 +1,14 @@ +{% extends "collapse.html" %} + +{% block collapse_title %} +

Part Categories

+{% endblock %} + +{% block collapse_heading %} +

{% include "InvenTree/searching.html" %}

+{% endblock %} + +{% block collapse_content %} + +
+{% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/InvenTree/search_stock_location.html b/InvenTree/templates/InvenTree/search_stock_location.html new file mode 100644 index 0000000000..923635b13e --- /dev/null +++ b/InvenTree/templates/InvenTree/search_stock_location.html @@ -0,0 +1,14 @@ +{% extends "collapse.html" %} + +{% block collapse_title %} +

Stock Locations

+{% endblock %} + +{% block collapse_heading %} +

{% include "InvenTree/searching.html" %}

+{% endblock %} + +{% block collapse_content %} + +
+{% endblock %} \ No newline at end of file diff --git a/InvenTree/templates/InvenTree/searching.html b/InvenTree/templates/InvenTree/searching.html index 9d111fe257..5821515ad7 100644 --- a/InvenTree/templates/InvenTree/searching.html +++ b/InvenTree/templates/InvenTree/searching.html @@ -1 +1 @@ - Searching... \ No newline at end of file + Searching \ No newline at end of file From ba26acd48722d037e89ca04b6d28828ae199e30e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 19:46:30 +1000 Subject: [PATCH 5/8] Rename some fields - Oops didn't think that through, gotta go through and fix the data now... --- InvenTree/InvenTree/views.py | 4 +- InvenTree/part/api.py | 4 +- InvenTree/part/fixtures/part.yaml | 2 +- InvenTree/part/forms.py | 4 +- .../migrations/0007_auto_20190602_1944.py | 42 +++++++++++++++++++ InvenTree/part/models.py | 12 +++--- InvenTree/part/serializers.py | 4 +- InvenTree/part/templates/part/detail.html | 4 +- InvenTree/part/templates/part/part_base.html | 2 +- InvenTree/part/templates/part/tabs.html | 4 +- InvenTree/part/test_api.py | 2 +- .../static/script/inventree/inventree.js | 4 ++ .../migrations/0005_auto_20190602_1944.py | 19 +++++++++ InvenTree/templates/InvenTree/search.html | 11 +++++ 14 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 InvenTree/part/migrations/0007_auto_20190602_1944.py create mode 100644 InvenTree/stock/migrations/0005_auto_20190602_1944.py diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index 338e664252..71f87ffd19 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -445,9 +445,9 @@ class IndexView(TemplateView): # 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()] - # Generate a list of buildable parts which have stock below their minimum values + # 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(buildable=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 acfb02144d..d0beee54ce 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -129,8 +129,8 @@ class PartList(generics.ListCreateAPIView): filter_fields = [ 'is_template', 'variant_of', - 'buildable', - 'consumable', + 'assembly', + 'component', 'trackable', 'purchaseable', 'salable', diff --git a/InvenTree/part/fixtures/part.yaml b/InvenTree/part/fixtures/part.yaml index 632a265e23..7d669cf49d 100644 --- a/InvenTree/part/fixtures/part.yaml +++ b/InvenTree/part/fixtures/part.yaml @@ -57,5 +57,5 @@ fields: name: 'Bob' description: 'Can we build it?' - buildable: true + assembly: true \ No newline at end of file diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index a2ec613429..841622b865 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -101,8 +101,8 @@ class EditPartForm(HelperForm): 'default_supplier', 'units', 'minimum_stock', - 'buildable', - 'consumable', + 'assembly', + 'component', 'trackable', 'purchaseable', 'salable', diff --git a/InvenTree/part/migrations/0007_auto_20190602_1944.py b/InvenTree/part/migrations/0007_auto_20190602_1944.py new file mode 100644 index 0000000000..9bfeb7fb8d --- /dev/null +++ b/InvenTree/part/migrations/0007_auto_20190602_1944.py @@ -0,0 +1,42 @@ +# Generated by Django 2.2 on 2019-06-02 09:44 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0006_auto_20190526_1215'), + ] + + operations = [ + migrations.RemoveField( + model_name='part', + name='buildable', + ), + migrations.RemoveField( + model_name='part', + name='consumable', + ), + migrations.AddField( + model_name='part', + name='assembly', + field=models.BooleanField(default=False, help_text='Can this part be built from other parts?', verbose_name='Assembly'), + ), + migrations.AddField( + model_name='part', + name='component', + field=models.BooleanField(default=True, help_text='Can this part be used to build other parts?', verbose_name='Component'), + ), + migrations.AlterField( + model_name='bomitem', + name='part', + field=models.ForeignKey(help_text='Select parent part', limit_choices_to={'active': True, 'assembly': True}, on_delete=django.db.models.deletion.CASCADE, related_name='bom_items', to='part.Part'), + ), + migrations.AlterField( + model_name='bomitem', + name='sub_part', + field=models.ForeignKey(help_text='Select part to be used in BOM', limit_choices_to={'active': True, 'component': True}, on_delete=django.db.models.deletion.CASCADE, related_name='used_in', to='part.Part'), + ), + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 048330c2c6..fa6f40915f 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -201,8 +201,8 @@ class Part(models.Model): minimum_stock: Minimum preferred quantity to keep in stock units: Units of measure for this part (default='pcs') salable: Can this part be sold to customers? - buildable: Can this part be build from other parts? - consumable: Can this part be used to make other parts? + assembly: Can this part be build from other parts? + component: Can this part be used to make other parts? purchaseable: Can this part be purchased from suppliers? trackable: Trackable parts can have unique serial numbers assigned, etc, etc active: Is this part active? Parts are deactivated instead of being deleted @@ -343,9 +343,9 @@ class Part(models.Model): units = models.CharField(max_length=20, default="pcs", blank=True, help_text='Stock keeping units for this part') - buildable = models.BooleanField(default=False, help_text='Can this part be built from other parts?') + assembly = models.BooleanField(default=False, verbose_name='Assembly', help_text='Can this part be built from other parts?') - consumable = models.BooleanField(default=True, help_text='Can this part be used to build other parts?') + component = models.BooleanField(default=True, verbose_name='Component', help_text='Can this part be used to build other parts?') trackable = models.BooleanField(default=False, help_text='Does this part have tracking for unique items?') @@ -858,7 +858,7 @@ class BomItem(models.Model): part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='bom_items', help_text='Select parent part', limit_choices_to={ - 'buildable': True, + 'assembly': True, 'active': True, }) @@ -867,7 +867,7 @@ class BomItem(models.Model): sub_part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='used_in', help_text='Select part to be used in BOM', limit_choices_to={ - 'consumable': True, + 'component': True, 'active': True }) diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index fe858091c7..224e41cd97 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -94,8 +94,8 @@ class PartSerializer(serializers.ModelSerializer): # 'available_stock', 'units', 'trackable', - 'buildable', - 'consumable', + 'assembly', + 'component', 'trackable', 'salable', 'active', diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index 4522d47236..b536a5542e 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -100,13 +100,13 @@
- {% if part.buildable %} + {% if part.assembly %} {% endif %} - {% if part.consumable %} + {% if part.component %} diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index d8af6d2ec9..1354a9a4a4 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -78,7 +78,7 @@ - {% if part.buildable %} + {% if part.assembly %} diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index 53a551f36c..dd5034040d 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -15,13 +15,13 @@ Allocated {{ part.allocation_count }} {% endif %} - {% if part.buildable %} + {% if part.assembly %} BOM{{ part.bom_count }} Build{{ part.active_builds|length }} {% endif %} - {% if part.consumable or part.used_in_count > 0 %} + {% if part.component or part.used_in_count > 0 %} Used In{% if part.used_in_count > 0 %}{{ part.used_in_count }}{% endif %} {% endif %} diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py index 15c6f58dcd..b0859e5c1e 100644 --- a/InvenTree/part/test_api.py +++ b/InvenTree/part/test_api.py @@ -149,7 +149,7 @@ class PartAPITest(APITestCase): response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_201_CREATED) - # Now try to create a BomItem which points to a non-buildable part (should fail) + # Now try to create a BomItem which points to a non-assembly part (should fail) data['part'] = 3 response = self.client.post(url, data, format='json') self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) diff --git a/InvenTree/static/script/inventree/inventree.js b/InvenTree/static/script/inventree/inventree.js index 48ba874262..703c49218b 100644 --- a/InvenTree/static/script/inventree/inventree.js +++ b/InvenTree/static/script/inventree/inventree.js @@ -124,6 +124,10 @@ function imageHoverIcon(url) { * On mouseover, display a full-size version of the image */ + if (!url) { + url = '/static/img/blank_image.png'; + } + var html = ` diff --git a/InvenTree/stock/migrations/0005_auto_20190602_1944.py b/InvenTree/stock/migrations/0005_auto_20190602_1944.py new file mode 100644 index 0000000000..1d134288cc --- /dev/null +++ b/InvenTree/stock/migrations/0005_auto_20190602_1944.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2 on 2019-06-02 09:44 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0004_auto_20190525_2356'), + ] + + operations = [ + migrations.AlterField( + model_name='stockitem', + name='status', + field=models.PositiveIntegerField(choices=[(10, 'OK'), (50, 'Attention needed'), (55, 'Damaged'), (60, 'Destroyed'), (70, 'Lost')], default=10, validators=[django.core.validators.MinValueValidator(0)]), + ), + ] diff --git a/InvenTree/templates/InvenTree/search.html b/InvenTree/templates/InvenTree/search.html index 2f88d3caed..7de08fd9b6 100644 --- a/InvenTree/templates/InvenTree/search.html +++ b/InvenTree/templates/InvenTree/search.html @@ -37,17 +37,28 @@ InvenTree | Search Results function onSearchResults(table, output) { $(table).on('load-success.bs.table', function() { + + var panel = $(output).parent('.panel-group'); + var n = $(table).bootstrapTable('getData').length; var text = ''; if (n == 0) { text = 'No results' + + $(panel).hide(); + + $(table).hide(); + + } else { text = n + ' result'; if (n > 1) { text += 's'; } + + $(panel).show(); } $(output).html(text); From 0b88953706466951ba385bac97d83db3a6a0f2ed Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 20:07:30 +1000 Subject: [PATCH 6/8] Form field rearrangement --- InvenTree/build/models.py | 2 +- InvenTree/part/forms.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index ad482b8dc6..f9ffc99953 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -51,7 +51,7 @@ class Build(models.Model): related_name='builds', limit_choices_to={ 'is_template': False, - 'buildable': True, + 'assembly': True, 'active': True }, help_text='Select part to build', diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py index 841622b865..2eb9600065 100644 --- a/InvenTree/part/forms.py +++ b/InvenTree/part/forms.py @@ -92,10 +92,10 @@ class EditPartForm(HelperForm): 'category', 'name', 'IPN', - 'is_template', - 'variant_of', 'description', 'keywords', + 'variant_of', + 'is_template', 'URL', 'default_location', 'default_supplier', From 64d541f45379728126d26b7fc41c0824ac14e982 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 20:28:17 +1000 Subject: [PATCH 7/8] Hide result types which return no results --- InvenTree/part/templates/part/category.html | 1 + InvenTree/static/script/inventree/part.js | 150 +++++++++++--------- InvenTree/templates/InvenTree/search.html | 13 +- InvenTree/templates/search_form.html | 2 +- 4 files changed, 92 insertions(+), 74 deletions(-) diff --git a/InvenTree/part/templates/part/category.html b/InvenTree/part/templates/part/category.html index fd8f20d2aa..2d76b08ac9 100644 --- a/InvenTree/part/templates/part/category.html +++ b/InvenTree/part/templates/part/category.html @@ -156,6 +156,7 @@ {% endif %} }, buttons: ['#part-options'], + checkbox: true, }, ); diff --git a/InvenTree/static/script/inventree/part.js b/InvenTree/static/script/inventree/part.js index 1001ef11a1..7c933ef303 100644 --- a/InvenTree/static/script/inventree/part.js +++ b/InvenTree/static/script/inventree/part.js @@ -82,6 +82,7 @@ function loadPartTable(table, url, options={}) { * - url: Base URL for API query * - options: object containing following (optional) fields * allowInactive: If true, allow display of inactive parts + * checkbox: Show the checkbox column * query: extra query params for API request * buttons: If provided, link buttons to selection status of this table */ @@ -94,6 +95,84 @@ function loadPartTable(table, url, options={}) { query.active = true; } + var columns = [ + { + field: 'pk', + title: 'ID', + visible: false, + } + ]; + + if (options.checkbox) { + columns.push({ + checkbox: true, + title: 'Select', + searchable: false, + }); + } + + columns.push({ + field: 'full_name', + title: 'Part', + sortable: true, + formatter: function(value, row, index, field) { + + if (row.is_template) { + value = '' + value + ''; + } + + var display = imageHoverIcon(row.image_url) + renderLink(value, row.url); + + if (!row.active) { + display = display + "INACTIVE"; + } + return display; + } + }); + + columns.push({ + sortable: true, + field: 'description', + title: 'Description', + formatter: function(value, row, index, field) { + + if (row.is_template) { + value = '' + value + ''; + } + + return value; + } + }); + + columns.push({ + sortable: true, + field: 'category_name', + title: 'Category', + formatter: function(value, row, index, field) { + if (row.category) { + return renderLink(row.category_name, "/part/category/" + row.category + "/"); + } + else { + return ''; + } + } + }); + + columns.push({ + field: 'total_stock', + title: 'Stock', + searchable: false, + sortable: true, + formatter: function(value, row, index, field) { + if (value) { + return renderLink(value, row.url + 'stock/'); + } + else { + return "No Stock"; + } + } + }); + $(table).bootstrapTable({ url: url, sortable: true, @@ -107,76 +186,7 @@ function loadPartTable(table, url, options={}) { queryParams: function(p) { return query; }, - columns: [ - { - checkbox: true, - title: 'Select', - searchable: false, - }, - { - field: 'pk', - title: 'ID', - visible: false, - }, - { - field: 'full_name', - title: 'Part', - sortable: true, - formatter: function(value, row, index, field) { - - if (row.is_template) { - value = '' + value + ''; - } - - var display = imageHoverIcon(row.image_url) + renderLink(value, row.url); - - if (!row.active) { - display = display + "INACTIVE"; - } - return display; - } - }, - { - sortable: true, - field: 'description', - title: 'Description', - formatter: function(value, row, index, field) { - - if (row.is_template) { - value = '' + value + ''; - } - - return value; - } - }, - { - sortable: true, - field: 'category_name', - title: 'Category', - formatter: function(value, row, index, field) { - if (row.category) { - return renderLink(row.category_name, "/part/category/" + row.category + "/"); - } - else { - return ''; - } - } - }, - { - field: 'total_stock', - title: 'Stock', - searchable: false, - sortable: true, - formatter: function(value, row, index, field) { - if (value) { - return renderLink(value, row.url + 'stock/'); - } - else { - return "No Stock"; - } - } - } - ], + columns: columns, }); if (options.buttons) { diff --git a/InvenTree/templates/InvenTree/search.html b/InvenTree/templates/InvenTree/search.html index 7de08fd9b6..70915a49fd 100644 --- a/InvenTree/templates/InvenTree/search.html +++ b/InvenTree/templates/InvenTree/search.html @@ -17,6 +17,10 @@ InvenTree | Search Results


+
+

No results found

+
+ {% include "InvenTree/search_part_category.html" with collapse_id="categories" %} {% include "InvenTree/search_parts.html" with collapse_id='parts' %} @@ -35,10 +39,12 @@ InvenTree | Search Results {% block js_ready %} {{ block.super }} + $(".panel-group").hide(); + function onSearchResults(table, output) { $(table).on('load-success.bs.table', function() { - var panel = $(output).parent('.panel-group'); + var panel = $(output).closest('.panel-group'); var n = $(table).bootstrapTable('getData').length; @@ -47,8 +53,6 @@ InvenTree | Search Results text = 'No results' $(panel).hide(); - - $(table).hide(); } else { @@ -59,6 +63,8 @@ InvenTree | Search Results } $(panel).show(); + + $("#no-search-results").hide(); } $(output).html(text); @@ -120,6 +126,7 @@ InvenTree | Search Results search: "{{ query }}", }, allowInactive: true, + checkbox: false, } ); diff --git a/InvenTree/templates/search_form.html b/InvenTree/templates/search_form.html index 72d317d3ec..e9c2214731 100644 --- a/InvenTree/templates/search_form.html +++ b/InvenTree/templates/search_form.html @@ -3,5 +3,5 @@
- + From 219c438b82f6694a56e9cfc387fd5fb2f179584a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 2 Jun 2019 20:37:59 +1000 Subject: [PATCH 8/8] Part name uniqueness is case insensitive --- InvenTree/part/models.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index fa6f40915f..f311aea1f1 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -248,6 +248,18 @@ class Part(models.Model): else: return static('/img/blank_image.png') + def validate_unique(self, exclude=None): + super().validate_unique(exclude) + + # Part name uniqueness should be case insensitive + try: + if Part.objects.filter(name__iexact=self.name).exclude(id=self.id).exists(): + raise ValidationError({ + "name": _("A part with this name already exists") + }) + except Part.DoesNotExist: + pass + def clean(self): """ Perform cleaning operations for the Part model """
Assembly This part can be assembled from other parts
Component This part can be used in assembliesIn Stock {{ part.total_stock }}
Can Build {{ part.can_build }}