From 1235d47eca762bf6a2aea9a3b6c23ffd25110fc3 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 5 Jul 2022 17:00:25 +1000 Subject: [PATCH 1/3] Bug fix for sales order allocation table (#3295) - Display serial number where available --- InvenTree/order/serializers.py | 2 +- InvenTree/templates/js/translated/order.js | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index 757559da06..95a41902d5 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -741,7 +741,7 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer): """Initialization routine for the serializer""" order_detail = kwargs.pop('order_detail', False) part_detail = kwargs.pop('part_detail', True) - item_detail = kwargs.pop('item_detail', False) + item_detail = kwargs.pop('item_detail', True) location_detail = kwargs.pop('location_detail', False) customer_detail = kwargs.pop('customer_detail', False) diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index 0909c02808..04bed69c6c 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -3236,10 +3236,12 @@ function showAllocationSubTable(index, row, element, options) { formatter: function(value, row, index, field) { var text = ''; - if (row.serial != null && row.quantity == 1) { - text = `{% trans "Serial Number" %}: ${row.serial}`; - } else { - text = `{% trans "Quantity" %}: ${row.quantity}`; + var item = row.item_detail; + + var text = `{% trans "Quantity" %}: ${row.quantity}`; + + if (item && item.serial != null && row.quantity == 1) { + text = `{% trans "Serial Number" %}: ${item.serial}`; } return renderLink(text, `/stock/item/${row.item}/`); From 0787264930f566c160a2f9072e4fb874c86b33b9 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 6 Jul 2022 10:09:58 +1000 Subject: [PATCH 2/3] Allow searching of BOM List API endpoint (#3296) * Allow searching of BOM List API endpoint * Bump API version * Adds ordering field options to BOM List API endpoint * Add some unit testing for new API features * Fixes for unit tests --- InvenTree/InvenTree/api_version.py | 3 + InvenTree/part/api.py | 23 ++++++- InvenTree/part/fixtures/bom.yaml | 3 + InvenTree/part/test_api.py | 96 ++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index b7ce609491..6db9e05719 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -7,6 +7,9 @@ INVENTREE_API_VERSION = 61 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about +v62 -> 2022-07-05 : https://github.com/inventree/InvenTree/pull/3296 + - Allows search on BOM List API endpoint + v61 -> 2022-06-12 : https://github.com/inventree/InvenTree/pull/3183 - Migrate the "Convert Stock Item" form class to use the API - There is now an API endpoint for converting a stock item to a valid variant diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 2959206daa..c4cada7430 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -24,6 +24,7 @@ from common.models import InvenTreeSetting from company.models import Company, ManufacturerPart, SupplierPart from InvenTree.api import (APIDownloadMixin, AttachmentMixin, ListCreateDestroyAPIView) +from InvenTree.filters import InvenTreeOrderingFilter from InvenTree.helpers import DownloadFile, increment, isNull, str2bool from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveAPI, RetrieveUpdateAPI, RetrieveUpdateDestroyAPI, @@ -1756,12 +1757,32 @@ class BomList(ListCreateDestroyAPIView): filter_backends = [ DjangoFilterBackend, filters.SearchFilter, - filters.OrderingFilter, + InvenTreeOrderingFilter, ] filterset_fields = [ ] + search_fields = [ + 'reference', + 'sub_part__name', + 'sub_part__description', + 'sub_part__IPN', + 'sub_part__revision', + 'sub_part__keywords', + 'sub_part__category__name', + ] + + ordering_fields = [ + 'quantity', + 'sub_part', + 'available_stock', + ] + + ordering_field_aliases = { + 'sub_part': 'sub_part__name', + } + class BomImportUpload(CreateAPI): """API endpoint for uploading a complete Bill of Materials. diff --git a/InvenTree/part/fixtures/bom.yaml b/InvenTree/part/fixtures/bom.yaml index ac52452d75..f09d1b8cf4 100644 --- a/InvenTree/part/fixtures/bom.yaml +++ b/InvenTree/part/fixtures/bom.yaml @@ -24,6 +24,7 @@ part: 100 sub_part: 5 quantity: 25 + reference: ABCDE # 3 x Orphan - model: part.bomitem @@ -32,6 +33,7 @@ part: 100 sub_part: 50 quantity: 3 + reference: VWXYZ - model: part.bomitem pk: 5 @@ -39,6 +41,7 @@ part: 1 sub_part: 5 quantity: 3 + reference: LMNOP # Make "Assembly" from "Bob" - model: part.bomitem diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py index 96057bc9c7..ec3d39abb3 100644 --- a/InvenTree/part/test_api.py +++ b/InvenTree/part/test_api.py @@ -1647,6 +1647,102 @@ class BomItemTest(InvenTreeAPITestCase): for key in ['available_stock', 'available_substitute_stock']: self.assertTrue(key in el) + def test_bom_list_search(self): + """Test that we can search the BOM list API endpoint""" + + url = reverse('api-bom-list') + + response = self.get(url, expected_code=200) + + self.assertEqual(len(response.data), 6) + + # Limit the results with a search term + response = self.get( + url, + { + 'search': '0805', + }, + expected_code=200, + ) + + self.assertEqual(len(response.data), 3) + + # Search by 'reference' field + for q in ['ABCDE', 'LMNOP', 'VWXYZ']: + response = self.get( + url, + { + 'search': q, + }, + expected_code=200 + ) + + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]['reference'], q) + + # Search by nonsense data + response = self.get( + url, + { + 'search': 'xxxxxxxxxxxxxxxxx', + }, + expected_code=200 + ) + + self.assertEqual(len(response.data), 0) + + def test_bom_list_ordering(self): + """Test that the BOM list results can be ordered""" + + url = reverse('api-bom-list') + + # Order by increasing quantity + response = self.get( + f"{url}?ordering=+quantity", + expected_code=200 + ) + + self.assertEqual(len(response.data), 6) + + q1 = response.data[0]['quantity'] + q2 = response.data[-1]['quantity'] + + self.assertTrue(q1 < q2) + + # Order by decreasing quantity + response = self.get( + f"{url}?ordering=-quantity", + expected_code=200, + ) + + self.assertEqual(q1, response.data[-1]['quantity']) + self.assertEqual(q2, response.data[0]['quantity']) + + # Now test ordering by 'sub_part' (which is actually 'sub_part__name') + response = self.get( + url, + { + 'ordering': 'sub_part', + 'sub_part_detail': True, + }, + expected_code=200, + ) + + n1 = response.data[0]['sub_part_detail']['name'] + n2 = response.data[-1]['sub_part_detail']['name'] + + response = self.get( + url, + { + 'ordering': '-sub_part', + 'sub_part_detail': True, + }, + expected_code=200, + ) + + self.assertEqual(n1, response.data[-1]['sub_part_detail']['name']) + self.assertEqual(n2, response.data[0]['sub_part_detail']['name']) + def test_get_bom_detail(self): """Get the detail view for a single BomItem object.""" url = reverse('api-bom-item-detail', kwargs={'pk': 3}) From 412fdf246a56d02c91093289b90380536ab93d33 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 6 Jul 2022 10:10:09 +1000 Subject: [PATCH 3/3] Bump django version (#3299) --- requirements-dev.txt | 6 +++--- requirements.in | 2 +- requirements.txt | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 2fb26e7727..a60d5bd101 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -94,9 +94,9 @@ distlib==0.3.4 \ --hash=sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b \ --hash=sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579 # via virtualenv -django==3.2.13 \ - --hash=sha256:6d93497a0a9bf6ba0e0b1a29cccdc40efbfc76297255b1309b3a884a688ec4b6 \ - --hash=sha256:b896ca61edc079eb6bbaa15cf6071eb69d6aac08cce5211583cfb41515644fdf +django==3.2.14 \ + --hash=sha256:677182ba8b5b285a4e072f3ac17ceee6aff1b5ce77fd173cc5b6a2d3dc022fcf \ + --hash=sha256:a8681e098fa60f7c33a4b628d6fcd3fe983a0939ff1301ecacac21d0b38bad56 # via # -c requirements.txt # django-debug-toolbar diff --git a/requirements.in b/requirements.in index dde42bbd8f..d02436ded1 100644 --- a/requirements.in +++ b/requirements.in @@ -1,5 +1,5 @@ # Please keep this list sorted - if you pin a version provide a reason -Django<4 # Django package +Django>=3.2.14,<4 # Django package coreapi # API documentation for djangorestframework cryptography==3.4.8 # Core cryptographic functionality django-allauth # SSO for external providers via OpenID diff --git a/requirements.txt b/requirements.txt index 27b5cf7493..32cfc56fa2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -42,7 +42,7 @@ defusedxml==0.7.1 # python3-openid diff-match-patch==20200713 # via django-import-export -django==3.2.13 +django==3.2.14 # via # -r requirements.in # django-allauth