From e1e04329637d5d1b410911108dcc3389c267ea61 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 26 Apr 2022 10:57:43 +1000 Subject: [PATCH 1/3] Adds 'variant_of' filter back into Part API --- InvenTree/InvenTree/api_version.py | 5 ++++- InvenTree/part/api.py | 12 ++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index 7851efd8dd..f767148199 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -4,11 +4,14 @@ InvenTree API version information # InvenTree API version -INVENTREE_API_VERSION = 40 +INVENTREE_API_VERSION = 41 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about +v41 -> 2022-04-26 + - Fixes 'variant_of' filter for Part list endpoint + v40 -> 2022-04-19 - Adds ability to filter StockItem list by "tracked" parameter - This checks the serial number or batch code fields diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index de6cd4a974..e394efae7e 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -1175,6 +1175,18 @@ class PartList(generics.ListCreateAPIView): except (ValueError, Part.DoesNotExist): pass + # Filter by 'variant_of' + # Note that this is subtly different from 'ancestor' filter (above) + variant_of = params.get('variant_of', None) + + if variant_of is not None: + try: + template = Part.objects.get(pk=variant_of) + variants = template.get_children() + queryset = queryset.filter(pk__in=[v.pk for v in variants]) + except (ValueError, Part.DoesNotExist): + pass + # Filter only parts which are in the "BOM" for a given part in_bom_for = params.get('in_bom_for', None) From 693e47ab891328357d355e1932b58f7b2d9d93ea Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 26 Apr 2022 11:02:22 +1000 Subject: [PATCH 2/3] Remove outdated filter_fields variable --- InvenTree/part/api.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index e394efae7e..b025791a7f 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -1351,10 +1351,6 @@ class PartList(generics.ListCreateAPIView): filters.OrderingFilter, ] - filter_fields = [ - 'variant_of', - ] - ordering_fields = [ 'name', 'creation_date', From 70d4960fa30ae26afdb342931532f2a07bc70828 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 26 Apr 2022 11:43:34 +1000 Subject: [PATCH 3/3] Add new unit tests for part API filters --- InvenTree/part/fixtures/part.yaml | 1 + InvenTree/part/test_api.py | 91 +++++++++++++++++++++++++++++++ 2 files changed, 92 insertions(+) diff --git a/InvenTree/part/fixtures/part.yaml b/InvenTree/part/fixtures/part.yaml index d0a2d949b1..fd38036fa9 100644 --- a/InvenTree/part/fixtures/part.yaml +++ b/InvenTree/part/fixtures/part.yaml @@ -177,6 +177,7 @@ fields: name: 'Green chair variant' variant_of: 10003 + is_template: true category: 7 trackable: true tree_id: 1 diff --git a/InvenTree/part/test_api.py b/InvenTree/part/test_api.py index 5a8acbecd9..ddfaa44e94 100644 --- a/InvenTree/part/test_api.py +++ b/InvenTree/part/test_api.py @@ -567,6 +567,97 @@ class PartAPITest(InvenTreeAPITestCase): self.assertEqual(response.data['name'], name) self.assertEqual(response.data['description'], description) + def test_template_filters(self): + """ + Unit tests for API filters related to template parts: + + - variant_of : Return children of specified part + - ancestor : Return descendants of specified part + + Uses the 'chair template' part (pk=10000) + """ + + # Rebuild the MPTT structure before running these tests + Part.objects.rebuild() + + url = reverse('api-part-list') + + response = self.get( + url, + { + 'variant_of': 10000, + }, + expected_code=200 + ) + + # 3 direct children of template part + self.assertEqual(len(response.data), 3) + + response = self.get( + url, + { + 'ancestor': 10000, + }, + expected_code=200, + ) + + # 4 total descendants + self.assertEqual(len(response.data), 4) + + # Use the 'green chair' as our reference + response = self.get( + url, + { + 'variant_of': 10003, + }, + expected_code=200, + ) + + self.assertEqual(len(response.data), 1) + + response = self.get( + url, + { + 'ancestor': 10003, + }, + expected_code=200, + ) + + self.assertEqual(len(response.data), 1) + + # Add some more variants + + p = Part.objects.get(pk=10004) + + for i in range(100): + Part.objects.create( + name=f'Chair variant {i}', + description='A new chair variant', + variant_of=p, + ) + + # There should still be only one direct variant + response = self.get( + url, + { + 'variant_of': 10003, + }, + expected_code=200, + ) + + self.assertEqual(len(response.data), 1) + + # However, now should be 101 descendants + response = self.get( + url, + { + 'ancestor': 10003, + }, + expected_code=200, + ) + + self.assertEqual(len(response.data), 101) + class PartDetailTests(InvenTreeAPITestCase): """