mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 15:15:42 +00:00 
			
		
		
		
	Metadata API refactor (#4545)
* Add generic metadata API endpoint (cherry picked from commit 7bbd53fc7647e2bb18d36c8c351e3fc080037ab1) * Refactor metadata endpoints for build models (cherry picked from commit 722b44e1259f1c5b046c7bc4328995b8238fc342) * Update metadata views for company models * labels * orders * part * report * stock
This commit is contained in:
		@@ -16,7 +16,9 @@ import users.models
 | 
			
		||||
from InvenTree.mixins import ListCreateAPI
 | 
			
		||||
from InvenTree.permissions import RolePermission
 | 
			
		||||
from part.templatetags.inventree_extras import plugins_info
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
 | 
			
		||||
from .mixins import RetrieveUpdateAPI
 | 
			
		||||
from .status import is_worker_running
 | 
			
		||||
from .version import (inventreeApiVersion, inventreeInstanceName,
 | 
			
		||||
                      inventreeVersion)
 | 
			
		||||
@@ -354,3 +356,26 @@ class StatusView(APIView):
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return Response(data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MetadataView(RetrieveUpdateAPI):
 | 
			
		||||
    """Generic API endpoint for reading and editing metadata for a model"""
 | 
			
		||||
 | 
			
		||||
    MODEL_REF = 'model'
 | 
			
		||||
 | 
			
		||||
    def get_model_type(self):
 | 
			
		||||
        """Return the model type associated with this API instance"""
 | 
			
		||||
        model = self.kwargs.get(self.MODEL_REF, None)
 | 
			
		||||
 | 
			
		||||
        if model is None:
 | 
			
		||||
            raise ValidationError(f"MetadataView called without '{self.MODEL_REF}' parameter")
 | 
			
		||||
 | 
			
		||||
        return model
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self):
 | 
			
		||||
        """Return the queryset for this endpoint"""
 | 
			
		||||
        return self.get_model_type().objects.all()
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance"""
 | 
			
		||||
        return MetadataSerializer(self.get_model_type(), *args, **kwargs)
 | 
			
		||||
 
 | 
			
		||||
@@ -10,13 +10,11 @@ from rest_framework.exceptions import ValidationError
 | 
			
		||||
from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from django_filters import rest_framework as rest_filters
 | 
			
		||||
 | 
			
		||||
from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, StatusView
 | 
			
		||||
from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, MetadataView, StatusView
 | 
			
		||||
from InvenTree.helpers import str2bool, isNull, DownloadFile
 | 
			
		||||
from InvenTree.filters import InvenTreeOrderingFilter
 | 
			
		||||
from InvenTree.status_codes import BuildStatus
 | 
			
		||||
from InvenTree.mixins import CreateAPI, RetrieveUpdateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI
 | 
			
		||||
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI
 | 
			
		||||
 | 
			
		||||
import build.admin
 | 
			
		||||
import build.serializers
 | 
			
		||||
@@ -292,16 +290,6 @@ class BuildOrderContextMixin:
 | 
			
		||||
        return ctx
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BuildOrderMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating BuildOrder metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance"""
 | 
			
		||||
        return MetadataSerializer(Build, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = Build.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BuildOutputCreate(BuildOrderContextMixin, CreateAPI):
 | 
			
		||||
    """API endpoint for creating new build output(s)."""
 | 
			
		||||
 | 
			
		||||
@@ -473,16 +461,6 @@ class BuildItemList(ListCreateAPI):
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BuildItemMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating BuildItem metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance"""
 | 
			
		||||
        return MetadataSerializer(BuildItem, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = BuildItem.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BuildAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
 | 
			
		||||
    """API endpoint for listing (and creating) BuildOrderAttachment objects."""
 | 
			
		||||
 | 
			
		||||
@@ -516,7 +494,7 @@ build_api_urls = [
 | 
			
		||||
    # Build Items
 | 
			
		||||
    re_path(r'^item/', include([
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'^metadata/', BuildItemMetadata.as_view(), name='api-build-item-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': BuildItem}, name='api-build-item-metadata'),
 | 
			
		||||
            re_path(r'^.*$', BuildItemDetail.as_view(), name='api-build-item-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
        re_path(r'^.*$', BuildItemList.as_view(), name='api-build-item-list'),
 | 
			
		||||
@@ -532,7 +510,7 @@ build_api_urls = [
 | 
			
		||||
        re_path(r'^finish/', BuildFinish.as_view(), name='api-build-finish'),
 | 
			
		||||
        re_path(r'^cancel/', BuildCancel.as_view(), name='api-build-cancel'),
 | 
			
		||||
        re_path(r'^unallocate/', BuildUnallocate.as_view(), name='api-build-unallocate'),
 | 
			
		||||
        re_path(r'^metadata/', BuildOrderMetadata.as_view(), name='api-build-metadata'),
 | 
			
		||||
        re_path(r'^metadata/', MetadataView.as_view(), {'model': Build}, name='api-build-metadata'),
 | 
			
		||||
        re_path(r'^.*$', BuildDetail.as_view(), name='api-build-detail'),
 | 
			
		||||
    ])),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -8,12 +8,11 @@ from django_filters.rest_framework import DjangoFilterBackend
 | 
			
		||||
from rest_framework import filters
 | 
			
		||||
 | 
			
		||||
import part.models
 | 
			
		||||
from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView
 | 
			
		||||
from InvenTree.api import (AttachmentMixin, ListCreateDestroyAPIView,
 | 
			
		||||
                           MetadataView)
 | 
			
		||||
from InvenTree.filters import InvenTreeOrderingFilter
 | 
			
		||||
from InvenTree.helpers import str2bool
 | 
			
		||||
from InvenTree.mixins import (ListCreateAPI, RetrieveUpdateAPI,
 | 
			
		||||
                              RetrieveUpdateDestroyAPI)
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI
 | 
			
		||||
 | 
			
		||||
from .models import (Company, CompanyAttachment, Contact, ManufacturerPart,
 | 
			
		||||
                     ManufacturerPartAttachment, ManufacturerPartParameter,
 | 
			
		||||
@@ -87,16 +86,6 @@ class CompanyDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
        return queryset
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CompanyMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating Company metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a Company"""
 | 
			
		||||
        return MetadataSerializer(Company, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = Company.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CompanyAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
 | 
			
		||||
    """API endpoint for the CompanyAttachment model"""
 | 
			
		||||
 | 
			
		||||
@@ -231,16 +220,6 @@ class ManufacturerPartDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
    serializer_class = ManufacturerPartSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ManufacturerPartMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating ManufacturerPart metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a Company"""
 | 
			
		||||
        return MetadataSerializer(ManufacturerPart, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = ManufacturerPart.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ManufacturerPartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
 | 
			
		||||
    """API endpoint for listing (and creating) a ManufacturerPartAttachment (file upload)."""
 | 
			
		||||
 | 
			
		||||
@@ -470,16 +449,6 @@ class SupplierPartDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SupplierPartMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating SupplierPart metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a Company"""
 | 
			
		||||
        return MetadataSerializer(SupplierPart, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = SupplierPart.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SupplierPriceBreakFilter(rest_filters.FilterSet):
 | 
			
		||||
    """Custom API filters for the SupplierPriceBreak list endpoint"""
 | 
			
		||||
 | 
			
		||||
@@ -567,7 +536,7 @@ manufacturer_part_api_urls = [
 | 
			
		||||
    ])),
 | 
			
		||||
 | 
			
		||||
    re_path(r'^(?P<pk>\d+)/?', include([
 | 
			
		||||
        re_path('^metadata/', ManufacturerPartMetadata.as_view(), name='api-manufacturer-part-metadata'),
 | 
			
		||||
        re_path('^metadata/', MetadataView.as_view(), {'model': ManufacturerPart}, name='api-manufacturer-part-metadata'),
 | 
			
		||||
        re_path('^.*$', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'),
 | 
			
		||||
    ])),
 | 
			
		||||
 | 
			
		||||
@@ -579,7 +548,7 @@ manufacturer_part_api_urls = [
 | 
			
		||||
supplier_part_api_urls = [
 | 
			
		||||
 | 
			
		||||
    re_path(r'^(?P<pk>\d+)/?', include([
 | 
			
		||||
        re_path('^metadata/', SupplierPartMetadata.as_view(), name='api-supplier-part-metadata'),
 | 
			
		||||
        re_path('^metadata/', MetadataView.as_view(), {'model': SupplierPart}, name='api-supplier-part-metadata'),
 | 
			
		||||
        re_path('^.*$', SupplierPartDetail.as_view(), name='api-supplier-part-detail'),
 | 
			
		||||
    ])),
 | 
			
		||||
 | 
			
		||||
@@ -601,7 +570,7 @@ company_api_urls = [
 | 
			
		||||
    ])),
 | 
			
		||||
 | 
			
		||||
    re_path(r'^(?P<pk>\d+)/?', include([
 | 
			
		||||
        re_path(r'^metadata/', CompanyMetadata.as_view(), name='api-company-metadata'),
 | 
			
		||||
        re_path(r'^metadata/', MetadataView.as_view(), {'model': Company}, name='api-company-metadata'),
 | 
			
		||||
        re_path(r'^.*$', CompanyDetail.as_view(), name='api-company-detail'),
 | 
			
		||||
    ])),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -13,13 +13,12 @@ from rest_framework.exceptions import NotFound
 | 
			
		||||
 | 
			
		||||
import common.models
 | 
			
		||||
import InvenTree.helpers
 | 
			
		||||
from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI,
 | 
			
		||||
                              RetrieveUpdateDestroyAPI)
 | 
			
		||||
from InvenTree.api import MetadataView
 | 
			
		||||
from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI
 | 
			
		||||
from InvenTree.tasks import offload_task
 | 
			
		||||
from part.models import Part
 | 
			
		||||
from plugin.base.label import label as plugin_label
 | 
			
		||||
from plugin.registry import registry
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
from stock.models import StockItem, StockLocation
 | 
			
		||||
 | 
			
		||||
from .models import PartLabel, StockItemLabel, StockLocationLabel
 | 
			
		||||
@@ -307,16 +306,6 @@ class StockItemLabelDetail(StockItemLabelMixin, RetrieveUpdateDestroyAPI):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StockItemLabelMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating StockItemLabel metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a Company"""
 | 
			
		||||
        return MetadataSerializer(StockItemLabel, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = StockItemLabel.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StockItemLabelPrint(StockItemLabelMixin, LabelPrintMixin, RetrieveAPI):
 | 
			
		||||
    """API endpoint for printing a StockItemLabel object."""
 | 
			
		||||
    pass
 | 
			
		||||
@@ -349,16 +338,6 @@ class StockLocationLabelDetail(StockLocationLabelMixin, RetrieveUpdateDestroyAPI
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StockLocationLabelMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating StockLocationLabel metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a Company"""
 | 
			
		||||
        return MetadataSerializer(StockLocationLabel, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = StockLocationLabel.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StockLocationLabelPrint(StockLocationLabelMixin, LabelPrintMixin, RetrieveAPI):
 | 
			
		||||
    """API endpoint for printing a StockLocationLabel object."""
 | 
			
		||||
    pass
 | 
			
		||||
@@ -378,16 +357,6 @@ class PartLabelList(PartLabelMixin, LabelListView):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PartLabelMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating PartLabel metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a Company"""
 | 
			
		||||
        return MetadataSerializer(PartLabel, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = PartLabel.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PartLabelDetail(PartLabelMixin, RetrieveUpdateDestroyAPI):
 | 
			
		||||
    """API endpoint for a single PartLabel object."""
 | 
			
		||||
    pass
 | 
			
		||||
@@ -405,7 +374,7 @@ label_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'print/?', StockItemLabelPrint.as_view(), name='api-stockitem-label-print'),
 | 
			
		||||
            re_path(r'metadata/', StockItemLabelMetadata.as_view(), name='api-stockitem-label-metadata'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'model': StockItemLabel}, name='api-stockitem-label-metadata'),
 | 
			
		||||
            re_path(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
@@ -418,7 +387,7 @@ label_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'print/?', StockLocationLabelPrint.as_view(), name='api-stocklocation-label-print'),
 | 
			
		||||
            re_path(r'metadata/', StockLocationLabelMetadata.as_view(), name='api-stocklocation-label-metadata'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'model': StockLocationLabel}, name='api-stocklocation-label-metadata'),
 | 
			
		||||
            re_path(r'^.*$', StockLocationLabelDetail.as_view(), name='api-stocklocation-label-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
@@ -431,7 +400,7 @@ label_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'^print/', PartLabelPrint.as_view(), name='api-part-label-print'),
 | 
			
		||||
            re_path(r'^metadata/', PartLabelMetadata.as_view(), name='api-part-label-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': PartLabel}, name='api-part-label-metadata'),
 | 
			
		||||
            re_path(r'^.*$', PartLabelDetail.as_view(), name='api-part-label-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -20,11 +20,11 @@ from common.models import InvenTreeSetting
 | 
			
		||||
from common.settings import settings
 | 
			
		||||
from company.models import SupplierPart
 | 
			
		||||
from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
 | 
			
		||||
                           ListCreateDestroyAPIView, StatusView)
 | 
			
		||||
                           ListCreateDestroyAPIView, MetadataView, StatusView)
 | 
			
		||||
from InvenTree.filters import InvenTreeOrderingFilter
 | 
			
		||||
from InvenTree.helpers import DownloadFile, str2bool
 | 
			
		||||
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI,
 | 
			
		||||
                              RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
 | 
			
		||||
                              RetrieveUpdateDestroyAPI)
 | 
			
		||||
from InvenTree.status_codes import (PurchaseOrderStatus, ReturnOrderLineStatus,
 | 
			
		||||
                                    ReturnOrderStatus, SalesOrderStatus)
 | 
			
		||||
from order.admin import (PurchaseOrderExtraLineResource,
 | 
			
		||||
@@ -32,7 +32,6 @@ from order.admin import (PurchaseOrderExtraLineResource,
 | 
			
		||||
                         ReturnOrderResource, SalesOrderExtraLineResource,
 | 
			
		||||
                         SalesOrderLineItemResource, SalesOrderResource)
 | 
			
		||||
from part.models import Part
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
from users.models import Owner
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -385,16 +384,6 @@ class PurchaseOrderIssue(PurchaseOrderContextMixin, CreateAPI):
 | 
			
		||||
    serializer_class = serializers.PurchaseOrderIssueSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating PurchaseOrder metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a PurchaseOrder"""
 | 
			
		||||
        return MetadataSerializer(models.PurchaseOrder, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = models.PurchaseOrder.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderReceive(PurchaseOrderContextMixin, CreateAPI):
 | 
			
		||||
    """API endpoint to receive stock items against a PurchaseOrder.
 | 
			
		||||
 | 
			
		||||
@@ -557,16 +546,6 @@ class PurchaseOrderLineItemDetail(PurchaseOrderLineItemMixin, RetrieveUpdateDest
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderLineItemMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating PurchaseOrderLineItem metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance for a Company"""
 | 
			
		||||
        return MetadataSerializer(models.PurchaseOrderLineItem, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = models.PurchaseOrderLineItem.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderExtraLineList(GeneralExtraLineList, ListCreateAPI):
 | 
			
		||||
    """API endpoint for accessing a list of PurchaseOrderExtraLine objects."""
 | 
			
		||||
 | 
			
		||||
@@ -590,16 +569,6 @@ class PurchaseOrderExtraLineDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
    serializer_class = serializers.PurchaseOrderExtraLineSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderExtraLineItemMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating PurchaseOrderExtraLineItem metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance"""
 | 
			
		||||
        return MetadataSerializer(models.PurchaseOrderExtraLine, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = models.PurchaseOrderExtraLine.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
 | 
			
		||||
    """API endpoint for listing (and creating) a SalesOrderAttachment (file upload)"""
 | 
			
		||||
 | 
			
		||||
@@ -873,16 +842,6 @@ class SalesOrderLineItemDetail(SalesOrderLineItemMixin, RetrieveUpdateDestroyAPI
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderLineItemMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating SalesOrderLineItem metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance"""
 | 
			
		||||
        return MetadataSerializer(models.SalesOrderLineItem, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = models.SalesOrderLineItem.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderExtraLineList(GeneralExtraLineList, ListCreateAPI):
 | 
			
		||||
    """API endpoint for accessing a list of SalesOrderExtraLine objects."""
 | 
			
		||||
 | 
			
		||||
@@ -906,16 +865,6 @@ class SalesOrderExtraLineDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
    serializer_class = serializers.SalesOrderExtraLineSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderExtraLineItemMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating SalesOrderExtraLineItem metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance"""
 | 
			
		||||
        return MetadataSerializer(models.SalesOrderExtraLine, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = models.SalesOrderExtraLine.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderContextMixin:
 | 
			
		||||
    """Mixin to add sales order object as serializer context variable."""
 | 
			
		||||
 | 
			
		||||
@@ -951,16 +900,6 @@ class SalesOrderComplete(SalesOrderContextMixin, CreateAPI):
 | 
			
		||||
    serializer_class = serializers.SalesOrderCompleteSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating SalesOrder metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return a metadata serializer for the SalesOrder model"""
 | 
			
		||||
        return MetadataSerializer(models.SalesOrder, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = models.SalesOrder.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderAllocateSerials(SalesOrderContextMixin, CreateAPI):
 | 
			
		||||
    """API endpoint to allocation stock items against a SalesOrder, by specifying serial numbers."""
 | 
			
		||||
 | 
			
		||||
@@ -1122,16 +1061,6 @@ class SalesOrderShipmentComplete(CreateAPI):
 | 
			
		||||
        return ctx
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class SalesOrderShipmentMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating SalesOrderShipment metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return MetadataSerializer instance"""
 | 
			
		||||
        return MetadataSerializer(models.SalesOrderShipment, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = models.SalesOrderShipment.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PurchaseOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
 | 
			
		||||
    """API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload)"""
 | 
			
		||||
 | 
			
		||||
@@ -1604,7 +1533,7 @@ order_api_urls = [
 | 
			
		||||
            re_path(r'^cancel/', PurchaseOrderCancel.as_view(), name='api-po-cancel'),
 | 
			
		||||
            re_path(r'^complete/', PurchaseOrderComplete.as_view(), name='api-po-complete'),
 | 
			
		||||
            re_path(r'^issue/', PurchaseOrderIssue.as_view(), name='api-po-issue'),
 | 
			
		||||
            re_path(r'^metadata/', PurchaseOrderMetadata.as_view(), name='api-po-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': models.PurchaseOrder}, name='api-po-metadata'),
 | 
			
		||||
            re_path(r'^receive/', PurchaseOrderReceive.as_view(), name='api-po-receive'),
 | 
			
		||||
 | 
			
		||||
            # PurchaseOrder detail API endpoint
 | 
			
		||||
@@ -1621,7 +1550,7 @@ order_api_urls = [
 | 
			
		||||
    # API endpoints for purchase order line items
 | 
			
		||||
    re_path(r'^po-line/', include([
 | 
			
		||||
        path('<int:pk>/', include([
 | 
			
		||||
            re_path(r'^metadata/', PurchaseOrderLineItemMetadata.as_view(), name='api-po-line-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': models.PurchaseOrderLineItem}, name='api-po-line-metadata'),
 | 
			
		||||
            re_path(r'^.*$', PurchaseOrderLineItemDetail.as_view(), name='api-po-line-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
        re_path(r'^.*$', PurchaseOrderLineItemList.as_view(), name='api-po-line-list'),
 | 
			
		||||
@@ -1630,7 +1559,7 @@ order_api_urls = [
 | 
			
		||||
    # API endpoints for purchase order extra line
 | 
			
		||||
    re_path(r'^po-extra-line/', include([
 | 
			
		||||
        path('<int:pk>/', include([
 | 
			
		||||
            re_path(r'^metadata/', PurchaseOrderExtraLineItemMetadata.as_view(), name='api-po-extra-line-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': models.PurchaseOrderExtraLine}, name='api-po-extra-line-metadata'),
 | 
			
		||||
            re_path(r'^.*$', PurchaseOrderExtraLineDetail.as_view(), name='api-po-extra-line-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
        path('', PurchaseOrderExtraLineList.as_view(), name='api-po-extra-line-list'),
 | 
			
		||||
@@ -1646,7 +1575,7 @@ order_api_urls = [
 | 
			
		||||
        re_path(r'^shipment/', include([
 | 
			
		||||
            path(r'<int:pk>/', include([
 | 
			
		||||
                path('ship/', SalesOrderShipmentComplete.as_view(), name='api-so-shipment-ship'),
 | 
			
		||||
                re_path(r'^metadata/', SalesOrderShipmentMetadata.as_view(), name='api-so-shipment-metadata'),
 | 
			
		||||
                re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrderShipment}, name='api-so-shipment-metadata'),
 | 
			
		||||
                re_path(r'^.*$', SalesOrderShipmentDetail.as_view(), name='api-so-shipment-detail'),
 | 
			
		||||
            ])),
 | 
			
		||||
            re_path(r'^.*$', SalesOrderShipmentList.as_view(), name='api-so-shipment-list'),
 | 
			
		||||
@@ -1659,7 +1588,7 @@ order_api_urls = [
 | 
			
		||||
            re_path(r'^cancel/', SalesOrderCancel.as_view(), name='api-so-cancel'),
 | 
			
		||||
            re_path(r'^issue/', SalesOrderIssue.as_view(), name='api-so-issue'),
 | 
			
		||||
            re_path(r'^complete/', SalesOrderComplete.as_view(), name='api-so-complete'),
 | 
			
		||||
            re_path(r'^metadata/', SalesOrderMetadata.as_view(), name='api-so-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrder}, name='api-so-metadata'),
 | 
			
		||||
 | 
			
		||||
            # SalesOrder detail endpoint
 | 
			
		||||
            re_path(r'^.*$', SalesOrderDetail.as_view(), name='api-so-detail'),
 | 
			
		||||
@@ -1675,7 +1604,7 @@ order_api_urls = [
 | 
			
		||||
    # API endpoints for sales order line items
 | 
			
		||||
    re_path(r'^so-line/', include([
 | 
			
		||||
        path('<int:pk>/', include([
 | 
			
		||||
            re_path(r'^metadata/', SalesOrderLineItemMetadata.as_view(), name='api-so-line-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrderLineItem}, name='api-so-line-metadata'),
 | 
			
		||||
            re_path(r'^.*$', SalesOrderLineItemDetail.as_view(), name='api-so-line-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
        path('', SalesOrderLineItemList.as_view(), name='api-so-line-list'),
 | 
			
		||||
@@ -1684,7 +1613,7 @@ order_api_urls = [
 | 
			
		||||
    # API endpoints for sales order extra line
 | 
			
		||||
    re_path(r'^so-extra-line/', include([
 | 
			
		||||
        path('<int:pk>/', include([
 | 
			
		||||
            re_path(r'^metadata/', SalesOrderExtraLineItemMetadata.as_view(), name='api-so-extra-line-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': models.SalesOrderExtraLine}, name='api-so-extra-line-metadata'),
 | 
			
		||||
            re_path(r'^.*$', SalesOrderExtraLineDetail.as_view(), name='api-so-extra-line-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
        path('', SalesOrderExtraLineList.as_view(), name='api-so-extra-line-list'),
 | 
			
		||||
 
 | 
			
		||||
@@ -16,7 +16,7 @@ from rest_framework.response import Response
 | 
			
		||||
import order.models
 | 
			
		||||
from build.models import Build, BuildItem
 | 
			
		||||
from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
 | 
			
		||||
                           ListCreateDestroyAPIView)
 | 
			
		||||
                           ListCreateDestroyAPIView, MetadataView)
 | 
			
		||||
from InvenTree.filters import InvenTreeOrderingFilter
 | 
			
		||||
from InvenTree.helpers import (DownloadFile, increment_serial_number, isNull,
 | 
			
		||||
                               str2bool, str2int)
 | 
			
		||||
@@ -28,7 +28,6 @@ from InvenTree.permissions import RolePermission
 | 
			
		||||
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
 | 
			
		||||
                                    SalesOrderStatus)
 | 
			
		||||
from part.admin import PartCategoryResource, PartResource
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
 | 
			
		||||
from . import serializers as part_serializers
 | 
			
		||||
from . import views
 | 
			
		||||
@@ -230,16 +229,6 @@ class CategoryTree(ListAPI):
 | 
			
		||||
    ordering = ['level', 'name']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CategoryMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating PartCategory metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return a MetadataSerializer pointing to the referenced PartCategory instance"""
 | 
			
		||||
        return MetadataSerializer(PartCategory, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = PartCategory.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class CategoryParameterList(ListCreateAPI):
 | 
			
		||||
    """API endpoint for accessing a list of PartCategoryParameterTemplate objects.
 | 
			
		||||
 | 
			
		||||
@@ -698,16 +687,6 @@ class PartRequirements(RetrieveAPI):
 | 
			
		||||
        return Response(data)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PartMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating Part metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Returns a MetadataSerializer instance pointing to the referenced Part"""
 | 
			
		||||
        return MetadataSerializer(Part, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = Part.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PartPricingDetail(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing part pricing data"""
 | 
			
		||||
 | 
			
		||||
@@ -1415,16 +1394,6 @@ class PartParameterTemplateDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
    serializer_class = part_serializers.PartParameterTemplateSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PartParameterTemplateMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating PartParameterTemplate metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return a MetadataSerializer pointing to the referenced PartParameterTemplate instance"""
 | 
			
		||||
        return MetadataSerializer(PartParameterTemplate, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = PartParameterTemplate.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PartParameterList(ListCreateAPI):
 | 
			
		||||
    """API endpoint for accessing a list of PartParameter objects.
 | 
			
		||||
 | 
			
		||||
@@ -1886,16 +1855,6 @@ class BomItemSubstituteDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
    serializer_class = part_serializers.BomItemSubstituteSerializer
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BomItemMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating PartBOM metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return a MetadataSerializer pointing to the referenced PartCategory instance"""
 | 
			
		||||
        return MetadataSerializer(BomItem, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = BomItem.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
part_api_urls = [
 | 
			
		||||
 | 
			
		||||
    # Base URL for PartCategory API endpoints
 | 
			
		||||
@@ -1910,7 +1869,7 @@ part_api_urls = [
 | 
			
		||||
        # Category detail endpoints
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
 | 
			
		||||
            re_path(r'^metadata/', CategoryMetadata.as_view(), name='api-part-category-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': PartCategory}, name='api-part-category-metadata'),
 | 
			
		||||
 | 
			
		||||
            # PartCategory detail endpoint
 | 
			
		||||
            re_path(r'^.*$', CategoryDetail.as_view(), name='api-part-category-detail'),
 | 
			
		||||
@@ -1953,7 +1912,7 @@ part_api_urls = [
 | 
			
		||||
    re_path(r'^parameter/', include([
 | 
			
		||||
        path('template/', include([
 | 
			
		||||
            re_path(r'^(?P<pk>\d+)/', include([
 | 
			
		||||
                re_path(r'^metadata/?', PartParameterTemplateMetadata.as_view(), name='api-part-parameter-template-metadata'),
 | 
			
		||||
                re_path(r'^metadata/?', MetadataView.as_view(), {'model': PartParameter}, name='api-part-parameter-template-metadata'),
 | 
			
		||||
                re_path(r'^.*$', PartParameterTemplateDetail.as_view(), name='api-part-parameter-template-detail'),
 | 
			
		||||
            ])),
 | 
			
		||||
            re_path(r'^.*$', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'),
 | 
			
		||||
@@ -2000,7 +1959,7 @@ part_api_urls = [
 | 
			
		||||
        re_path(r'^bom-validate/', PartValidateBOM.as_view(), name='api-part-bom-validate'),
 | 
			
		||||
 | 
			
		||||
        # Part metadata
 | 
			
		||||
        re_path(r'^metadata/', PartMetadata.as_view(), name='api-part-metadata'),
 | 
			
		||||
        re_path(r'^metadata/', MetadataView.as_view(), {'model': Part}, name='api-part-metadata'),
 | 
			
		||||
 | 
			
		||||
        # Part pricing
 | 
			
		||||
        re_path(r'^pricing/', PartPricingDetail.as_view(), name='api-part-pricing'),
 | 
			
		||||
@@ -2032,7 +1991,7 @@ bom_api_urls = [
 | 
			
		||||
    # BOM Item Detail
 | 
			
		||||
    path(r'<int:pk>/', include([
 | 
			
		||||
        re_path(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'),
 | 
			
		||||
        re_path(r'^metadata/?', BomItemMetadata.as_view(), name='api-bom-item-metadata'),
 | 
			
		||||
        re_path(r'^metadata/?', MetadataView.as_view(), {'model': BomItem}, name='api-bom-item-metadata'),
 | 
			
		||||
        re_path(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'),
 | 
			
		||||
    ])),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -18,9 +18,8 @@ import common.models
 | 
			
		||||
import InvenTree.helpers
 | 
			
		||||
import order.models
 | 
			
		||||
import part.models
 | 
			
		||||
from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI,
 | 
			
		||||
                              RetrieveUpdateDestroyAPI)
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
from InvenTree.api import MetadataView
 | 
			
		||||
from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI
 | 
			
		||||
from stock.models import StockItem, StockItemAttachment
 | 
			
		||||
 | 
			
		||||
from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport,
 | 
			
		||||
@@ -449,31 +448,6 @@ class ReturnOrderReportPrint(ReturnOrderReportMixin, ReportPrintMixin, RetrieveA
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ReportMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating Report metadata."""
 | 
			
		||||
    MODEL_REF = 'reportmodel'
 | 
			
		||||
 | 
			
		||||
    def _get_model(self, *args, **kwargs):
 | 
			
		||||
        """Return model depending on which report type is requested in get_view constructor."""
 | 
			
		||||
        reportmodel = self.kwargs.get(self.MODEL_REF, PurchaseOrderReport)
 | 
			
		||||
 | 
			
		||||
        if reportmodel not in [PurchaseOrderReport, SalesOrderReport, BuildReport, BillOfMaterialsReport, TestReport]:
 | 
			
		||||
            raise ValidationError("Invalid report model")
 | 
			
		||||
        return reportmodel
 | 
			
		||||
 | 
			
		||||
        # Return corresponding Serializer
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return correct MetadataSerializer instance depending on which model is requested"""
 | 
			
		||||
        # Get type of report, make sure its one of the allowed values
 | 
			
		||||
        UseModel = self._get_model(*args, **kwargs)
 | 
			
		||||
        return MetadataSerializer(UseModel, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self, *args, **kwargs):
 | 
			
		||||
        """Return correct queryset depending on which model is requested"""
 | 
			
		||||
        UseModel = self._get_model(*args, **kwargs)
 | 
			
		||||
        return UseModel.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
report_api_urls = [
 | 
			
		||||
 | 
			
		||||
    # Purchase order reports
 | 
			
		||||
@@ -481,7 +455,7 @@ report_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'print/', PurchaseOrderReportPrint.as_view(), name='api-po-report-print'),
 | 
			
		||||
            re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: PurchaseOrderReport}, name='api-po-report-metadata'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'model': PurchaseOrderReport}, name='api-po-report-metadata'),
 | 
			
		||||
            path('', PurchaseOrderReportDetail.as_view(), name='api-po-report-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
@@ -494,7 +468,7 @@ report_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'print/', SalesOrderReportPrint.as_view(), name='api-so-report-print'),
 | 
			
		||||
            re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: SalesOrderReport}, name='api-so-report-metadata'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'model': SalesOrderReport}, name='api-so-report-metadata'),
 | 
			
		||||
            path('', SalesOrderReportDetail.as_view(), name='api-so-report-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
@@ -505,6 +479,7 @@ report_api_urls = [
 | 
			
		||||
    re_path(r'ro/', include([
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            path(r'print/', ReturnOrderReportPrint.as_view(), name='api-return-order-report-print'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'model': ReturnOrderReport}, name='api-so-report-metadata'),
 | 
			
		||||
            path('', ReturnOrderReportDetail.as_view(), name='api-return-order-report-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
        path('', ReturnOrderReportList.as_view(), name='api-return-order-report-list'),
 | 
			
		||||
@@ -515,7 +490,7 @@ report_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'print/?', BuildReportPrint.as_view(), name='api-build-report-print'),
 | 
			
		||||
            re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: BuildReport}, name='api-build-report-metadata'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'model': BuildReport}, name='api-build-report-metadata'),
 | 
			
		||||
            re_path(r'^.$', BuildReportDetail.as_view(), name='api-build-report-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
@@ -529,7 +504,7 @@ report_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'print/?', BOMReportPrint.as_view(), name='api-bom-report-print'),
 | 
			
		||||
            re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: BillOfMaterialsReport}, name='api-bom-report-metadata'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'model': BillOfMaterialsReport}, name='api-bom-report-metadata'),
 | 
			
		||||
            re_path(r'^.*$', BOMReportDetail.as_view(), name='api-bom-report-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
@@ -542,7 +517,7 @@ report_api_urls = [
 | 
			
		||||
        # Detail views
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
            re_path(r'print/?', StockItemTestReportPrint.as_view(), name='api-stockitem-testreport-print'),
 | 
			
		||||
            re_path(r'metadata/', ReportMetadata.as_view(), {ReportMetadata.MODEL_REF: TestReport}, name='api-stockitem-testreport-metadata'),
 | 
			
		||||
            re_path(r'metadata/', MetadataView.as_view(), {'report': TestReport}, name='api-stockitem-testreport-metadata'),
 | 
			
		||||
            re_path(r'^.*$', StockItemTestReportDetail.as_view(), name='api-stockitem-testreport-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -23,13 +23,13 @@ from build.models import Build
 | 
			
		||||
from company.models import Company, SupplierPart
 | 
			
		||||
from company.serializers import CompanySerializer, SupplierPartSerializer
 | 
			
		||||
from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
 | 
			
		||||
                           ListCreateDestroyAPIView, StatusView)
 | 
			
		||||
                           ListCreateDestroyAPIView, MetadataView, StatusView)
 | 
			
		||||
from InvenTree.filters import InvenTreeOrderingFilter
 | 
			
		||||
from InvenTree.helpers import (DownloadFile, extract_serial_numbers, isNull,
 | 
			
		||||
                               str2bool, str2int)
 | 
			
		||||
from InvenTree.mixins import (CreateAPI, CustomRetrieveUpdateDestroyAPI,
 | 
			
		||||
                              ListAPI, ListCreateAPI, RetrieveAPI,
 | 
			
		||||
                              RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
 | 
			
		||||
                              RetrieveUpdateDestroyAPI)
 | 
			
		||||
from InvenTree.status_codes import StockHistoryCode, StockStatus
 | 
			
		||||
from order.models import (PurchaseOrder, ReturnOrder, SalesOrder,
 | 
			
		||||
                          SalesOrderAllocation)
 | 
			
		||||
@@ -37,7 +37,6 @@ from order.serializers import (PurchaseOrderSerializer, ReturnOrderSerializer,
 | 
			
		||||
                               SalesOrderSerializer)
 | 
			
		||||
from part.models import BomItem, Part, PartCategory
 | 
			
		||||
from part.serializers import PartBriefSerializer
 | 
			
		||||
from plugin.serializers import MetadataSerializer
 | 
			
		||||
from stock.admin import LocationResource, StockItemResource
 | 
			
		||||
from stock.models import (StockItem, StockItemAttachment, StockItemTestResult,
 | 
			
		||||
                          StockItemTracking, StockLocation)
 | 
			
		||||
@@ -83,16 +82,6 @@ class StockDetail(RetrieveUpdateDestroyAPI):
 | 
			
		||||
        return self.serializer_class(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StockMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating StockItem metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return serializer."""
 | 
			
		||||
        return MetadataSerializer(StockItem, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = StockItem.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class StockItemContextMixin:
 | 
			
		||||
    """Mixin class for adding StockItem object to serializer context."""
 | 
			
		||||
 | 
			
		||||
@@ -1344,16 +1333,6 @@ class StockTrackingList(ListAPI):
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LocationMetadata(RetrieveUpdateAPI):
 | 
			
		||||
    """API endpoint for viewing / updating StockLocation metadata."""
 | 
			
		||||
 | 
			
		||||
    def get_serializer(self, *args, **kwargs):
 | 
			
		||||
        """Return serializer."""
 | 
			
		||||
        return MetadataSerializer(StockLocation, *args, **kwargs)
 | 
			
		||||
 | 
			
		||||
    queryset = StockLocation.objects.all()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LocationDetail(CustomRetrieveUpdateDestroyAPI):
 | 
			
		||||
    """API endpoint for detail view of StockLocation object.
 | 
			
		||||
 | 
			
		||||
@@ -1391,7 +1370,7 @@ stock_api_urls = [
 | 
			
		||||
        # Stock location detail endpoints
 | 
			
		||||
        path(r'<int:pk>/', include([
 | 
			
		||||
 | 
			
		||||
            re_path(r'^metadata/', LocationMetadata.as_view(), name='api-location-metadata'),
 | 
			
		||||
            re_path(r'^metadata/', MetadataView.as_view(), {'model': StockLocation}, name='api-location-metadata'),
 | 
			
		||||
 | 
			
		||||
            re_path(r'^.*$', LocationDetail.as_view(), name='api-location-detail'),
 | 
			
		||||
        ])),
 | 
			
		||||
@@ -1433,7 +1412,7 @@ stock_api_urls = [
 | 
			
		||||
    path(r'<int:pk>/', include([
 | 
			
		||||
        re_path(r'^convert/', StockItemConvert.as_view(), name='api-stock-item-convert'),
 | 
			
		||||
        re_path(r'^install/', StockItemInstall.as_view(), name='api-stock-item-install'),
 | 
			
		||||
        re_path(r'^metadata/', StockMetadata.as_view(), name='api-stock-item-metadata'),
 | 
			
		||||
        re_path(r'^metadata/', MetadataView.as_view(), {'model': StockItem}, name='api-stock-item-metadata'),
 | 
			
		||||
        re_path(r'^return/', StockItemReturn.as_view(), name='api-stock-item-return'),
 | 
			
		||||
        re_path(r'^serialize/', StockItemSerialize.as_view(), name='api-stock-item-serialize'),
 | 
			
		||||
        re_path(r'^uninstall/', StockItemUninstall.as_view(), name='api-stock-item-uninstall'),
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user