mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55: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.mixins import ListCreateAPI | ||||||
| from InvenTree.permissions import RolePermission | from InvenTree.permissions import RolePermission | ||||||
| from part.templatetags.inventree_extras import plugins_info | from part.templatetags.inventree_extras import plugins_info | ||||||
|  | from plugin.serializers import MetadataSerializer | ||||||
|  |  | ||||||
|  | from .mixins import RetrieveUpdateAPI | ||||||
| from .status import is_worker_running | from .status import is_worker_running | ||||||
| from .version import (inventreeApiVersion, inventreeInstanceName, | from .version import (inventreeApiVersion, inventreeInstanceName, | ||||||
|                       inventreeVersion) |                       inventreeVersion) | ||||||
| @@ -354,3 +356,26 @@ class StatusView(APIView): | |||||||
|         } |         } | ||||||
|  |  | ||||||
|         return Response(data) |         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.rest_framework import DjangoFilterBackend | ||||||
| from django_filters import rest_framework as rest_filters | 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.helpers import str2bool, isNull, DownloadFile | ||||||
| from InvenTree.filters import InvenTreeOrderingFilter | from InvenTree.filters import InvenTreeOrderingFilter | ||||||
| from InvenTree.status_codes import BuildStatus | from InvenTree.status_codes import BuildStatus | ||||||
| from InvenTree.mixins import CreateAPI, RetrieveUpdateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI | from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI | ||||||
|  |  | ||||||
| from plugin.serializers import MetadataSerializer |  | ||||||
|  |  | ||||||
| import build.admin | import build.admin | ||||||
| import build.serializers | import build.serializers | ||||||
| @@ -292,16 +290,6 @@ class BuildOrderContextMixin: | |||||||
|         return ctx |         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): | class BuildOutputCreate(BuildOrderContextMixin, CreateAPI): | ||||||
|     """API endpoint for creating new build output(s).""" |     """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): | class BuildAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): | ||||||
|     """API endpoint for listing (and creating) BuildOrderAttachment objects.""" |     """API endpoint for listing (and creating) BuildOrderAttachment objects.""" | ||||||
|  |  | ||||||
| @@ -516,7 +494,7 @@ build_api_urls = [ | |||||||
|     # Build Items |     # Build Items | ||||||
|     re_path(r'^item/', include([ |     re_path(r'^item/', include([ | ||||||
|         path(r'<int:pk>/', 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'^.*$', BuildItemDetail.as_view(), name='api-build-item-detail'), | ||||||
|         ])), |         ])), | ||||||
|         re_path(r'^.*$', BuildItemList.as_view(), name='api-build-item-list'), |         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'^finish/', BuildFinish.as_view(), name='api-build-finish'), | ||||||
|         re_path(r'^cancel/', BuildCancel.as_view(), name='api-build-cancel'), |         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'^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'), |         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 | from rest_framework import filters | ||||||
|  |  | ||||||
| import part.models | import part.models | ||||||
| from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView | from InvenTree.api import (AttachmentMixin, ListCreateDestroyAPIView, | ||||||
|  |                            MetadataView) | ||||||
| from InvenTree.filters import InvenTreeOrderingFilter | from InvenTree.filters import InvenTreeOrderingFilter | ||||||
| from InvenTree.helpers import str2bool | from InvenTree.helpers import str2bool | ||||||
| from InvenTree.mixins import (ListCreateAPI, RetrieveUpdateAPI, | from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI | ||||||
|                               RetrieveUpdateDestroyAPI) |  | ||||||
| from plugin.serializers import MetadataSerializer |  | ||||||
|  |  | ||||||
| from .models import (Company, CompanyAttachment, Contact, ManufacturerPart, | from .models import (Company, CompanyAttachment, Contact, ManufacturerPart, | ||||||
|                      ManufacturerPartAttachment, ManufacturerPartParameter, |                      ManufacturerPartAttachment, ManufacturerPartParameter, | ||||||
| @@ -87,16 +86,6 @@ class CompanyDetail(RetrieveUpdateDestroyAPI): | |||||||
|         return queryset |         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): | class CompanyAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): | ||||||
|     """API endpoint for the CompanyAttachment model""" |     """API endpoint for the CompanyAttachment model""" | ||||||
|  |  | ||||||
| @@ -231,16 +220,6 @@ class ManufacturerPartDetail(RetrieveUpdateDestroyAPI): | |||||||
|     serializer_class = ManufacturerPartSerializer |     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): | class ManufacturerPartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): | ||||||
|     """API endpoint for listing (and creating) a ManufacturerPartAttachment (file upload).""" |     """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): | class SupplierPriceBreakFilter(rest_filters.FilterSet): | ||||||
|     """Custom API filters for the SupplierPriceBreak list endpoint""" |     """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(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'), |         re_path('^.*$', ManufacturerPartDetail.as_view(), name='api-manufacturer-part-detail'), | ||||||
|     ])), |     ])), | ||||||
|  |  | ||||||
| @@ -579,7 +548,7 @@ manufacturer_part_api_urls = [ | |||||||
| supplier_part_api_urls = [ | supplier_part_api_urls = [ | ||||||
|  |  | ||||||
|     re_path(r'^(?P<pk>\d+)/?', include([ |     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'), |         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'^(?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'), |         re_path(r'^.*$', CompanyDetail.as_view(), name='api-company-detail'), | ||||||
|     ])), |     ])), | ||||||
|  |  | ||||||
|   | |||||||
| @@ -13,13 +13,12 @@ from rest_framework.exceptions import NotFound | |||||||
|  |  | ||||||
| import common.models | import common.models | ||||||
| import InvenTree.helpers | import InvenTree.helpers | ||||||
| from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI, | from InvenTree.api import MetadataView | ||||||
|                               RetrieveUpdateDestroyAPI) | from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI | ||||||
| from InvenTree.tasks import offload_task | from InvenTree.tasks import offload_task | ||||||
| from part.models import Part | from part.models import Part | ||||||
| from plugin.base.label import label as plugin_label | from plugin.base.label import label as plugin_label | ||||||
| from plugin.registry import registry | from plugin.registry import registry | ||||||
| from plugin.serializers import MetadataSerializer |  | ||||||
| from stock.models import StockItem, StockLocation | from stock.models import StockItem, StockLocation | ||||||
|  |  | ||||||
| from .models import PartLabel, StockItemLabel, StockLocationLabel | from .models import PartLabel, StockItemLabel, StockLocationLabel | ||||||
| @@ -307,16 +306,6 @@ class StockItemLabelDetail(StockItemLabelMixin, RetrieveUpdateDestroyAPI): | |||||||
|     pass |     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): | class StockItemLabelPrint(StockItemLabelMixin, LabelPrintMixin, RetrieveAPI): | ||||||
|     """API endpoint for printing a StockItemLabel object.""" |     """API endpoint for printing a StockItemLabel object.""" | ||||||
|     pass |     pass | ||||||
| @@ -349,16 +338,6 @@ class StockLocationLabelDetail(StockLocationLabelMixin, RetrieveUpdateDestroyAPI | |||||||
|     pass |     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): | class StockLocationLabelPrint(StockLocationLabelMixin, LabelPrintMixin, RetrieveAPI): | ||||||
|     """API endpoint for printing a StockLocationLabel object.""" |     """API endpoint for printing a StockLocationLabel object.""" | ||||||
|     pass |     pass | ||||||
| @@ -378,16 +357,6 @@ class PartLabelList(PartLabelMixin, LabelListView): | |||||||
|     pass |     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): | class PartLabelDetail(PartLabelMixin, RetrieveUpdateDestroyAPI): | ||||||
|     """API endpoint for a single PartLabel object.""" |     """API endpoint for a single PartLabel object.""" | ||||||
|     pass |     pass | ||||||
| @@ -405,7 +374,7 @@ label_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'print/?', StockItemLabelPrint.as_view(), name='api-stockitem-label-print'), |             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'), |             re_path(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'), | ||||||
|         ])), |         ])), | ||||||
|  |  | ||||||
| @@ -418,7 +387,7 @@ label_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'print/?', StockLocationLabelPrint.as_view(), name='api-stocklocation-label-print'), |             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'), |             re_path(r'^.*$', StockLocationLabelDetail.as_view(), name='api-stocklocation-label-detail'), | ||||||
|         ])), |         ])), | ||||||
|  |  | ||||||
| @@ -431,7 +400,7 @@ label_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'^print/', PartLabelPrint.as_view(), name='api-part-label-print'), |             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'), |             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 common.settings import settings | ||||||
| from company.models import SupplierPart | from company.models import SupplierPart | ||||||
| from InvenTree.api import (APIDownloadMixin, AttachmentMixin, | from InvenTree.api import (APIDownloadMixin, AttachmentMixin, | ||||||
|                            ListCreateDestroyAPIView, StatusView) |                            ListCreateDestroyAPIView, MetadataView, StatusView) | ||||||
| from InvenTree.filters import InvenTreeOrderingFilter | from InvenTree.filters import InvenTreeOrderingFilter | ||||||
| from InvenTree.helpers import DownloadFile, str2bool | from InvenTree.helpers import DownloadFile, str2bool | ||||||
| from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, | from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, | ||||||
|                               RetrieveUpdateAPI, RetrieveUpdateDestroyAPI) |                               RetrieveUpdateDestroyAPI) | ||||||
| from InvenTree.status_codes import (PurchaseOrderStatus, ReturnOrderLineStatus, | from InvenTree.status_codes import (PurchaseOrderStatus, ReturnOrderLineStatus, | ||||||
|                                     ReturnOrderStatus, SalesOrderStatus) |                                     ReturnOrderStatus, SalesOrderStatus) | ||||||
| from order.admin import (PurchaseOrderExtraLineResource, | from order.admin import (PurchaseOrderExtraLineResource, | ||||||
| @@ -32,7 +32,6 @@ from order.admin import (PurchaseOrderExtraLineResource, | |||||||
|                          ReturnOrderResource, SalesOrderExtraLineResource, |                          ReturnOrderResource, SalesOrderExtraLineResource, | ||||||
|                          SalesOrderLineItemResource, SalesOrderResource) |                          SalesOrderLineItemResource, SalesOrderResource) | ||||||
| from part.models import Part | from part.models import Part | ||||||
| from plugin.serializers import MetadataSerializer |  | ||||||
| from users.models import Owner | from users.models import Owner | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -385,16 +384,6 @@ class PurchaseOrderIssue(PurchaseOrderContextMixin, CreateAPI): | |||||||
|     serializer_class = serializers.PurchaseOrderIssueSerializer |     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): | class PurchaseOrderReceive(PurchaseOrderContextMixin, CreateAPI): | ||||||
|     """API endpoint to receive stock items against a PurchaseOrder. |     """API endpoint to receive stock items against a PurchaseOrder. | ||||||
|  |  | ||||||
| @@ -557,16 +546,6 @@ class PurchaseOrderLineItemDetail(PurchaseOrderLineItemMixin, RetrieveUpdateDest | |||||||
|     pass |     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): | class PurchaseOrderExtraLineList(GeneralExtraLineList, ListCreateAPI): | ||||||
|     """API endpoint for accessing a list of PurchaseOrderExtraLine objects.""" |     """API endpoint for accessing a list of PurchaseOrderExtraLine objects.""" | ||||||
|  |  | ||||||
| @@ -590,16 +569,6 @@ class PurchaseOrderExtraLineDetail(RetrieveUpdateDestroyAPI): | |||||||
|     serializer_class = serializers.PurchaseOrderExtraLineSerializer |     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): | class SalesOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): | ||||||
|     """API endpoint for listing (and creating) a SalesOrderAttachment (file upload)""" |     """API endpoint for listing (and creating) a SalesOrderAttachment (file upload)""" | ||||||
|  |  | ||||||
| @@ -873,16 +842,6 @@ class SalesOrderLineItemDetail(SalesOrderLineItemMixin, RetrieveUpdateDestroyAPI | |||||||
|     pass |     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): | class SalesOrderExtraLineList(GeneralExtraLineList, ListCreateAPI): | ||||||
|     """API endpoint for accessing a list of SalesOrderExtraLine objects.""" |     """API endpoint for accessing a list of SalesOrderExtraLine objects.""" | ||||||
|  |  | ||||||
| @@ -906,16 +865,6 @@ class SalesOrderExtraLineDetail(RetrieveUpdateDestroyAPI): | |||||||
|     serializer_class = serializers.SalesOrderExtraLineSerializer |     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: | class SalesOrderContextMixin: | ||||||
|     """Mixin to add sales order object as serializer context variable.""" |     """Mixin to add sales order object as serializer context variable.""" | ||||||
|  |  | ||||||
| @@ -951,16 +900,6 @@ class SalesOrderComplete(SalesOrderContextMixin, CreateAPI): | |||||||
|     serializer_class = serializers.SalesOrderCompleteSerializer |     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): | class SalesOrderAllocateSerials(SalesOrderContextMixin, CreateAPI): | ||||||
|     """API endpoint to allocation stock items against a SalesOrder, by specifying serial numbers.""" |     """API endpoint to allocation stock items against a SalesOrder, by specifying serial numbers.""" | ||||||
|  |  | ||||||
| @@ -1122,16 +1061,6 @@ class SalesOrderShipmentComplete(CreateAPI): | |||||||
|         return ctx |         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): | class PurchaseOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView): | ||||||
|     """API endpoint for listing (and creating) a PurchaseOrderAttachment (file upload)""" |     """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'^cancel/', PurchaseOrderCancel.as_view(), name='api-po-cancel'), | ||||||
|             re_path(r'^complete/', PurchaseOrderComplete.as_view(), name='api-po-complete'), |             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'^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'), |             re_path(r'^receive/', PurchaseOrderReceive.as_view(), name='api-po-receive'), | ||||||
|  |  | ||||||
|             # PurchaseOrder detail API endpoint |             # PurchaseOrder detail API endpoint | ||||||
| @@ -1621,7 +1550,7 @@ order_api_urls = [ | |||||||
|     # API endpoints for purchase order line items |     # API endpoints for purchase order line items | ||||||
|     re_path(r'^po-line/', include([ |     re_path(r'^po-line/', include([ | ||||||
|         path('<int:pk>/', 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'^.*$', PurchaseOrderLineItemDetail.as_view(), name='api-po-line-detail'), | ||||||
|         ])), |         ])), | ||||||
|         re_path(r'^.*$', PurchaseOrderLineItemList.as_view(), name='api-po-line-list'), |         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 |     # API endpoints for purchase order extra line | ||||||
|     re_path(r'^po-extra-line/', include([ |     re_path(r'^po-extra-line/', include([ | ||||||
|         path('<int:pk>/', 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'), |             re_path(r'^.*$', PurchaseOrderExtraLineDetail.as_view(), name='api-po-extra-line-detail'), | ||||||
|         ])), |         ])), | ||||||
|         path('', PurchaseOrderExtraLineList.as_view(), name='api-po-extra-line-list'), |         path('', PurchaseOrderExtraLineList.as_view(), name='api-po-extra-line-list'), | ||||||
| @@ -1646,7 +1575,7 @@ order_api_urls = [ | |||||||
|         re_path(r'^shipment/', include([ |         re_path(r'^shipment/', include([ | ||||||
|             path(r'<int:pk>/', include([ |             path(r'<int:pk>/', include([ | ||||||
|                 path('ship/', SalesOrderShipmentComplete.as_view(), name='api-so-shipment-ship'), |                 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'^.*$', SalesOrderShipmentDetail.as_view(), name='api-so-shipment-detail'), | ||||||
|             ])), |             ])), | ||||||
|             re_path(r'^.*$', SalesOrderShipmentList.as_view(), name='api-so-shipment-list'), |             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'^cancel/', SalesOrderCancel.as_view(), name='api-so-cancel'), | ||||||
|             re_path(r'^issue/', SalesOrderIssue.as_view(), name='api-so-issue'), |             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'^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 |             # SalesOrder detail endpoint | ||||||
|             re_path(r'^.*$', SalesOrderDetail.as_view(), name='api-so-detail'), |             re_path(r'^.*$', SalesOrderDetail.as_view(), name='api-so-detail'), | ||||||
| @@ -1675,7 +1604,7 @@ order_api_urls = [ | |||||||
|     # API endpoints for sales order line items |     # API endpoints for sales order line items | ||||||
|     re_path(r'^so-line/', include([ |     re_path(r'^so-line/', include([ | ||||||
|         path('<int:pk>/', 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'), |             re_path(r'^.*$', SalesOrderLineItemDetail.as_view(), name='api-so-line-detail'), | ||||||
|         ])), |         ])), | ||||||
|         path('', SalesOrderLineItemList.as_view(), name='api-so-line-list'), |         path('', SalesOrderLineItemList.as_view(), name='api-so-line-list'), | ||||||
| @@ -1684,7 +1613,7 @@ order_api_urls = [ | |||||||
|     # API endpoints for sales order extra line |     # API endpoints for sales order extra line | ||||||
|     re_path(r'^so-extra-line/', include([ |     re_path(r'^so-extra-line/', include([ | ||||||
|         path('<int:pk>/', 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'), |             re_path(r'^.*$', SalesOrderExtraLineDetail.as_view(), name='api-so-extra-line-detail'), | ||||||
|         ])), |         ])), | ||||||
|         path('', SalesOrderExtraLineList.as_view(), name='api-so-extra-line-list'), |         path('', SalesOrderExtraLineList.as_view(), name='api-so-extra-line-list'), | ||||||
|   | |||||||
| @@ -16,7 +16,7 @@ from rest_framework.response import Response | |||||||
| import order.models | import order.models | ||||||
| from build.models import Build, BuildItem | from build.models import Build, BuildItem | ||||||
| from InvenTree.api import (APIDownloadMixin, AttachmentMixin, | from InvenTree.api import (APIDownloadMixin, AttachmentMixin, | ||||||
|                            ListCreateDestroyAPIView) |                            ListCreateDestroyAPIView, MetadataView) | ||||||
| from InvenTree.filters import InvenTreeOrderingFilter | from InvenTree.filters import InvenTreeOrderingFilter | ||||||
| from InvenTree.helpers import (DownloadFile, increment_serial_number, isNull, | from InvenTree.helpers import (DownloadFile, increment_serial_number, isNull, | ||||||
|                                str2bool, str2int) |                                str2bool, str2int) | ||||||
| @@ -28,7 +28,6 @@ from InvenTree.permissions import RolePermission | |||||||
| from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus, | from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus, | ||||||
|                                     SalesOrderStatus) |                                     SalesOrderStatus) | ||||||
| from part.admin import PartCategoryResource, PartResource | from part.admin import PartCategoryResource, PartResource | ||||||
| from plugin.serializers import MetadataSerializer |  | ||||||
|  |  | ||||||
| from . import serializers as part_serializers | from . import serializers as part_serializers | ||||||
| from . import views | from . import views | ||||||
| @@ -230,16 +229,6 @@ class CategoryTree(ListAPI): | |||||||
|     ordering = ['level', 'name'] |     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): | class CategoryParameterList(ListCreateAPI): | ||||||
|     """API endpoint for accessing a list of PartCategoryParameterTemplate objects. |     """API endpoint for accessing a list of PartCategoryParameterTemplate objects. | ||||||
|  |  | ||||||
| @@ -698,16 +687,6 @@ class PartRequirements(RetrieveAPI): | |||||||
|         return Response(data) |         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): | class PartPricingDetail(RetrieveUpdateAPI): | ||||||
|     """API endpoint for viewing part pricing data""" |     """API endpoint for viewing part pricing data""" | ||||||
|  |  | ||||||
| @@ -1415,16 +1394,6 @@ class PartParameterTemplateDetail(RetrieveUpdateDestroyAPI): | |||||||
|     serializer_class = part_serializers.PartParameterTemplateSerializer |     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): | class PartParameterList(ListCreateAPI): | ||||||
|     """API endpoint for accessing a list of PartParameter objects. |     """API endpoint for accessing a list of PartParameter objects. | ||||||
|  |  | ||||||
| @@ -1886,16 +1855,6 @@ class BomItemSubstituteDetail(RetrieveUpdateDestroyAPI): | |||||||
|     serializer_class = part_serializers.BomItemSubstituteSerializer |     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 = [ | part_api_urls = [ | ||||||
|  |  | ||||||
|     # Base URL for PartCategory API endpoints |     # Base URL for PartCategory API endpoints | ||||||
| @@ -1910,7 +1869,7 @@ part_api_urls = [ | |||||||
|         # Category detail endpoints |         # Category detail endpoints | ||||||
|         path(r'<int:pk>/', include([ |         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 |             # PartCategory detail endpoint | ||||||
|             re_path(r'^.*$', CategoryDetail.as_view(), name='api-part-category-detail'), |             re_path(r'^.*$', CategoryDetail.as_view(), name='api-part-category-detail'), | ||||||
| @@ -1953,7 +1912,7 @@ part_api_urls = [ | |||||||
|     re_path(r'^parameter/', include([ |     re_path(r'^parameter/', include([ | ||||||
|         path('template/', include([ |         path('template/', include([ | ||||||
|             re_path(r'^(?P<pk>\d+)/', 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'^.*$', PartParameterTemplateDetail.as_view(), name='api-part-parameter-template-detail'), | ||||||
|             ])), |             ])), | ||||||
|             re_path(r'^.*$', PartParameterTemplateList.as_view(), name='api-part-parameter-template-list'), |             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'), |         re_path(r'^bom-validate/', PartValidateBOM.as_view(), name='api-part-bom-validate'), | ||||||
|  |  | ||||||
|         # Part metadata |         # 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 |         # Part pricing | ||||||
|         re_path(r'^pricing/', PartPricingDetail.as_view(), name='api-part-pricing'), |         re_path(r'^pricing/', PartPricingDetail.as_view(), name='api-part-pricing'), | ||||||
| @@ -2032,7 +1991,7 @@ bom_api_urls = [ | |||||||
|     # BOM Item Detail |     # BOM Item Detail | ||||||
|     path(r'<int:pk>/', include([ |     path(r'<int:pk>/', include([ | ||||||
|         re_path(r'^validate/?', BomItemValidate.as_view(), name='api-bom-item-validate'), |         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'), |         re_path(r'^.*$', BomDetail.as_view(), name='api-bom-item-detail'), | ||||||
|     ])), |     ])), | ||||||
|  |  | ||||||
|   | |||||||
| @@ -18,9 +18,8 @@ import common.models | |||||||
| import InvenTree.helpers | import InvenTree.helpers | ||||||
| import order.models | import order.models | ||||||
| import part.models | import part.models | ||||||
| from InvenTree.mixins import (ListAPI, RetrieveAPI, RetrieveUpdateAPI, | from InvenTree.api import MetadataView | ||||||
|                               RetrieveUpdateDestroyAPI) | from InvenTree.mixins import ListAPI, RetrieveAPI, RetrieveUpdateDestroyAPI | ||||||
| from plugin.serializers import MetadataSerializer |  | ||||||
| from stock.models import StockItem, StockItemAttachment | from stock.models import StockItem, StockItemAttachment | ||||||
|  |  | ||||||
| from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport, | from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport, | ||||||
| @@ -449,31 +448,6 @@ class ReturnOrderReportPrint(ReturnOrderReportMixin, ReportPrintMixin, RetrieveA | |||||||
|     pass |     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 = [ | report_api_urls = [ | ||||||
|  |  | ||||||
|     # Purchase order reports |     # Purchase order reports | ||||||
| @@ -481,7 +455,7 @@ report_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'print/', PurchaseOrderReportPrint.as_view(), name='api-po-report-print'), |             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'), |             path('', PurchaseOrderReportDetail.as_view(), name='api-po-report-detail'), | ||||||
|         ])), |         ])), | ||||||
|  |  | ||||||
| @@ -494,7 +468,7 @@ report_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'print/', SalesOrderReportPrint.as_view(), name='api-so-report-print'), |             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'), |             path('', SalesOrderReportDetail.as_view(), name='api-so-report-detail'), | ||||||
|         ])), |         ])), | ||||||
|  |  | ||||||
| @@ -505,6 +479,7 @@ report_api_urls = [ | |||||||
|     re_path(r'ro/', include([ |     re_path(r'ro/', include([ | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             path(r'print/', ReturnOrderReportPrint.as_view(), name='api-return-order-report-print'), |             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('', ReturnOrderReportDetail.as_view(), name='api-return-order-report-detail'), | ||||||
|         ])), |         ])), | ||||||
|         path('', ReturnOrderReportList.as_view(), name='api-return-order-report-list'), |         path('', ReturnOrderReportList.as_view(), name='api-return-order-report-list'), | ||||||
| @@ -515,7 +490,7 @@ report_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'print/?', BuildReportPrint.as_view(), name='api-build-report-print'), |             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'), |             re_path(r'^.$', BuildReportDetail.as_view(), name='api-build-report-detail'), | ||||||
|         ])), |         ])), | ||||||
|  |  | ||||||
| @@ -529,7 +504,7 @@ report_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'print/?', BOMReportPrint.as_view(), name='api-bom-report-print'), |             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'), |             re_path(r'^.*$', BOMReportDetail.as_view(), name='api-bom-report-detail'), | ||||||
|         ])), |         ])), | ||||||
|  |  | ||||||
| @@ -542,7 +517,7 @@ report_api_urls = [ | |||||||
|         # Detail views |         # Detail views | ||||||
|         path(r'<int:pk>/', include([ |         path(r'<int:pk>/', include([ | ||||||
|             re_path(r'print/?', StockItemTestReportPrint.as_view(), name='api-stockitem-testreport-print'), |             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'), |             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.models import Company, SupplierPart | ||||||
| from company.serializers import CompanySerializer, SupplierPartSerializer | from company.serializers import CompanySerializer, SupplierPartSerializer | ||||||
| from InvenTree.api import (APIDownloadMixin, AttachmentMixin, | from InvenTree.api import (APIDownloadMixin, AttachmentMixin, | ||||||
|                            ListCreateDestroyAPIView, StatusView) |                            ListCreateDestroyAPIView, MetadataView, StatusView) | ||||||
| from InvenTree.filters import InvenTreeOrderingFilter | from InvenTree.filters import InvenTreeOrderingFilter | ||||||
| from InvenTree.helpers import (DownloadFile, extract_serial_numbers, isNull, | from InvenTree.helpers import (DownloadFile, extract_serial_numbers, isNull, | ||||||
|                                str2bool, str2int) |                                str2bool, str2int) | ||||||
| from InvenTree.mixins import (CreateAPI, CustomRetrieveUpdateDestroyAPI, | from InvenTree.mixins import (CreateAPI, CustomRetrieveUpdateDestroyAPI, | ||||||
|                               ListAPI, ListCreateAPI, RetrieveAPI, |                               ListAPI, ListCreateAPI, RetrieveAPI, | ||||||
|                               RetrieveUpdateAPI, RetrieveUpdateDestroyAPI) |                               RetrieveUpdateDestroyAPI) | ||||||
| from InvenTree.status_codes import StockHistoryCode, StockStatus | from InvenTree.status_codes import StockHistoryCode, StockStatus | ||||||
| from order.models import (PurchaseOrder, ReturnOrder, SalesOrder, | from order.models import (PurchaseOrder, ReturnOrder, SalesOrder, | ||||||
|                           SalesOrderAllocation) |                           SalesOrderAllocation) | ||||||
| @@ -37,7 +37,6 @@ from order.serializers import (PurchaseOrderSerializer, ReturnOrderSerializer, | |||||||
|                                SalesOrderSerializer) |                                SalesOrderSerializer) | ||||||
| from part.models import BomItem, Part, PartCategory | from part.models import BomItem, Part, PartCategory | ||||||
| from part.serializers import PartBriefSerializer | from part.serializers import PartBriefSerializer | ||||||
| from plugin.serializers import MetadataSerializer |  | ||||||
| from stock.admin import LocationResource, StockItemResource | from stock.admin import LocationResource, StockItemResource | ||||||
| from stock.models import (StockItem, StockItemAttachment, StockItemTestResult, | from stock.models import (StockItem, StockItemAttachment, StockItemTestResult, | ||||||
|                           StockItemTracking, StockLocation) |                           StockItemTracking, StockLocation) | ||||||
| @@ -83,16 +82,6 @@ class StockDetail(RetrieveUpdateDestroyAPI): | |||||||
|         return self.serializer_class(*args, **kwargs) |         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: | class StockItemContextMixin: | ||||||
|     """Mixin class for adding StockItem object to serializer context.""" |     """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): | class LocationDetail(CustomRetrieveUpdateDestroyAPI): | ||||||
|     """API endpoint for detail view of StockLocation object. |     """API endpoint for detail view of StockLocation object. | ||||||
|  |  | ||||||
| @@ -1391,7 +1370,7 @@ stock_api_urls = [ | |||||||
|         # Stock location detail endpoints |         # Stock location detail endpoints | ||||||
|         path(r'<int:pk>/', include([ |         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'), |             re_path(r'^.*$', LocationDetail.as_view(), name='api-location-detail'), | ||||||
|         ])), |         ])), | ||||||
| @@ -1433,7 +1412,7 @@ stock_api_urls = [ | |||||||
|     path(r'<int:pk>/', include([ |     path(r'<int:pk>/', include([ | ||||||
|         re_path(r'^convert/', StockItemConvert.as_view(), name='api-stock-item-convert'), |         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'^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'^return/', StockItemReturn.as_view(), name='api-stock-item-return'), | ||||||
|         re_path(r'^serialize/', StockItemSerialize.as_view(), name='api-stock-item-serialize'), |         re_path(r'^serialize/', StockItemSerialize.as_view(), name='api-stock-item-serialize'), | ||||||
|         re_path(r'^uninstall/', StockItemUninstall.as_view(), name='api-stock-item-uninstall'), |         re_path(r'^uninstall/', StockItemUninstall.as_view(), name='api-stock-item-uninstall'), | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user