From 0787264930f566c160a2f9072e4fb874c86b33b9 Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 6 Jul 2022 10:09:58 +1000 Subject: [PATCH] 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})