From a1cf893eb20517b6f5ecdb0cae11451bd773cb40 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 12 Feb 2021 20:55:13 +1100 Subject: [PATCH] List API endpint for BOM reports --- InvenTree/report/api.py | 111 +++++++++++++++++++++++++++++++- InvenTree/report/serializers.py | 31 +++++++-- 2 files changed, 134 insertions(+), 8 deletions(-) diff --git a/InvenTree/report/api.py b/InvenTree/report/api.py index 68a1f23f0d..4030874a51 100644 --- a/InvenTree/report/api.py +++ b/InvenTree/report/api.py @@ -3,7 +3,7 @@ from __future__ import unicode_literals from django.utils.translation import ugettext as _ from django.conf.urls import url, include -from django.core.exceptions import FieldError +from django.core.exceptions import ValidationError, FieldError from django.http import HttpResponse from django_filters.rest_framework import DjangoFilterBackend @@ -16,8 +16,13 @@ import InvenTree.helpers from stock.models import StockItem +import part.models + from .models import TestReport +from .models import BillOfMaterialsReport + from .serializers import TestReportSerializer +from .serializers import BOMReportSerializer class ReportListView(generics.ListAPIView): @@ -76,6 +81,42 @@ class StockItemReportMixin: return valid_items +class PartReportMixin: + """ + Mixin for extracting part items from query params + """ + + def get_parts(self): + """ + Return a list of requested part objects + """ + + parts = [] + + params = self.request.query_params + + if 'parts[]' in params: + parts = params.getlist('parts[]', []) + elif 'part' in params: + parts = [params.get('part', None)] + + if type(parts) not in [list, tuple]: + parts = [parts] + + valid_ids = [] + + for p in parts: + try: + valid_ids.append(int(p)) + except (ValueError): + continue + + # Extract a valid set of Part objects + valid_parts = part.models.Part.objects.filter(pk__in=valid_ids) + + return valid_parts + + class StockItemTestReportList(ReportListView, StockItemReportMixin): """ API endpoint for viewing list of TestReport objects. @@ -224,8 +265,76 @@ class StockItemTestReportPrint(generics.RetrieveAPIView, StockItemReportMixin): ) +class BOMReportList(ReportListView, PartReportMixin): + """ + API endpoint for viewing a list of BillOfMaterialReport objects. + + Filterably by: + + - enabled: Filter by enabled / disabled status + - part: Filter by single part + - parts: Filter by list of parts + """ + + queryset = BillOfMaterialsReport.objects.all() + serializer_class = BOMReportSerializer + + def filter_queryset(self, queryset): + + queryset = super().filter_queryset(queryset) + + # List of Part objects to match against + parts = self.get_parts() + + if len(parts) > 0: + """ + We wish to filter by part(s). + + We need to compare the 'filters' string of each report, + and see if it matches against each of the specified parts. + """ + + valid_report_ids = set() + + for report in queryset.all(): + + matches = True + + try: + filters = InvenTree.helpers.validateFilterString(report.filters) + except ValidationError: + # Filters are ill-defined + continue + + for p in parts: + part_query = part.models.Part.objects.filter(pk=p.pk) + + try: + if not part_query.filter(**filters).exists(): + matches = False + break + except FieldError: + matches = False + break + + if matches: + valid_report_ids.add(report.pk) + else: + continue + + # Reduce queryset to only valid matches + queryset = queryset.filter(pk__in=[pk for pk in valid_report_ids]) + + return queryset + + report_api_urls = [ + url(r'bom/', include([ + # List view + url(r'^.*$', BOMReportList.as_view(), name='api-bom-report-list'), + ])), + # Stock item test reports url(r'test/', include([ # Detail views diff --git a/InvenTree/report/serializers.py b/InvenTree/report/serializers.py index 0cd4d4f40a..4474a276a2 100644 --- a/InvenTree/report/serializers.py +++ b/InvenTree/report/serializers.py @@ -5,6 +5,7 @@ from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeAttachmentSerializerField from .models import TestReport +from .models import BillOfMaterialsReport class TestReportSerializer(InvenTreeModelSerializer): @@ -14,10 +15,26 @@ class TestReportSerializer(InvenTreeModelSerializer): class Meta: model = TestReport fields = [ - 'pk', - 'name', - 'description', - 'template', - 'filters', - 'enabled', - ] + 'pk', + 'name', + 'description', + 'template', + 'filters', + 'enabled', + ] + + +class BOMReportSerializer(InvenTreeModelSerializer): + + template = InvenTreeAttachmentSerializerField(required=True) + + class Meta: + model = BillOfMaterialsReport + fields = [ + 'pk', + 'name', + 'description', + 'template', + 'filters', + 'enabled', + ]