mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-03 13:58:47 +00:00
* use shims for API view inheritation * Add mixin for input sanitation * fix clean operation to fix all string values * Also clean up dicts this is to future-proof this function * Update docstirng * proof custom methods against XSS through authenticated users
This commit is contained in:
parent
f8a2760955
commit
e83995b4f5
@ -6,10 +6,12 @@ from django.http import JsonResponse
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics, permissions
|
from rest_framework import filters, permissions
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
|
from InvenTree.mixins import ListCreateAPI
|
||||||
|
|
||||||
from .status import is_worker_running
|
from .status import is_worker_running
|
||||||
from .version import (inventreeApiVersion, inventreeInstanceName,
|
from .version import (inventreeApiVersion, inventreeInstanceName,
|
||||||
inventreeVersion)
|
inventreeVersion)
|
||||||
@ -134,7 +136,7 @@ class BulkDeleteMixin:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class ListCreateDestroyAPIView(BulkDeleteMixin, generics.ListCreateAPIView):
|
class ListCreateDestroyAPIView(BulkDeleteMixin, ListCreateAPI):
|
||||||
"""Custom API endpoint which provides BulkDelete functionality in addition to List and Create"""
|
"""Custom API endpoint which provides BulkDelete functionality in addition to List and Create"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
90
InvenTree/InvenTree/mixins.py
Normal file
90
InvenTree/InvenTree/mixins.py
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
"""Mixins for (API) views in the whole project."""
|
||||||
|
|
||||||
|
from bleach import clean
|
||||||
|
from rest_framework import generics, status
|
||||||
|
from rest_framework.response import Response
|
||||||
|
|
||||||
|
|
||||||
|
class CleanMixin():
|
||||||
|
"""Model mixin class which cleans inputs."""
|
||||||
|
|
||||||
|
# Define a map of fields avaialble for import
|
||||||
|
SAFE_FIELDS = {}
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""Override to clean data before processing it."""
|
||||||
|
serializer = self.get_serializer(data=self.clean_data(request.data))
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
self.perform_create(serializer)
|
||||||
|
headers = self.get_success_headers(serializer.data)
|
||||||
|
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|
||||||
|
def update(self, request, *args, **kwargs):
|
||||||
|
"""Override to clean data before processing it."""
|
||||||
|
partial = kwargs.pop('partial', False)
|
||||||
|
instance = self.get_object()
|
||||||
|
serializer = self.get_serializer(instance, data=self.clean_data(request.data), partial=partial)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
self.perform_update(serializer)
|
||||||
|
|
||||||
|
if getattr(instance, '_prefetched_objects_cache', None):
|
||||||
|
# If 'prefetch_related' has been applied to a queryset, we need to
|
||||||
|
# forcibly invalidate the prefetch cache on the instance.
|
||||||
|
instance._prefetched_objects_cache = {}
|
||||||
|
|
||||||
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
def clean_data(self, data: dict) -> dict:
|
||||||
|
"""Clean / snatize data.
|
||||||
|
|
||||||
|
This uses mozillas bleach under the hood to disable certain html tags by
|
||||||
|
encoding them - this leads to script tags etc. to not work.
|
||||||
|
The results can be longer then the input; might make some character combinations
|
||||||
|
`ugly`. Prevents XSS on the server-level.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
data (dict): Data that should be sanatized.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
dict: Profided data sanatized; still in the same order.
|
||||||
|
"""
|
||||||
|
clean_data = {}
|
||||||
|
for k, v in data.items():
|
||||||
|
if isinstance(v, str):
|
||||||
|
ret = clean(v)
|
||||||
|
elif isinstance(v, dict):
|
||||||
|
ret = self.clean_data(v)
|
||||||
|
else:
|
||||||
|
ret = v
|
||||||
|
clean_data[k] = ret
|
||||||
|
return clean_data
|
||||||
|
|
||||||
|
|
||||||
|
class ListAPI(generics.ListAPIView):
|
||||||
|
"""View for list API."""
|
||||||
|
|
||||||
|
|
||||||
|
class ListCreateAPI(CleanMixin, generics.ListCreateAPIView):
|
||||||
|
"""View for list and create API."""
|
||||||
|
|
||||||
|
|
||||||
|
class CreateAPI(CleanMixin, generics.CreateAPIView):
|
||||||
|
"""View for create API."""
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveAPI(generics.RetrieveAPIView):
|
||||||
|
"""View for retreive API."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUpdateAPI(CleanMixin, generics.RetrieveUpdateAPIView):
|
||||||
|
"""View for retrieve and update API."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class RetrieveUpdateDestroyAPI(CleanMixin, generics.RetrieveUpdateDestroyAPIView):
|
||||||
|
"""View for retrieve, update and destroy API."""
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateAPI(CleanMixin, generics.UpdateAPIView):
|
||||||
|
"""View for update API."""
|
@ -3,7 +3,7 @@
|
|||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
@ -13,6 +13,7 @@ from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAP
|
|||||||
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, RetrieveUpdateDestroyAPI, ListCreateAPI
|
||||||
|
|
||||||
import build.admin
|
import build.admin
|
||||||
import build.serializers
|
import build.serializers
|
||||||
@ -65,7 +66,7 @@ class BuildFilter(rest_filters.FilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
|
class BuildList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of Build objects.
|
"""API endpoint for accessing a list of Build objects.
|
||||||
|
|
||||||
- GET: Return list of objects (with filters)
|
- GET: Return list of objects (with filters)
|
||||||
@ -200,7 +201,7 @@ class BuildList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
return self.serializer_class(*args, **kwargs)
|
return self.serializer_class(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class BuildDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BuildDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a Build object."""
|
"""API endpoint for detail view of a Build object."""
|
||||||
|
|
||||||
queryset = Build.objects.all()
|
queryset = Build.objects.all()
|
||||||
@ -219,7 +220,7 @@ class BuildDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return super().destroy(request, *args, **kwargs)
|
return super().destroy(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class BuildUnallocate(generics.CreateAPIView):
|
class BuildUnallocate(CreateAPI):
|
||||||
"""API endpoint for unallocating stock items from a build order.
|
"""API endpoint for unallocating stock items from a build order.
|
||||||
|
|
||||||
- The BuildOrder object is specified by the URL
|
- The BuildOrder object is specified by the URL
|
||||||
@ -263,7 +264,7 @@ class BuildOrderContextMixin:
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputCreate(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildOutputCreate(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for creating new build output(s)."""
|
"""API endpoint for creating new build output(s)."""
|
||||||
|
|
||||||
queryset = Build.objects.none()
|
queryset = Build.objects.none()
|
||||||
@ -271,7 +272,7 @@ class BuildOutputCreate(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildOutputCreateSerializer
|
serializer_class = build.serializers.BuildOutputCreateSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputComplete(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildOutputComplete(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for completing build outputs."""
|
"""API endpoint for completing build outputs."""
|
||||||
|
|
||||||
queryset = Build.objects.none()
|
queryset = Build.objects.none()
|
||||||
@ -279,7 +280,7 @@ class BuildOutputComplete(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildOutputCompleteSerializer
|
serializer_class = build.serializers.BuildOutputCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputDelete(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildOutputDelete(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for deleting multiple build outputs."""
|
"""API endpoint for deleting multiple build outputs."""
|
||||||
|
|
||||||
def get_serializer_context(self):
|
def get_serializer_context(self):
|
||||||
@ -295,7 +296,7 @@ class BuildOutputDelete(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildOutputDeleteSerializer
|
serializer_class = build.serializers.BuildOutputDeleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildFinish(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildFinish(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for marking a build as finished (completed)."""
|
"""API endpoint for marking a build as finished (completed)."""
|
||||||
|
|
||||||
queryset = Build.objects.none()
|
queryset = Build.objects.none()
|
||||||
@ -303,7 +304,7 @@ class BuildFinish(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildCompleteSerializer
|
serializer_class = build.serializers.BuildCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildAutoAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildAutoAllocate(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for 'automatically' allocating stock against a build order.
|
"""API endpoint for 'automatically' allocating stock against a build order.
|
||||||
|
|
||||||
- Only looks at 'untracked' parts
|
- Only looks at 'untracked' parts
|
||||||
@ -317,7 +318,7 @@ class BuildAutoAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildAutoAllocationSerializer
|
serializer_class = build.serializers.BuildAutoAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildAllocate(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to allocate stock items to a build order.
|
"""API endpoint to allocate stock items to a build order.
|
||||||
|
|
||||||
- The BuildOrder object is specified by the URL
|
- The BuildOrder object is specified by the URL
|
||||||
@ -333,21 +334,21 @@ class BuildAllocate(BuildOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = build.serializers.BuildAllocationSerializer
|
serializer_class = build.serializers.BuildAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildCancel(BuildOrderContextMixin, generics.CreateAPIView):
|
class BuildCancel(BuildOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for cancelling a BuildOrder."""
|
"""API endpoint for cancelling a BuildOrder."""
|
||||||
|
|
||||||
queryset = Build.objects.all()
|
queryset = Build.objects.all()
|
||||||
serializer_class = build.serializers.BuildCancelSerializer
|
serializer_class = build.serializers.BuildCancelSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BuildItemDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a BuildItem object."""
|
"""API endpoint for detail view of a BuildItem object."""
|
||||||
|
|
||||||
queryset = BuildItem.objects.all()
|
queryset = BuildItem.objects.all()
|
||||||
serializer_class = build.serializers.BuildItemSerializer
|
serializer_class = build.serializers.BuildItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildItemList(generics.ListCreateAPIView):
|
class BuildItemList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of BuildItem objects.
|
"""API endpoint for accessing a list of BuildItem objects.
|
||||||
|
|
||||||
- GET: Return list of objects
|
- GET: Return list of objects
|
||||||
@ -442,7 +443,7 @@ class BuildAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BuildAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class BuildAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for a BuildOrderAttachment object."""
|
"""Detail endpoint for a BuildOrderAttachment object."""
|
||||||
|
|
||||||
queryset = BuildOrderAttachment.objects.all()
|
queryset = BuildOrderAttachment.objects.all()
|
||||||
|
@ -9,7 +9,7 @@ from django.views.decorators.csrf import csrf_exempt
|
|||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from django_q.tasks import async_task
|
from django_q.tasks import async_task
|
||||||
from rest_framework import filters, generics, permissions, serializers
|
from rest_framework import filters, permissions, serializers
|
||||||
from rest_framework.exceptions import NotAcceptable, NotFound
|
from rest_framework.exceptions import NotAcceptable, NotFound
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
@ -18,6 +18,8 @@ import common.models
|
|||||||
import common.serializers
|
import common.serializers
|
||||||
from InvenTree.api import BulkDeleteMixin
|
from InvenTree.api import BulkDeleteMixin
|
||||||
from InvenTree.helpers import inheritors
|
from InvenTree.helpers import inheritors
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, RetrieveAPI,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
|
||||||
from plugin.models import NotificationUserSetting
|
from plugin.models import NotificationUserSetting
|
||||||
from plugin.serializers import NotificationUserSettingSerializer
|
from plugin.serializers import NotificationUserSettingSerializer
|
||||||
|
|
||||||
@ -97,7 +99,7 @@ class WebhookView(CsrfExemptMixin, APIView):
|
|||||||
raise NotFound()
|
raise NotFound()
|
||||||
|
|
||||||
|
|
||||||
class SettingsList(generics.ListAPIView):
|
class SettingsList(ListAPI):
|
||||||
"""Generic ListView for settings.
|
"""Generic ListView for settings.
|
||||||
|
|
||||||
This is inheritted by all list views for settings.
|
This is inheritted by all list views for settings.
|
||||||
@ -145,7 +147,7 @@ class GlobalSettingsPermissions(permissions.BasePermission):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
class GlobalSettingsDetail(generics.RetrieveUpdateAPIView):
|
class GlobalSettingsDetail(RetrieveUpdateAPI):
|
||||||
"""Detail view for an individual "global setting" object.
|
"""Detail view for an individual "global setting" object.
|
||||||
|
|
||||||
- User must have 'staff' status to view / edit
|
- User must have 'staff' status to view / edit
|
||||||
@ -203,7 +205,7 @@ class UserSettingsPermissions(permissions.BasePermission):
|
|||||||
return user == obj.user
|
return user == obj.user
|
||||||
|
|
||||||
|
|
||||||
class UserSettingsDetail(generics.RetrieveUpdateAPIView):
|
class UserSettingsDetail(RetrieveUpdateAPI):
|
||||||
"""Detail view for an individual "user setting" object.
|
"""Detail view for an individual "user setting" object.
|
||||||
|
|
||||||
- User can only view / edit settings their own settings objects
|
- User can only view / edit settings their own settings objects
|
||||||
@ -245,7 +247,7 @@ class NotificationUserSettingsList(SettingsList):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class NotificationUserSettingsDetail(generics.RetrieveUpdateAPIView):
|
class NotificationUserSettingsDetail(RetrieveUpdateAPI):
|
||||||
"""Detail view for an individual "notification user setting" object.
|
"""Detail view for an individual "notification user setting" object.
|
||||||
|
|
||||||
- User can only view / edit settings their own settings objects
|
- User can only view / edit settings their own settings objects
|
||||||
@ -259,7 +261,7 @@ class NotificationUserSettingsDetail(generics.RetrieveUpdateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NotificationList(BulkDeleteMixin, generics.ListAPIView):
|
class NotificationList(BulkDeleteMixin, ListAPI):
|
||||||
"""List view for all notifications of the current user."""
|
"""List view for all notifications of the current user."""
|
||||||
|
|
||||||
queryset = common.models.NotificationMessage.objects.all()
|
queryset = common.models.NotificationMessage.objects.all()
|
||||||
@ -310,7 +312,7 @@ class NotificationList(BulkDeleteMixin, generics.ListAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class NotificationDetail(generics.RetrieveUpdateDestroyAPIView):
|
class NotificationDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail view for an individual notification object.
|
"""Detail view for an individual notification object.
|
||||||
|
|
||||||
- User can only view / delete their own notification objects
|
- User can only view / delete their own notification objects
|
||||||
@ -323,7 +325,7 @@ class NotificationDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class NotificationReadEdit(generics.CreateAPIView):
|
class NotificationReadEdit(CreateAPI):
|
||||||
"""General API endpoint to manipulate read state of a notification."""
|
"""General API endpoint to manipulate read state of a notification."""
|
||||||
|
|
||||||
queryset = common.models.NotificationMessage.objects.all()
|
queryset = common.models.NotificationMessage.objects.all()
|
||||||
@ -360,7 +362,7 @@ class NotificationUnread(NotificationReadEdit):
|
|||||||
target = False
|
target = False
|
||||||
|
|
||||||
|
|
||||||
class NotificationReadAll(generics.RetrieveAPIView):
|
class NotificationReadAll(RetrieveAPI):
|
||||||
"""API endpoint to mark all notifications as read."""
|
"""API endpoint to mark all notifications as read."""
|
||||||
|
|
||||||
queryset = common.models.NotificationMessage.objects.all()
|
queryset = common.models.NotificationMessage.objects.all()
|
||||||
|
@ -5,10 +5,11 @@ from django.urls import include, re_path
|
|||||||
|
|
||||||
from django_filters import rest_framework as rest_filters
|
from django_filters import rest_framework as rest_filters
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
|
|
||||||
from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView
|
from InvenTree.api import AttachmentMixin, ListCreateDestroyAPIView
|
||||||
from InvenTree.helpers import str2bool
|
from InvenTree.helpers import str2bool
|
||||||
|
from InvenTree.mixins import ListCreateAPI, RetrieveUpdateDestroyAPI
|
||||||
|
|
||||||
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
from .models import (Company, ManufacturerPart, ManufacturerPartAttachment,
|
||||||
ManufacturerPartParameter, SupplierPart,
|
ManufacturerPartParameter, SupplierPart,
|
||||||
@ -20,7 +21,7 @@ from .serializers import (CompanySerializer,
|
|||||||
SupplierPriceBreakSerializer)
|
SupplierPriceBreakSerializer)
|
||||||
|
|
||||||
|
|
||||||
class CompanyList(generics.ListCreateAPIView):
|
class CompanyList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of Company objects.
|
"""API endpoint for accessing a list of Company objects.
|
||||||
|
|
||||||
Provides two methods:
|
Provides two methods:
|
||||||
@ -67,7 +68,7 @@ class CompanyList(generics.ListCreateAPIView):
|
|||||||
ordering = 'name'
|
ordering = 'name'
|
||||||
|
|
||||||
|
|
||||||
class CompanyDetail(generics.RetrieveUpdateDestroyAPIView):
|
class CompanyDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail of a single Company object."""
|
"""API endpoint for detail of a single Company object."""
|
||||||
|
|
||||||
queryset = Company.objects.all()
|
queryset = Company.objects.all()
|
||||||
@ -146,7 +147,7 @@ class ManufacturerPartList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class ManufacturerPartDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of ManufacturerPart object.
|
"""API endpoint for detail view of ManufacturerPart object.
|
||||||
|
|
||||||
- GET: Retrieve detail view
|
- GET: Retrieve detail view
|
||||||
@ -173,7 +174,7 @@ class ManufacturerPartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class ManufacturerPartAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpooint for ManufacturerPartAttachment model."""
|
"""Detail endpooint for ManufacturerPartAttachment model."""
|
||||||
|
|
||||||
queryset = ManufacturerPartAttachment.objects.all()
|
queryset = ManufacturerPartAttachment.objects.all()
|
||||||
@ -246,7 +247,7 @@ class ManufacturerPartParameterList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartParameterDetail(generics.RetrieveUpdateDestroyAPIView):
|
class ManufacturerPartParameterDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of ManufacturerPartParameter model."""
|
"""API endpoint for detail view of ManufacturerPartParameter model."""
|
||||||
|
|
||||||
queryset = ManufacturerPartParameter.objects.all()
|
queryset = ManufacturerPartParameter.objects.all()
|
||||||
@ -347,7 +348,7 @@ class SupplierPartList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SupplierPartDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of SupplierPart object.
|
"""API endpoint for detail view of SupplierPart object.
|
||||||
|
|
||||||
- GET: Retrieve detail view
|
- GET: Retrieve detail view
|
||||||
@ -362,7 +363,7 @@ class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SupplierPriceBreakList(generics.ListCreateAPIView):
|
class SupplierPriceBreakList(ListCreateAPI):
|
||||||
"""API endpoint for list view of SupplierPriceBreak object.
|
"""API endpoint for list view of SupplierPriceBreak object.
|
||||||
|
|
||||||
- GET: Retrieve list of SupplierPriceBreak objects
|
- GET: Retrieve list of SupplierPriceBreak objects
|
||||||
@ -381,7 +382,7 @@ class SupplierPriceBreakList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SupplierPriceBreakDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SupplierPriceBreakDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for SupplierPriceBreak object."""
|
"""Detail endpoint for SupplierPriceBreak object."""
|
||||||
|
|
||||||
queryset = SupplierPriceBreak.objects.all()
|
queryset = SupplierPriceBreak.objects.all()
|
||||||
|
@ -6,11 +6,12 @@ from django.http import HttpResponse, JsonResponse
|
|||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
|
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
|
||||||
@ -22,7 +23,7 @@ from .serializers import (PartLabelSerializer, StockItemLabelSerializer,
|
|||||||
StockLocationLabelSerializer)
|
StockLocationLabelSerializer)
|
||||||
|
|
||||||
|
|
||||||
class LabelListView(generics.ListAPIView):
|
class LabelListView(ListAPI):
|
||||||
"""Generic API class for label templates."""
|
"""Generic API class for label templates."""
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
@ -275,14 +276,14 @@ class StockItemLabelList(LabelListView, StockItemLabelMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class StockItemLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockItemLabelDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single StockItemLabel object."""
|
"""API endpoint for a single StockItemLabel object."""
|
||||||
|
|
||||||
queryset = StockItemLabel.objects.all()
|
queryset = StockItemLabel.objects.all()
|
||||||
serializer_class = StockItemLabelSerializer
|
serializer_class = StockItemLabelSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemLabelPrint(generics.RetrieveAPIView, StockItemLabelMixin, LabelPrintMixin):
|
class StockItemLabelPrint(RetrieveAPI, StockItemLabelMixin, LabelPrintMixin):
|
||||||
"""API endpoint for printing a StockItemLabel object."""
|
"""API endpoint for printing a StockItemLabel object."""
|
||||||
|
|
||||||
queryset = StockItemLabel.objects.all()
|
queryset = StockItemLabel.objects.all()
|
||||||
@ -391,14 +392,14 @@ class StockLocationLabelList(LabelListView, StockLocationLabelMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class StockLocationLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockLocationLabelDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single StockLocationLabel object."""
|
"""API endpoint for a single StockLocationLabel object."""
|
||||||
|
|
||||||
queryset = StockLocationLabel.objects.all()
|
queryset = StockLocationLabel.objects.all()
|
||||||
serializer_class = StockLocationLabelSerializer
|
serializer_class = StockLocationLabelSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockLocationLabelPrint(generics.RetrieveAPIView, StockLocationLabelMixin, LabelPrintMixin):
|
class StockLocationLabelPrint(RetrieveAPI, StockLocationLabelMixin, LabelPrintMixin):
|
||||||
"""API endpoint for printing a StockLocationLabel object."""
|
"""API endpoint for printing a StockLocationLabel object."""
|
||||||
|
|
||||||
queryset = StockLocationLabel.objects.all()
|
queryset = StockLocationLabel.objects.all()
|
||||||
@ -483,14 +484,14 @@ class PartLabelList(LabelListView, PartLabelMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PartLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartLabelDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single PartLabel object."""
|
"""API endpoint for a single PartLabel object."""
|
||||||
|
|
||||||
queryset = PartLabel.objects.all()
|
queryset = PartLabel.objects.all()
|
||||||
serializer_class = PartLabelSerializer
|
serializer_class = PartLabelSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartLabelPrint(generics.RetrieveAPIView, PartLabelMixin, LabelPrintMixin):
|
class PartLabelPrint(RetrieveAPI, PartLabelMixin, LabelPrintMixin):
|
||||||
"""API endpoint for printing a PartLabel object."""
|
"""API endpoint for printing a PartLabel object."""
|
||||||
|
|
||||||
queryset = PartLabel.objects.all()
|
queryset = PartLabel.objects.all()
|
||||||
|
@ -4,7 +4,7 @@ from django.db.models import F, Q
|
|||||||
from django.urls import include, path, re_path
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
from django_filters import rest_framework as rest_filters
|
from django_filters import rest_framework as rest_filters
|
||||||
from rest_framework import filters, generics, status
|
from rest_framework import filters, status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import order.models as models
|
import order.models as models
|
||||||
@ -14,6 +14,8 @@ from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
|||||||
ListCreateDestroyAPIView)
|
ListCreateDestroyAPIView)
|
||||||
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,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
|
||||||
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus
|
from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus
|
||||||
from order.admin import (PurchaseOrderLineItemResource, PurchaseOrderResource,
|
from order.admin import (PurchaseOrderLineItemResource, PurchaseOrderResource,
|
||||||
SalesOrderResource)
|
SalesOrderResource)
|
||||||
@ -101,7 +103,7 @@ class PurchaseOrderFilter(rest_filters.FilterSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
class PurchaseOrderList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PurchaseOrder objects.
|
"""API endpoint for accessing a list of PurchaseOrder objects.
|
||||||
|
|
||||||
- GET: Return list of PurchaseOrder objects (with filters)
|
- GET: Return list of PurchaseOrder objects (with filters)
|
||||||
@ -114,7 +116,7 @@ class PurchaseOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Save user information on create."""
|
"""Save user information on create."""
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=self.clean_data(request.data))
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
item = serializer.save()
|
item = serializer.save()
|
||||||
@ -254,7 +256,7 @@ class PurchaseOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
ordering = '-creation_date'
|
ordering = '-creation_date'
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a PurchaseOrder object."""
|
"""API endpoint for detail view of a PurchaseOrder object."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
@ -304,7 +306,7 @@ class PurchaseOrderContextMixin:
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderCancel(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderCancel(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to 'cancel' a purchase order.
|
"""API endpoint to 'cancel' a purchase order.
|
||||||
|
|
||||||
The purchase order must be in a state which can be cancelled
|
The purchase order must be in a state which can be cancelled
|
||||||
@ -315,7 +317,7 @@ class PurchaseOrderCancel(PurchaseOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.PurchaseOrderCancelSerializer
|
serializer_class = serializers.PurchaseOrderCancelSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderComplete(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderComplete(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to 'complete' a purchase order."""
|
"""API endpoint to 'complete' a purchase order."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
@ -323,7 +325,7 @@ class PurchaseOrderComplete(PurchaseOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.PurchaseOrderCompleteSerializer
|
serializer_class = serializers.PurchaseOrderCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderIssue(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderIssue(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to 'complete' a purchase order."""
|
"""API endpoint to 'complete' a purchase order."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
@ -331,7 +333,7 @@ class PurchaseOrderIssue(PurchaseOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.PurchaseOrderIssueSerializer
|
serializer_class = serializers.PurchaseOrderIssueSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderMetadata(generics.RetrieveUpdateAPIView):
|
class PurchaseOrderMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating PurchaseOrder metadata."""
|
"""API endpoint for viewing / updating PurchaseOrder metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -341,7 +343,7 @@ class PurchaseOrderMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = models.PurchaseOrder.objects.all()
|
queryset = models.PurchaseOrder.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReceive(PurchaseOrderContextMixin, generics.CreateAPIView):
|
class PurchaseOrderReceive(PurchaseOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to receive stock items against a purchase order.
|
"""API endpoint to receive stock items against a purchase order.
|
||||||
|
|
||||||
- The purchase order is specified in the URL.
|
- The purchase order is specified in the URL.
|
||||||
@ -405,7 +407,7 @@ class PurchaseOrderLineItemFilter(rest_filters.FilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItemList(APIDownloadMixin, generics.ListCreateAPIView):
|
class PurchaseOrderLineItemList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PurchaseOrderLineItem objects.
|
"""API endpoint for accessing a list of PurchaseOrderLineItem objects.
|
||||||
|
|
||||||
- GET: Return a list of PurchaseOrder Line Item objects
|
- GET: Return a list of PurchaseOrder Line Item objects
|
||||||
@ -499,7 +501,7 @@ class PurchaseOrderLineItemList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderLineItemDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail API endpoint for PurchaseOrderLineItem object."""
|
"""Detail API endpoint for PurchaseOrderLineItem object."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderLineItem.objects.all()
|
queryset = models.PurchaseOrderLineItem.objects.all()
|
||||||
@ -514,14 +516,14 @@ class PurchaseOrderLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderExtraLineList(GeneralExtraLineList, generics.ListCreateAPIView):
|
class PurchaseOrderExtraLineList(GeneralExtraLineList, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PurchaseOrderExtraLine objects."""
|
"""API endpoint for accessing a list of PurchaseOrderExtraLine objects."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderExtraLine.objects.all()
|
queryset = models.PurchaseOrderExtraLine.objects.all()
|
||||||
serializer_class = serializers.PurchaseOrderExtraLineSerializer
|
serializer_class = serializers.PurchaseOrderExtraLineSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderExtraLineDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderExtraLineDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a PurchaseOrderExtraLine object."""
|
"""API endpoint for detail view of a PurchaseOrderExtraLine object."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderExtraLine.objects.all()
|
queryset = models.PurchaseOrderExtraLine.objects.all()
|
||||||
@ -543,14 +545,14 @@ class SalesOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for SalesOrderAttachment."""
|
"""Detail endpoint for SalesOrderAttachment."""
|
||||||
|
|
||||||
queryset = models.SalesOrderAttachment.objects.all()
|
queryset = models.SalesOrderAttachment.objects.all()
|
||||||
serializer_class = serializers.SalesOrderAttachmentSerializer
|
serializer_class = serializers.SalesOrderAttachmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
class SalesOrderList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of SalesOrder objects.
|
"""API endpoint for accessing a list of SalesOrder objects.
|
||||||
|
|
||||||
- GET: Return list of SalesOrder objects (with filters)
|
- GET: Return list of SalesOrder objects (with filters)
|
||||||
@ -562,7 +564,7 @@ class SalesOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Save user information on create."""
|
"""Save user information on create."""
|
||||||
serializer = self.get_serializer(data=request.data)
|
serializer = self.get_serializer(data=self.clean_data(request.data))
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
item = serializer.save()
|
item = serializer.save()
|
||||||
@ -695,7 +697,7 @@ class SalesOrderList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
ordering = '-creation_date'
|
ordering = '-creation_date'
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a SalesOrder object."""
|
"""API endpoint for detail view of a SalesOrder object."""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
@ -754,7 +756,7 @@ class SalesOrderLineItemFilter(rest_filters.FilterSet):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderLineItemList(generics.ListCreateAPIView):
|
class SalesOrderLineItemList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of SalesOrderLineItem objects."""
|
"""API endpoint for accessing a list of SalesOrderLineItem objects."""
|
||||||
|
|
||||||
queryset = models.SalesOrderLineItem.objects.all()
|
queryset = models.SalesOrderLineItem.objects.all()
|
||||||
@ -818,21 +820,21 @@ class SalesOrderLineItemList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderExtraLineList(GeneralExtraLineList, generics.ListCreateAPIView):
|
class SalesOrderExtraLineList(GeneralExtraLineList, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of SalesOrderExtraLine objects."""
|
"""API endpoint for accessing a list of SalesOrderExtraLine objects."""
|
||||||
|
|
||||||
queryset = models.SalesOrderExtraLine.objects.all()
|
queryset = models.SalesOrderExtraLine.objects.all()
|
||||||
serializer_class = serializers.SalesOrderExtraLineSerializer
|
serializer_class = serializers.SalesOrderExtraLineSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderExtraLineDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderExtraLineDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a SalesOrderExtraLine object."""
|
"""API endpoint for detail view of a SalesOrderExtraLine object."""
|
||||||
|
|
||||||
queryset = models.SalesOrderExtraLine.objects.all()
|
queryset = models.SalesOrderExtraLine.objects.all()
|
||||||
serializer_class = serializers.SalesOrderExtraLineSerializer
|
serializer_class = serializers.SalesOrderExtraLineSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderLineItemDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderLineItemDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a SalesOrderLineItem object."""
|
"""API endpoint for detail view of a SalesOrderLineItem object."""
|
||||||
|
|
||||||
queryset = models.SalesOrderLineItem.objects.all()
|
queryset = models.SalesOrderLineItem.objects.all()
|
||||||
@ -864,21 +866,21 @@ class SalesOrderContextMixin:
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderCancel(SalesOrderContextMixin, generics.CreateAPIView):
|
class SalesOrderCancel(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to cancel a SalesOrder"""
|
"""API endpoint to cancel a SalesOrder"""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
serializer_class = serializers.SalesOrderCancelSerializer
|
serializer_class = serializers.SalesOrderCancelSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderComplete(SalesOrderContextMixin, generics.CreateAPIView):
|
class SalesOrderComplete(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint for manually marking a SalesOrder as "complete"."""
|
"""API endpoint for manually marking a SalesOrder as "complete"."""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
serializer_class = serializers.SalesOrderCompleteSerializer
|
serializer_class = serializers.SalesOrderCompleteSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderMetadata(generics.RetrieveUpdateAPIView):
|
class SalesOrderMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating SalesOrder metadata."""
|
"""API endpoint for viewing / updating SalesOrder metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -888,14 +890,14 @@ class SalesOrderMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = models.SalesOrder.objects.all()
|
queryset = models.SalesOrder.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocateSerials(SalesOrderContextMixin, generics.CreateAPIView):
|
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."""
|
||||||
|
|
||||||
queryset = models.SalesOrder.objects.none()
|
queryset = models.SalesOrder.objects.none()
|
||||||
serializer_class = serializers.SalesOrderSerialAllocationSerializer
|
serializer_class = serializers.SalesOrderSerialAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocate(SalesOrderContextMixin, generics.CreateAPIView):
|
class SalesOrderAllocate(SalesOrderContextMixin, CreateAPI):
|
||||||
"""API endpoint to allocate stock items against a SalesOrder.
|
"""API endpoint to allocate stock items against a SalesOrder.
|
||||||
|
|
||||||
- The SalesOrder is specified in the URL
|
- The SalesOrder is specified in the URL
|
||||||
@ -906,14 +908,14 @@ class SalesOrderAllocate(SalesOrderContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = serializers.SalesOrderShipmentAllocationSerializer
|
serializer_class = serializers.SalesOrderShipmentAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderAllocationDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detali view of a SalesOrderAllocation object."""
|
"""API endpoint for detali view of a SalesOrderAllocation object."""
|
||||||
|
|
||||||
queryset = models.SalesOrderAllocation.objects.all()
|
queryset = models.SalesOrderAllocation.objects.all()
|
||||||
serializer_class = serializers.SalesOrderAllocationSerializer
|
serializer_class = serializers.SalesOrderAllocationSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationList(generics.ListAPIView):
|
class SalesOrderAllocationList(ListAPI):
|
||||||
"""API endpoint for listing SalesOrderAllocation objects."""
|
"""API endpoint for listing SalesOrderAllocation objects."""
|
||||||
|
|
||||||
queryset = models.SalesOrderAllocation.objects.all()
|
queryset = models.SalesOrderAllocation.objects.all()
|
||||||
@ -1017,7 +1019,7 @@ class SalesOrderShipmentFilter(rest_filters.FilterSet):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentList(generics.ListCreateAPIView):
|
class SalesOrderShipmentList(ListCreateAPI):
|
||||||
"""API list endpoint for SalesOrderShipment model."""
|
"""API list endpoint for SalesOrderShipment model."""
|
||||||
|
|
||||||
queryset = models.SalesOrderShipment.objects.all()
|
queryset = models.SalesOrderShipment.objects.all()
|
||||||
@ -1029,14 +1031,14 @@ class SalesOrderShipmentList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderShipmentDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API detail endpooint for SalesOrderShipment model."""
|
"""API detail endpooint for SalesOrderShipment model."""
|
||||||
|
|
||||||
queryset = models.SalesOrderShipment.objects.all()
|
queryset = models.SalesOrderShipment.objects.all()
|
||||||
serializer_class = serializers.SalesOrderShipmentSerializer
|
serializer_class = serializers.SalesOrderShipmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentComplete(generics.CreateAPIView):
|
class SalesOrderShipmentComplete(CreateAPI):
|
||||||
"""API endpoint for completing (shipping) a SalesOrderShipment."""
|
"""API endpoint for completing (shipping) a SalesOrderShipment."""
|
||||||
|
|
||||||
queryset = models.SalesOrderShipment.objects.all()
|
queryset = models.SalesOrderShipment.objects.all()
|
||||||
@ -1072,7 +1074,7 @@ class PurchaseOrderAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for a PurchaseOrderAttachment."""
|
"""Detail endpoint for a PurchaseOrderAttachment."""
|
||||||
|
|
||||||
queryset = models.PurchaseOrderAttachment.objects.all()
|
queryset = models.PurchaseOrderAttachment.objects.all()
|
||||||
|
@ -14,7 +14,7 @@ from django_filters.rest_framework import DjangoFilterBackend
|
|||||||
from djmoney.contrib.exchange.exceptions import MissingRate
|
from djmoney.contrib.exchange.exceptions import MissingRate
|
||||||
from djmoney.contrib.exchange.models import convert_money
|
from djmoney.contrib.exchange.models import convert_money
|
||||||
from djmoney.money import Money
|
from djmoney.money import Money
|
||||||
from rest_framework import filters, generics, serializers, status
|
from rest_framework import filters, serializers, status
|
||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
@ -25,6 +25,9 @@ from company.models import Company, ManufacturerPart, SupplierPart
|
|||||||
from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
||||||
ListCreateDestroyAPIView)
|
ListCreateDestroyAPIView)
|
||||||
from InvenTree.helpers import DownloadFile, increment, isNull, str2bool
|
from InvenTree.helpers import DownloadFile, increment, isNull, str2bool
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveAPI,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI,
|
||||||
|
UpdateAPI)
|
||||||
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
|
from InvenTree.status_codes import (BuildStatus, PurchaseOrderStatus,
|
||||||
SalesOrderStatus)
|
SalesOrderStatus)
|
||||||
from part.admin import PartResource
|
from part.admin import PartResource
|
||||||
@ -39,7 +42,7 @@ from .models import (BomItem, BomItemSubstitute, Part, PartAttachment,
|
|||||||
PartTestTemplate)
|
PartTestTemplate)
|
||||||
|
|
||||||
|
|
||||||
class CategoryList(generics.ListCreateAPIView):
|
class CategoryList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartCategory objects.
|
"""API endpoint for accessing a list of PartCategory objects.
|
||||||
|
|
||||||
- GET: Return a list of PartCategory objects
|
- GET: Return a list of PartCategory objects
|
||||||
@ -155,7 +158,7 @@ class CategoryList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
class CategoryDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single PartCategory object."""
|
"""API endpoint for detail view of a single PartCategory object."""
|
||||||
|
|
||||||
serializer_class = part_serializers.CategorySerializer
|
serializer_class = part_serializers.CategorySerializer
|
||||||
@ -175,8 +178,11 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
def update(self, request, *args, **kwargs):
|
def update(self, request, *args, **kwargs):
|
||||||
"""Perform 'update' function and mark this part as 'starred' (or not)"""
|
"""Perform 'update' function and mark this part as 'starred' (or not)"""
|
||||||
if 'starred' in request.data:
|
# Clean up input data
|
||||||
starred = str2bool(request.data.get('starred', False))
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
if 'starred' in data:
|
||||||
|
starred = str2bool(data.get('starred', False))
|
||||||
|
|
||||||
self.get_object().set_starred(request.user, starred)
|
self.get_object().set_starred(request.user, starred)
|
||||||
|
|
||||||
@ -185,7 +191,7 @@ class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return response
|
return response
|
||||||
|
|
||||||
|
|
||||||
class CategoryMetadata(generics.RetrieveUpdateAPIView):
|
class CategoryMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating PartCategory metadata."""
|
"""API endpoint for viewing / updating PartCategory metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -195,7 +201,7 @@ class CategoryMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class CategoryParameterList(generics.ListCreateAPIView):
|
class CategoryParameterList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartCategoryParameterTemplate objects.
|
"""API endpoint for accessing a list of PartCategoryParameterTemplate objects.
|
||||||
|
|
||||||
- GET: Return a list of PartCategoryParameterTemplate objects
|
- GET: Return a list of PartCategoryParameterTemplate objects
|
||||||
@ -236,14 +242,14 @@ class CategoryParameterList(generics.ListCreateAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class CategoryParameterDetail(generics.RetrieveUpdateDestroyAPIView):
|
class CategoryParameterDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint fro the PartCategoryParameterTemplate model"""
|
"""Detail endpoint fro the PartCategoryParameterTemplate model"""
|
||||||
|
|
||||||
queryset = PartCategoryParameterTemplate.objects.all()
|
queryset = PartCategoryParameterTemplate.objects.all()
|
||||||
serializer_class = part_serializers.CategoryParameterTemplateSerializer
|
serializer_class = part_serializers.CategoryParameterTemplateSerializer
|
||||||
|
|
||||||
|
|
||||||
class CategoryTree(generics.ListAPIView):
|
class CategoryTree(ListAPI):
|
||||||
"""API endpoint for accessing a list of PartCategory objects ready for rendering a tree."""
|
"""API endpoint for accessing a list of PartCategory objects ready for rendering a tree."""
|
||||||
|
|
||||||
queryset = PartCategory.objects.all()
|
queryset = PartCategory.objects.all()
|
||||||
@ -258,14 +264,14 @@ class CategoryTree(generics.ListAPIView):
|
|||||||
ordering = ['level', 'name']
|
ordering = ['level', 'name']
|
||||||
|
|
||||||
|
|
||||||
class PartSalePriceDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartSalePriceDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartSellPriceBreak model."""
|
"""Detail endpoint for PartSellPriceBreak model."""
|
||||||
|
|
||||||
queryset = PartSellPriceBreak.objects.all()
|
queryset = PartSellPriceBreak.objects.all()
|
||||||
serializer_class = part_serializers.PartSalePriceSerializer
|
serializer_class = part_serializers.PartSalePriceSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartSalePriceList(generics.ListCreateAPIView):
|
class PartSalePriceList(ListCreateAPI):
|
||||||
"""API endpoint for list view of PartSalePriceBreak model."""
|
"""API endpoint for list view of PartSalePriceBreak model."""
|
||||||
|
|
||||||
queryset = PartSellPriceBreak.objects.all()
|
queryset = PartSellPriceBreak.objects.all()
|
||||||
@ -280,14 +286,14 @@ class PartSalePriceList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartInternalPriceDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartInternalPriceDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartInternalPriceBreak model."""
|
"""Detail endpoint for PartInternalPriceBreak model."""
|
||||||
|
|
||||||
queryset = PartInternalPriceBreak.objects.all()
|
queryset = PartInternalPriceBreak.objects.all()
|
||||||
serializer_class = part_serializers.PartInternalPriceSerializer
|
serializer_class = part_serializers.PartInternalPriceSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartInternalPriceList(generics.ListCreateAPIView):
|
class PartInternalPriceList(ListCreateAPI):
|
||||||
"""API endpoint for list view of PartInternalPriceBreak model."""
|
"""API endpoint for list view of PartInternalPriceBreak model."""
|
||||||
|
|
||||||
queryset = PartInternalPriceBreak.objects.all()
|
queryset = PartInternalPriceBreak.objects.all()
|
||||||
@ -318,21 +324,21 @@ class PartAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class PartAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartAttachment model."""
|
"""Detail endpoint for PartAttachment model."""
|
||||||
|
|
||||||
queryset = PartAttachment.objects.all()
|
queryset = PartAttachment.objects.all()
|
||||||
serializer_class = part_serializers.PartAttachmentSerializer
|
serializer_class = part_serializers.PartAttachmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartTestTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartTestTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for PartTestTemplate model."""
|
"""Detail endpoint for PartTestTemplate model."""
|
||||||
|
|
||||||
queryset = PartTestTemplate.objects.all()
|
queryset = PartTestTemplate.objects.all()
|
||||||
serializer_class = part_serializers.PartTestTemplateSerializer
|
serializer_class = part_serializers.PartTestTemplateSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartTestTemplateList(generics.ListCreateAPIView):
|
class PartTestTemplateList(ListCreateAPI):
|
||||||
"""API endpoint for listing (and creating) a PartTestTemplate."""
|
"""API endpoint for listing (and creating) a PartTestTemplate."""
|
||||||
|
|
||||||
queryset = PartTestTemplate.objects.all()
|
queryset = PartTestTemplate.objects.all()
|
||||||
@ -372,7 +378,7 @@ class PartTestTemplateList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartThumbs(generics.ListAPIView):
|
class PartThumbs(ListAPI):
|
||||||
"""API endpoint for retrieving information on available Part thumbnails."""
|
"""API endpoint for retrieving information on available Part thumbnails."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -415,7 +421,7 @@ class PartThumbs(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
|
class PartThumbsUpdate(RetrieveUpdateAPI):
|
||||||
"""API endpoint for updating Part thumbnails."""
|
"""API endpoint for updating Part thumbnails."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -426,7 +432,7 @@ class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartScheduling(generics.RetrieveAPIView):
|
class PartScheduling(RetrieveAPI):
|
||||||
"""API endpoint for delivering "scheduling" information about a given part via the API.
|
"""API endpoint for delivering "scheduling" information about a given part via the API.
|
||||||
|
|
||||||
Returns a chronologically ordered list about future "scheduled" events,
|
Returns a chronologically ordered list about future "scheduled" events,
|
||||||
@ -560,7 +566,7 @@ class PartScheduling(generics.RetrieveAPIView):
|
|||||||
return Response(schedule)
|
return Response(schedule)
|
||||||
|
|
||||||
|
|
||||||
class PartMetadata(generics.RetrieveUpdateAPIView):
|
class PartMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating Part metadata."""
|
"""API endpoint for viewing / updating Part metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -570,7 +576,7 @@ class PartMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class PartSerialNumberDetail(generics.RetrieveAPIView):
|
class PartSerialNumberDetail(RetrieveAPI):
|
||||||
"""API endpoint for returning extra serial number information about a particular part."""
|
"""API endpoint for returning extra serial number information about a particular part."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -595,7 +601,7 @@ class PartSerialNumberDetail(generics.RetrieveAPIView):
|
|||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
class PartCopyBOM(generics.CreateAPIView):
|
class PartCopyBOM(CreateAPI):
|
||||||
"""API endpoint for duplicating a BOM."""
|
"""API endpoint for duplicating a BOM."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -613,7 +619,7 @@ class PartCopyBOM(generics.CreateAPIView):
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class PartValidateBOM(generics.RetrieveUpdateAPIView):
|
class PartValidateBOM(RetrieveUpdateAPI):
|
||||||
"""API endpoint for 'validating' the BOM for a given Part."""
|
"""API endpoint for 'validating' the BOM for a given Part."""
|
||||||
|
|
||||||
class BOMValidateSerializer(serializers.ModelSerializer):
|
class BOMValidateSerializer(serializers.ModelSerializer):
|
||||||
@ -654,7 +660,10 @@ class PartValidateBOM(generics.RetrieveUpdateAPIView):
|
|||||||
|
|
||||||
partial = kwargs.pop('partial', False)
|
partial = kwargs.pop('partial', False)
|
||||||
|
|
||||||
serializer = self.get_serializer(part, data=request.data, partial=partial)
|
# Clean up input data before using it
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(part, data=data, partial=partial)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
part.validate_bom(request.user)
|
part.validate_bom(request.user)
|
||||||
@ -664,7 +673,7 @@ class PartValidateBOM(generics.RetrieveUpdateAPIView):
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single Part object."""
|
"""API endpoint for detail view of a single Part object."""
|
||||||
|
|
||||||
queryset = Part.objects.all()
|
queryset = Part.objects.all()
|
||||||
@ -721,8 +730,11 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
|
|
||||||
- If the 'starred' field is provided, update the 'starred' status against current user
|
- If the 'starred' field is provided, update the 'starred' status against current user
|
||||||
"""
|
"""
|
||||||
if 'starred' in request.data:
|
# Clean input data
|
||||||
starred = str2bool(request.data.get('starred', False))
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
if 'starred' in data:
|
||||||
|
starred = str2bool(data.get('starred', False))
|
||||||
|
|
||||||
self.get_object().set_starred(request.user, starred)
|
self.get_object().set_starred(request.user, starred)
|
||||||
|
|
||||||
@ -874,7 +886,7 @@ class PartFilter(rest_filters.FilterSet):
|
|||||||
virtual = rest_filters.BooleanFilter()
|
virtual = rest_filters.BooleanFilter()
|
||||||
|
|
||||||
|
|
||||||
class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
class PartList(APIDownloadMixin, ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of Part objects.
|
"""API endpoint for accessing a list of Part objects.
|
||||||
|
|
||||||
- GET: Return list of objects
|
- GET: Return list of objects
|
||||||
@ -1003,7 +1015,10 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
"""
|
"""
|
||||||
# TODO: Unit tests for this function!
|
# TODO: Unit tests for this function!
|
||||||
|
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
part = serializer.save()
|
part = serializer.save()
|
||||||
@ -1011,23 +1026,23 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
|
|
||||||
# Optionally copy templates from category or parent category
|
# Optionally copy templates from category or parent category
|
||||||
copy_templates = {
|
copy_templates = {
|
||||||
'main': str2bool(request.data.get('copy_category_templates', False)),
|
'main': str2bool(data.get('copy_category_templates', False)),
|
||||||
'parent': str2bool(request.data.get('copy_parent_templates', False))
|
'parent': str2bool(data.get('copy_parent_templates', False))
|
||||||
}
|
}
|
||||||
|
|
||||||
part.save(**{'add_category_templates': copy_templates})
|
part.save(**{'add_category_templates': copy_templates})
|
||||||
|
|
||||||
# Optionally copy data from another part (e.g. when duplicating)
|
# Optionally copy data from another part (e.g. when duplicating)
|
||||||
copy_from = request.data.get('copy_from', None)
|
copy_from = data.get('copy_from', None)
|
||||||
|
|
||||||
if copy_from is not None:
|
if copy_from is not None:
|
||||||
|
|
||||||
try:
|
try:
|
||||||
original = Part.objects.get(pk=copy_from)
|
original = Part.objects.get(pk=copy_from)
|
||||||
|
|
||||||
copy_bom = str2bool(request.data.get('copy_bom', False))
|
copy_bom = str2bool(data.get('copy_bom', False))
|
||||||
copy_parameters = str2bool(request.data.get('copy_parameters', False))
|
copy_parameters = str2bool(data.get('copy_parameters', False))
|
||||||
copy_image = str2bool(request.data.get('copy_image', True))
|
copy_image = str2bool(data.get('copy_image', True))
|
||||||
|
|
||||||
# Copy image?
|
# Copy image?
|
||||||
if copy_image:
|
if copy_image:
|
||||||
@ -1046,12 +1061,12 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
# Optionally create initial stock item
|
# Optionally create initial stock item
|
||||||
initial_stock = str2bool(request.data.get('initial_stock', False))
|
initial_stock = str2bool(data.get('initial_stock', False))
|
||||||
|
|
||||||
if initial_stock:
|
if initial_stock:
|
||||||
try:
|
try:
|
||||||
|
|
||||||
initial_stock_quantity = Decimal(request.data.get('initial_stock_quantity', ''))
|
initial_stock_quantity = Decimal(data.get('initial_stock_quantity', ''))
|
||||||
|
|
||||||
if initial_stock_quantity <= 0:
|
if initial_stock_quantity <= 0:
|
||||||
raise ValidationError({
|
raise ValidationError({
|
||||||
@ -1062,7 +1077,7 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
'initial_stock_quantity': [_('Must be a valid quantity')],
|
'initial_stock_quantity': [_('Must be a valid quantity')],
|
||||||
})
|
})
|
||||||
|
|
||||||
initial_stock_location = request.data.get('initial_stock_location', None)
|
initial_stock_location = data.get('initial_stock_location', None)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
initial_stock_location = StockLocation.objects.get(pk=initial_stock_location)
|
initial_stock_location = StockLocation.objects.get(pk=initial_stock_location)
|
||||||
@ -1086,20 +1101,20 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
stock_item.save(user=request.user)
|
stock_item.save(user=request.user)
|
||||||
|
|
||||||
# Optionally add manufacturer / supplier data to the part
|
# Optionally add manufacturer / supplier data to the part
|
||||||
if part.purchaseable and str2bool(request.data.get('add_supplier_info', False)):
|
if part.purchaseable and str2bool(data.get('add_supplier_info', False)):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
manufacturer = Company.objects.get(pk=request.data.get('manufacturer', None))
|
manufacturer = Company.objects.get(pk=data.get('manufacturer', None))
|
||||||
except Exception:
|
except Exception:
|
||||||
manufacturer = None
|
manufacturer = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
supplier = Company.objects.get(pk=request.data.get('supplier', None))
|
supplier = Company.objects.get(pk=data.get('supplier', None))
|
||||||
except Exception:
|
except Exception:
|
||||||
supplier = None
|
supplier = None
|
||||||
|
|
||||||
mpn = str(request.data.get('MPN', '')).strip()
|
mpn = str(data.get('MPN', '')).strip()
|
||||||
sku = str(request.data.get('SKU', '')).strip()
|
sku = str(data.get('SKU', '')).strip()
|
||||||
|
|
||||||
# Construct a manufacturer part
|
# Construct a manufacturer part
|
||||||
if manufacturer or mpn:
|
if manufacturer or mpn:
|
||||||
@ -1347,7 +1362,7 @@ class PartList(APIDownloadMixin, generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartRelatedList(generics.ListCreateAPIView):
|
class PartRelatedList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartRelated objects."""
|
"""API endpoint for accessing a list of PartRelated objects."""
|
||||||
|
|
||||||
queryset = PartRelated.objects.all()
|
queryset = PartRelated.objects.all()
|
||||||
@ -1374,14 +1389,14 @@ class PartRelatedList(generics.ListCreateAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PartRelatedDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartRelatedDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for accessing detail view of a PartRelated object."""
|
"""API endpoint for accessing detail view of a PartRelated object."""
|
||||||
|
|
||||||
queryset = PartRelated.objects.all()
|
queryset = PartRelated.objects.all()
|
||||||
serializer_class = part_serializers.PartRelationSerializer
|
serializer_class = part_serializers.PartRelationSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateList(generics.ListCreateAPIView):
|
class PartParameterTemplateList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartParameterTemplate objects.
|
"""API endpoint for accessing a list of PartParameterTemplate objects.
|
||||||
|
|
||||||
- GET: Return list of PartParameterTemplate objects
|
- GET: Return list of PartParameterTemplate objects
|
||||||
@ -1441,14 +1456,14 @@ class PartParameterTemplateList(generics.ListCreateAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartParameterTemplateDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for accessing the detail view for a PartParameterTemplate object"""
|
"""API endpoint for accessing the detail view for a PartParameterTemplate object"""
|
||||||
|
|
||||||
queryset = PartParameterTemplate.objects.all()
|
queryset = PartParameterTemplate.objects.all()
|
||||||
serializer_class = part_serializers.PartParameterTemplateSerializer
|
serializer_class = part_serializers.PartParameterTemplateSerializer
|
||||||
|
|
||||||
|
|
||||||
class PartParameterList(generics.ListCreateAPIView):
|
class PartParameterList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of PartParameter objects.
|
"""API endpoint for accessing a list of PartParameter objects.
|
||||||
|
|
||||||
- GET: Return list of PartParameter objects
|
- GET: Return list of PartParameter objects
|
||||||
@ -1468,7 +1483,7 @@ class PartParameterList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PartParameterDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PartParameterDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single PartParameter object."""
|
"""API endpoint for detail view of a single PartParameter object."""
|
||||||
|
|
||||||
queryset = PartParameter.objects.all()
|
queryset = PartParameter.objects.all()
|
||||||
@ -1747,7 +1762,7 @@ class BomList(ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BomImportUpload(generics.CreateAPIView):
|
class BomImportUpload(CreateAPI):
|
||||||
"""API endpoint for uploading a complete Bill of Materials.
|
"""API endpoint for uploading a complete Bill of Materials.
|
||||||
|
|
||||||
It is assumed that the BOM has been extracted from a file using the BomExtract endpoint.
|
It is assumed that the BOM has been extracted from a file using the BomExtract endpoint.
|
||||||
@ -1758,7 +1773,10 @@ class BomImportUpload(generics.CreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Custom create function to return the extracted data."""
|
"""Custom create function to return the extracted data."""
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
self.perform_create(serializer)
|
self.perform_create(serializer)
|
||||||
headers = self.get_success_headers(serializer.data)
|
headers = self.get_success_headers(serializer.data)
|
||||||
@ -1768,21 +1786,21 @@ class BomImportUpload(generics.CreateAPIView):
|
|||||||
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
|
return Response(data, status=status.HTTP_201_CREATED, headers=headers)
|
||||||
|
|
||||||
|
|
||||||
class BomImportExtract(generics.CreateAPIView):
|
class BomImportExtract(CreateAPI):
|
||||||
"""API endpoint for extracting BOM data from a BOM file."""
|
"""API endpoint for extracting BOM data from a BOM file."""
|
||||||
|
|
||||||
queryset = Part.objects.none()
|
queryset = Part.objects.none()
|
||||||
serializer_class = part_serializers.BomImportExtractSerializer
|
serializer_class = part_serializers.BomImportExtractSerializer
|
||||||
|
|
||||||
|
|
||||||
class BomImportSubmit(generics.CreateAPIView):
|
class BomImportSubmit(CreateAPI):
|
||||||
"""API endpoint for submitting BOM data from a BOM file."""
|
"""API endpoint for submitting BOM data from a BOM file."""
|
||||||
|
|
||||||
queryset = BomItem.objects.none()
|
queryset = BomItem.objects.none()
|
||||||
serializer_class = part_serializers.BomImportSubmitSerializer
|
serializer_class = part_serializers.BomImportSubmitSerializer
|
||||||
|
|
||||||
|
|
||||||
class BomDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BomDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single BomItem object."""
|
"""API endpoint for detail view of a single BomItem object."""
|
||||||
|
|
||||||
queryset = BomItem.objects.all()
|
queryset = BomItem.objects.all()
|
||||||
@ -1798,7 +1816,7 @@ class BomDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BomItemValidate(generics.UpdateAPIView):
|
class BomItemValidate(UpdateAPI):
|
||||||
"""API endpoint for validating a BomItem."""
|
"""API endpoint for validating a BomItem."""
|
||||||
|
|
||||||
class BomItemValidationSerializer(serializers.Serializer):
|
class BomItemValidationSerializer(serializers.Serializer):
|
||||||
@ -1812,11 +1830,13 @@ class BomItemValidate(generics.UpdateAPIView):
|
|||||||
"""Perform update request."""
|
"""Perform update request."""
|
||||||
partial = kwargs.pop('partial', False)
|
partial = kwargs.pop('partial', False)
|
||||||
|
|
||||||
valid = request.data.get('valid', False)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
valid = data.get('valid', False)
|
||||||
|
|
||||||
instance = self.get_object()
|
instance = self.get_object()
|
||||||
|
|
||||||
serializer = self.get_serializer(instance, data=request.data, partial=partial)
|
serializer = self.get_serializer(instance, data=data, partial=partial)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
if type(instance) == BomItem:
|
if type(instance) == BomItem:
|
||||||
@ -1825,7 +1845,7 @@ class BomItemValidate(generics.UpdateAPIView):
|
|||||||
return Response(serializer.data)
|
return Response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class BomItemSubstituteList(generics.ListCreateAPIView):
|
class BomItemSubstituteList(ListCreateAPI):
|
||||||
"""API endpoint for accessing a list of BomItemSubstitute objects."""
|
"""API endpoint for accessing a list of BomItemSubstitute objects."""
|
||||||
|
|
||||||
serializer_class = part_serializers.BomItemSubstituteSerializer
|
serializer_class = part_serializers.BomItemSubstituteSerializer
|
||||||
@ -1843,7 +1863,7 @@ class BomItemSubstituteList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BomItemSubstituteDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BomItemSubstituteDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of a single BomItemSubstitute object."""
|
"""API endpoint for detail view of a single BomItemSubstitute object."""
|
||||||
|
|
||||||
queryset = BomItemSubstitute.objects.all()
|
queryset = BomItemSubstitute.objects.all()
|
||||||
|
@ -4,12 +4,14 @@ from django.conf import settings
|
|||||||
from django.urls import include, re_path
|
from django.urls import include, re_path
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics, permissions, status
|
from rest_framework import filters, permissions, status
|
||||||
from rest_framework.exceptions import NotFound
|
from rest_framework.exceptions import NotFound
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import plugin.serializers as PluginSerializers
|
import plugin.serializers as PluginSerializers
|
||||||
from common.api import GlobalSettingsPermissions
|
from common.api import GlobalSettingsPermissions
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, RetrieveUpdateAPI,
|
||||||
|
RetrieveUpdateDestroyAPI)
|
||||||
from plugin.base.action.api import ActionPluginView
|
from plugin.base.action.api import ActionPluginView
|
||||||
from plugin.base.barcodes.api import barcode_api_urls
|
from plugin.base.barcodes.api import barcode_api_urls
|
||||||
from plugin.base.locate.api import LocatePluginView
|
from plugin.base.locate.api import LocatePluginView
|
||||||
@ -17,7 +19,7 @@ from plugin.models import PluginConfig, PluginSetting
|
|||||||
from plugin.registry import registry
|
from plugin.registry import registry
|
||||||
|
|
||||||
|
|
||||||
class PluginList(generics.ListAPIView):
|
class PluginList(ListAPI):
|
||||||
"""API endpoint for list of PluginConfig objects.
|
"""API endpoint for list of PluginConfig objects.
|
||||||
|
|
||||||
- GET: Return a list of all PluginConfig objects
|
- GET: Return a list of all PluginConfig objects
|
||||||
@ -80,7 +82,7 @@ class PluginList(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PluginDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PluginDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API detail endpoint for PluginConfig object.
|
"""API detail endpoint for PluginConfig object.
|
||||||
|
|
||||||
get:
|
get:
|
||||||
@ -97,7 +99,7 @@ class PluginDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
serializer_class = PluginSerializers.PluginConfigSerializer
|
serializer_class = PluginSerializers.PluginConfigSerializer
|
||||||
|
|
||||||
|
|
||||||
class PluginInstall(generics.CreateAPIView):
|
class PluginInstall(CreateAPI):
|
||||||
"""Endpoint for installing a new plugin."""
|
"""Endpoint for installing a new plugin."""
|
||||||
|
|
||||||
queryset = PluginConfig.objects.none()
|
queryset = PluginConfig.objects.none()
|
||||||
@ -105,7 +107,10 @@ class PluginInstall(generics.CreateAPIView):
|
|||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Install a plugin via the API"""
|
"""Install a plugin via the API"""
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
result = self.perform_create(serializer)
|
result = self.perform_create(serializer)
|
||||||
result['input'] = serializer.data
|
result['input'] = serializer.data
|
||||||
@ -117,7 +122,7 @@ class PluginInstall(generics.CreateAPIView):
|
|||||||
return serializer.save()
|
return serializer.save()
|
||||||
|
|
||||||
|
|
||||||
class PluginSettingList(generics.ListAPIView):
|
class PluginSettingList(ListAPI):
|
||||||
"""List endpoint for all plugin related settings.
|
"""List endpoint for all plugin related settings.
|
||||||
|
|
||||||
- read only
|
- read only
|
||||||
@ -141,7 +146,7 @@ class PluginSettingList(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class PluginSettingDetail(generics.RetrieveUpdateAPIView):
|
class PluginSettingDetail(RetrieveUpdateAPI):
|
||||||
"""Detail endpoint for a plugin-specific setting.
|
"""Detail endpoint for a plugin-specific setting.
|
||||||
|
|
||||||
Note that these cannot be created or deleted via the API
|
Note that these cannot be created or deleted via the API
|
||||||
|
@ -8,7 +8,7 @@ from django.urls import include, path, re_path
|
|||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics
|
from rest_framework import filters
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
|
||||||
import build.models
|
import build.models
|
||||||
@ -16,6 +16,7 @@ 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, RetrieveUpdateDestroyAPI
|
||||||
from stock.models import StockItem, StockItemAttachment
|
from stock.models import StockItem, StockItemAttachment
|
||||||
|
|
||||||
from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport,
|
from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport,
|
||||||
@ -25,7 +26,7 @@ from .serializers import (BOMReportSerializer, BuildReportSerializer,
|
|||||||
SalesOrderReportSerializer, TestReportSerializer)
|
SalesOrderReportSerializer, TestReportSerializer)
|
||||||
|
|
||||||
|
|
||||||
class ReportListView(generics.ListAPIView):
|
class ReportListView(ListAPI):
|
||||||
"""Generic API class for report templates."""
|
"""Generic API class for report templates."""
|
||||||
|
|
||||||
filter_backends = [
|
filter_backends = [
|
||||||
@ -330,14 +331,14 @@ class StockItemTestReportList(ReportListView, StockItemReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class StockItemTestReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockItemTestReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single TestReport object."""
|
"""API endpoint for a single TestReport object."""
|
||||||
|
|
||||||
queryset = TestReport.objects.all()
|
queryset = TestReport.objects.all()
|
||||||
serializer_class = TestReportSerializer
|
serializer_class = TestReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemTestReportPrint(generics.RetrieveAPIView, StockItemReportMixin, ReportPrintMixin):
|
class StockItemTestReportPrint(RetrieveAPI, StockItemReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a TestReport object."""
|
"""API endpoint for printing a TestReport object."""
|
||||||
|
|
||||||
queryset = TestReport.objects.all()
|
queryset = TestReport.objects.all()
|
||||||
@ -427,14 +428,14 @@ class BOMReportList(ReportListView, PartReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BOMReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BOMReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single BillOfMaterialReport object."""
|
"""API endpoint for a single BillOfMaterialReport object."""
|
||||||
|
|
||||||
queryset = BillOfMaterialsReport.objects.all()
|
queryset = BillOfMaterialsReport.objects.all()
|
||||||
serializer_class = BOMReportSerializer
|
serializer_class = BOMReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class BOMReportPrint(generics.RetrieveAPIView, PartReportMixin, ReportPrintMixin):
|
class BOMReportPrint(RetrieveAPI, PartReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a BillOfMaterialReport object."""
|
"""API endpoint for printing a BillOfMaterialReport object."""
|
||||||
|
|
||||||
queryset = BillOfMaterialsReport.objects.all()
|
queryset = BillOfMaterialsReport.objects.all()
|
||||||
@ -509,14 +510,14 @@ class BuildReportList(ReportListView, BuildReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class BuildReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class BuildReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single BuildReport object."""
|
"""API endpoint for a single BuildReport object."""
|
||||||
|
|
||||||
queryset = BuildReport.objects.all()
|
queryset = BuildReport.objects.all()
|
||||||
serializer_class = BuildReportSerializer
|
serializer_class = BuildReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class BuildReportPrint(generics.RetrieveAPIView, BuildReportMixin, ReportPrintMixin):
|
class BuildReportPrint(RetrieveAPI, BuildReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a BuildReport."""
|
"""API endpoint for printing a BuildReport."""
|
||||||
|
|
||||||
queryset = BuildReport.objects.all()
|
queryset = BuildReport.objects.all()
|
||||||
@ -586,14 +587,14 @@ class PurchaseOrderReportList(ReportListView, OrderReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class PurchaseOrderReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single PurchaseOrderReport object."""
|
"""API endpoint for a single PurchaseOrderReport object."""
|
||||||
|
|
||||||
queryset = PurchaseOrderReport.objects.all()
|
queryset = PurchaseOrderReport.objects.all()
|
||||||
serializer_class = PurchaseOrderReportSerializer
|
serializer_class = PurchaseOrderReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin):
|
class PurchaseOrderReportPrint(RetrieveAPI, OrderReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a PurchaseOrderReport object."""
|
"""API endpoint for printing a PurchaseOrderReport object."""
|
||||||
|
|
||||||
OrderModel = order.models.PurchaseOrder
|
OrderModel = order.models.PurchaseOrder
|
||||||
@ -665,14 +666,14 @@ class SalesOrderReportList(ReportListView, OrderReportMixin):
|
|||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderReportDetail(generics.RetrieveUpdateDestroyAPIView):
|
class SalesOrderReportDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for a single SalesOrderReport object."""
|
"""API endpoint for a single SalesOrderReport object."""
|
||||||
|
|
||||||
queryset = SalesOrderReport.objects.all()
|
queryset = SalesOrderReport.objects.all()
|
||||||
serializer_class = SalesOrderReportSerializer
|
serializer_class = SalesOrderReportSerializer
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin):
|
class SalesOrderReportPrint(RetrieveAPI, OrderReportMixin, ReportPrintMixin):
|
||||||
"""API endpoint for printing a PurchaseOrderReport object."""
|
"""API endpoint for printing a PurchaseOrderReport object."""
|
||||||
|
|
||||||
OrderModel = order.models.SalesOrder
|
OrderModel = order.models.SalesOrder
|
||||||
|
@ -12,7 +12,7 @@ from django.utils.translation import gettext_lazy as _
|
|||||||
|
|
||||||
from django_filters import rest_framework as rest_filters
|
from django_filters import rest_framework as rest_filters
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics, status
|
from rest_framework import filters, status
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.serializers import ValidationError
|
from rest_framework.serializers import ValidationError
|
||||||
|
|
||||||
@ -27,6 +27,8 @@ from InvenTree.api import (APIDownloadMixin, AttachmentMixin,
|
|||||||
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)
|
str2bool)
|
||||||
|
from InvenTree.mixins import (CreateAPI, ListAPI, ListCreateAPI, RetrieveAPI,
|
||||||
|
RetrieveUpdateAPI, RetrieveUpdateDestroyAPI)
|
||||||
from order.models import PurchaseOrder, SalesOrder, SalesOrderAllocation
|
from order.models import PurchaseOrder, SalesOrder, SalesOrderAllocation
|
||||||
from order.serializers import PurchaseOrderSerializer
|
from order.serializers import PurchaseOrderSerializer
|
||||||
from part.models import BomItem, Part, PartCategory
|
from part.models import BomItem, Part, PartCategory
|
||||||
@ -37,7 +39,7 @@ from stock.models import (StockItem, StockItemAttachment, StockItemTestResult,
|
|||||||
StockItemTracking, StockLocation)
|
StockItemTracking, StockLocation)
|
||||||
|
|
||||||
|
|
||||||
class StockDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API detail endpoint for Stock object.
|
"""API detail endpoint for Stock object.
|
||||||
|
|
||||||
get:
|
get:
|
||||||
@ -78,7 +80,7 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return self.serializer_class(*args, **kwargs)
|
return self.serializer_class(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class StockMetadata(generics.RetrieveUpdateAPIView):
|
class StockMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating StockItem metadata."""
|
"""API endpoint for viewing / updating StockItem metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -106,13 +108,13 @@ class StockItemContextMixin:
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class StockItemSerialize(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemSerialize(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for serializing a stock item."""
|
"""API endpoint for serializing a stock item."""
|
||||||
|
|
||||||
serializer_class = StockSerializers.SerializeStockItemSerializer
|
serializer_class = StockSerializers.SerializeStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemInstall(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemInstall(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for installing a particular stock item into this stock item.
|
"""API endpoint for installing a particular stock item into this stock item.
|
||||||
|
|
||||||
- stock_item.part must be in the BOM for this part
|
- stock_item.part must be in the BOM for this part
|
||||||
@ -123,25 +125,25 @@ class StockItemInstall(StockItemContextMixin, generics.CreateAPIView):
|
|||||||
serializer_class = StockSerializers.InstallStockItemSerializer
|
serializer_class = StockSerializers.InstallStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemUninstall(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemUninstall(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for removing (uninstalling) items from this item."""
|
"""API endpoint for removing (uninstalling) items from this item."""
|
||||||
|
|
||||||
serializer_class = StockSerializers.UninstallStockItemSerializer
|
serializer_class = StockSerializers.UninstallStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemConvert(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemConvert(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for converting a stock item to a variant part"""
|
"""API endpoint for converting a stock item to a variant part"""
|
||||||
|
|
||||||
serializer_class = StockSerializers.ConvertStockItemSerializer
|
serializer_class = StockSerializers.ConvertStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemReturn(StockItemContextMixin, generics.CreateAPIView):
|
class StockItemReturn(StockItemContextMixin, CreateAPI):
|
||||||
"""API endpoint for returning a stock item from a customer"""
|
"""API endpoint for returning a stock item from a customer"""
|
||||||
|
|
||||||
serializer_class = StockSerializers.ReturnStockItemSerializer
|
serializer_class = StockSerializers.ReturnStockItemSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockAdjustView(generics.CreateAPIView):
|
class StockAdjustView(CreateAPI):
|
||||||
"""A generic class for handling stocktake actions.
|
"""A generic class for handling stocktake actions.
|
||||||
|
|
||||||
Subclasses exist for:
|
Subclasses exist for:
|
||||||
@ -186,7 +188,7 @@ class StockTransfer(StockAdjustView):
|
|||||||
serializer_class = StockSerializers.StockTransferSerializer
|
serializer_class = StockSerializers.StockTransferSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockAssign(generics.CreateAPIView):
|
class StockAssign(CreateAPI):
|
||||||
"""API endpoint for assigning stock to a particular customer."""
|
"""API endpoint for assigning stock to a particular customer."""
|
||||||
|
|
||||||
queryset = StockItem.objects.all()
|
queryset = StockItem.objects.all()
|
||||||
@ -200,7 +202,7 @@ class StockAssign(generics.CreateAPIView):
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class StockMerge(generics.CreateAPIView):
|
class StockMerge(CreateAPI):
|
||||||
"""API endpoint for merging multiple stock items."""
|
"""API endpoint for merging multiple stock items."""
|
||||||
|
|
||||||
queryset = StockItem.objects.none()
|
queryset = StockItem.objects.none()
|
||||||
@ -213,7 +215,7 @@ class StockMerge(generics.CreateAPIView):
|
|||||||
return ctx
|
return ctx
|
||||||
|
|
||||||
|
|
||||||
class StockLocationList(generics.ListCreateAPIView):
|
class StockLocationList(ListCreateAPI):
|
||||||
"""API endpoint for list view of StockLocation objects.
|
"""API endpoint for list view of StockLocation objects.
|
||||||
|
|
||||||
- GET: Return list of StockLocation objects
|
- GET: Return list of StockLocation objects
|
||||||
@ -305,7 +307,7 @@ class StockLocationList(generics.ListCreateAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class StockLocationTree(generics.ListAPIView):
|
class StockLocationTree(ListAPI):
|
||||||
"""API endpoint for accessing a list of StockLocation objects, ready for rendering as a tree."""
|
"""API endpoint for accessing a list of StockLocation objects, ready for rendering as a tree."""
|
||||||
|
|
||||||
queryset = StockLocation.objects.all()
|
queryset = StockLocation.objects.all()
|
||||||
@ -502,7 +504,8 @@ class StockList(APIDownloadMixin, ListCreateDestroyAPIView):
|
|||||||
|
|
||||||
# Copy the request data, to side-step "mutability" issues
|
# Copy the request data, to side-step "mutability" issues
|
||||||
data = OrderedDict()
|
data = OrderedDict()
|
||||||
data.update(request.data)
|
# Update with cleaned input data
|
||||||
|
data.update(self.clean_data(request.data))
|
||||||
|
|
||||||
quantity = data.get('quantity', None)
|
quantity = data.get('quantity', None)
|
||||||
|
|
||||||
@ -1067,14 +1070,14 @@ class StockAttachmentList(AttachmentMixin, ListCreateDestroyAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class StockAttachmentDetail(AttachmentMixin, generics.RetrieveUpdateDestroyAPIView):
|
class StockAttachmentDetail(AttachmentMixin, RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for StockItemAttachment."""
|
"""Detail endpoint for StockItemAttachment."""
|
||||||
|
|
||||||
queryset = StockItemAttachment.objects.all()
|
queryset = StockItemAttachment.objects.all()
|
||||||
serializer_class = StockSerializers.StockItemAttachmentSerializer
|
serializer_class = StockSerializers.StockItemAttachmentSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockItemTestResultDetail(generics.RetrieveUpdateDestroyAPIView):
|
class StockItemTestResultDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""Detail endpoint for StockItemTestResult."""
|
"""Detail endpoint for StockItemTestResult."""
|
||||||
|
|
||||||
queryset = StockItemTestResult.objects.all()
|
queryset = StockItemTestResult.objects.all()
|
||||||
@ -1170,14 +1173,14 @@ class StockItemTestResultList(ListCreateDestroyAPIView):
|
|||||||
test_result.save()
|
test_result.save()
|
||||||
|
|
||||||
|
|
||||||
class StockTrackingDetail(generics.RetrieveAPIView):
|
class StockTrackingDetail(RetrieveAPI):
|
||||||
"""Detail API endpoint for StockItemTracking model."""
|
"""Detail API endpoint for StockItemTracking model."""
|
||||||
|
|
||||||
queryset = StockItemTracking.objects.all()
|
queryset = StockItemTracking.objects.all()
|
||||||
serializer_class = StockSerializers.StockTrackingSerializer
|
serializer_class = StockSerializers.StockTrackingSerializer
|
||||||
|
|
||||||
|
|
||||||
class StockTrackingList(generics.ListAPIView):
|
class StockTrackingList(ListAPI):
|
||||||
"""API endpoint for list view of StockItemTracking objects.
|
"""API endpoint for list view of StockItemTracking objects.
|
||||||
|
|
||||||
StockItemTracking objects are read-only
|
StockItemTracking objects are read-only
|
||||||
@ -1276,7 +1279,10 @@ class StockTrackingList(generics.ListAPIView):
|
|||||||
Here we override the default 'create' implementation,
|
Here we override the default 'create' implementation,
|
||||||
to save the user information associated with the request object.
|
to save the user information associated with the request object.
|
||||||
"""
|
"""
|
||||||
serializer = self.get_serializer(data=request.data)
|
# Clean up input data
|
||||||
|
data = self.clean_data(request.data)
|
||||||
|
|
||||||
|
serializer = self.get_serializer(data=data)
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
|
|
||||||
# Record the user who created this Part object
|
# Record the user who created this Part object
|
||||||
@ -1314,7 +1320,7 @@ class StockTrackingList(generics.ListAPIView):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class LocationMetadata(generics.RetrieveUpdateAPIView):
|
class LocationMetadata(RetrieveUpdateAPI):
|
||||||
"""API endpoint for viewing / updating StockLocation metadata."""
|
"""API endpoint for viewing / updating StockLocation metadata."""
|
||||||
|
|
||||||
def get_serializer(self, *args, **kwargs):
|
def get_serializer(self, *args, **kwargs):
|
||||||
@ -1324,7 +1330,7 @@ class LocationMetadata(generics.RetrieveUpdateAPIView):
|
|||||||
queryset = StockLocation.objects.all()
|
queryset = StockLocation.objects.all()
|
||||||
|
|
||||||
|
|
||||||
class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
|
class LocationDetail(RetrieveUpdateDestroyAPI):
|
||||||
"""API endpoint for detail view of StockLocation object.
|
"""API endpoint for detail view of StockLocation object.
|
||||||
|
|
||||||
- GET: Return a single StockLocation object
|
- GET: Return a single StockLocation object
|
||||||
|
@ -5,17 +5,18 @@ from django.core.exceptions import ObjectDoesNotExist
|
|||||||
from django.urls import include, path, re_path
|
from django.urls import include, path, re_path
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
from rest_framework import filters, generics, permissions, status
|
from rest_framework import filters, permissions, status
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
|
||||||
|
from InvenTree.mixins import ListAPI, RetrieveAPI
|
||||||
from InvenTree.serializers import UserSerializer
|
from InvenTree.serializers import UserSerializer
|
||||||
from users.models import Owner, RuleSet, check_user_role
|
from users.models import Owner, RuleSet, check_user_role
|
||||||
from users.serializers import OwnerSerializer
|
from users.serializers import OwnerSerializer
|
||||||
|
|
||||||
|
|
||||||
class OwnerList(generics.ListAPIView):
|
class OwnerList(ListAPI):
|
||||||
"""List API endpoint for Owner model.
|
"""List API endpoint for Owner model.
|
||||||
|
|
||||||
Cannot create.
|
Cannot create.
|
||||||
@ -54,7 +55,7 @@ class OwnerList(generics.ListAPIView):
|
|||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
class OwnerDetail(generics.RetrieveAPIView):
|
class OwnerDetail(RetrieveAPI):
|
||||||
"""Detail API endpoint for Owner model.
|
"""Detail API endpoint for Owner model.
|
||||||
|
|
||||||
Cannot edit or delete
|
Cannot edit or delete
|
||||||
@ -107,7 +108,7 @@ class RoleDetails(APIView):
|
|||||||
return Response(data)
|
return Response(data)
|
||||||
|
|
||||||
|
|
||||||
class UserDetail(generics.RetrieveAPIView):
|
class UserDetail(RetrieveAPI):
|
||||||
"""Detail endpoint for a single user."""
|
"""Detail endpoint for a single user."""
|
||||||
|
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
@ -115,7 +116,7 @@ class UserDetail(generics.RetrieveAPIView):
|
|||||||
permission_classes = (permissions.IsAuthenticated,)
|
permission_classes = (permissions.IsAuthenticated,)
|
||||||
|
|
||||||
|
|
||||||
class UserList(generics.ListAPIView):
|
class UserList(ListAPI):
|
||||||
"""List endpoint for detail on all users."""
|
"""List endpoint for detail on all users."""
|
||||||
|
|
||||||
queryset = User.objects.all()
|
queryset = User.objects.all()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user