Adds a DRF serializer for stock adjustments

- Currently the "StockCount" action has been transferred
This commit is contained in:
Oliver 2021-10-05 21:55:05 +11:00
parent 8dcd2ab7ca
commit f197d8b1da
2 changed files with 128 additions and 44 deletions

View File

@ -41,11 +41,7 @@ from order.serializers import POSerializer
import common.settings import common.settings
import common.models import common.models
from .serializers import StockItemSerializer import stock.serializers as StockSerializers
from .serializers import LocationSerializer, LocationBriefSerializer
from .serializers import StockTrackingSerializer
from .serializers import StockItemAttachmentSerializer
from .serializers import StockItemTestResultSerializer
from InvenTree.views import TreeSerializer from InvenTree.views import TreeSerializer
from InvenTree.helpers import str2bool, isNull from InvenTree.helpers import str2bool, isNull
@ -83,12 +79,12 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
""" """
queryset = StockItem.objects.all() queryset = StockItem.objects.all()
serializer_class = StockItemSerializer serializer_class = StockSerializers.StockItemSerializer
def get_queryset(self, *args, **kwargs): def get_queryset(self, *args, **kwargs):
queryset = super().get_queryset(*args, **kwargs) queryset = super().get_queryset(*args, **kwargs)
queryset = StockItemSerializer.annotate_queryset(queryset) queryset = StockSerializers.StockItemSerializer.annotate_queryset(queryset)
return queryset return queryset
@ -138,8 +134,6 @@ class StockAdjust(APIView):
queryset = StockItem.objects.none() queryset = StockItem.objects.none()
allow_missing_quantity = False
def get_items(self, request): def get_items(self, request):
""" """
Return a list of items posted to the endpoint. Return a list of items posted to the endpoint.
@ -206,23 +200,22 @@ class StockAdjust(APIView):
self.notes = str(request.data.get('notes', '')) self.notes = str(request.data.get('notes', ''))
class StockCount(StockAdjust): class StockCount(generics.CreateAPIView):
""" """
Endpoint for counting stock (performing a stocktake). Endpoint for counting stock (performing a stocktake).
""" """
def post(self, request, *args, **kwargs): queryset = StockItem.objects.none()
self.get_items(request) serializer_class = StockSerializers.StockCountSerializer
n = 0 def get_serializer_context(self):
for item in self.items: context = super().get_serializer_context()
if item['item'].stocktake(item['quantity'], request.user, notes=self.notes): context['request'] = self.request
n += 1
return Response({'success': _('Updated stock for {n} items').format(n=n)}) return context
class StockAdd(StockAdjust): class StockAdd(StockAdjust):
@ -315,7 +308,7 @@ class StockLocationList(generics.ListCreateAPIView):
""" """
queryset = StockLocation.objects.all() queryset = StockLocation.objects.all()
serializer_class = LocationSerializer serializer_class = StockSerializers.LocationSerializer
def filter_queryset(self, queryset): def filter_queryset(self, queryset):
""" """
@ -517,7 +510,7 @@ class StockList(generics.ListCreateAPIView):
- POST: Create a new StockItem - POST: Create a new StockItem
""" """
serializer_class = StockItemSerializer serializer_class = StockSerializers.StockItemSerializer
queryset = StockItem.objects.all() queryset = StockItem.objects.all()
filterset_class = StockFilter filterset_class = StockFilter
@ -639,7 +632,7 @@ class StockList(generics.ListCreateAPIView):
# Serialize each StockLocation object # Serialize each StockLocation object
for location in locations: for location in locations:
location_map[location.pk] = LocationBriefSerializer(location).data location_map[location.pk] = StockSerializers.LocationBriefSerializer(location).data
# Now update each StockItem with the related StockLocation data # Now update each StockItem with the related StockLocation data
for stock_item in data: for stock_item in data:
@ -665,7 +658,7 @@ class StockList(generics.ListCreateAPIView):
queryset = super().get_queryset(*args, **kwargs) queryset = super().get_queryset(*args, **kwargs)
queryset = StockItemSerializer.annotate_queryset(queryset) queryset = StockSerializers.StockItemSerializer.annotate_queryset(queryset)
# Do not expose StockItem objects which are scheduled for deletion # Do not expose StockItem objects which are scheduled for deletion
queryset = queryset.filter(scheduled_for_deletion=False) queryset = queryset.filter(scheduled_for_deletion=False)
@ -954,7 +947,7 @@ class StockAttachmentList(generics.ListCreateAPIView, AttachmentMixin):
""" """
queryset = StockItemAttachment.objects.all() queryset = StockItemAttachment.objects.all()
serializer_class = StockItemAttachmentSerializer serializer_class = StockSerializers.StockItemAttachmentSerializer
filter_backends = [ filter_backends = [
DjangoFilterBackend, DjangoFilterBackend,
@ -973,7 +966,7 @@ class StockAttachmentDetail(generics.RetrieveUpdateDestroyAPIView, AttachmentMix
""" """
queryset = StockItemAttachment.objects.all() queryset = StockItemAttachment.objects.all()
serializer_class = StockItemAttachmentSerializer serializer_class = StockSerializers.StockItemAttachmentSerializer
class StockItemTestResultDetail(generics.RetrieveUpdateDestroyAPIView): class StockItemTestResultDetail(generics.RetrieveUpdateDestroyAPIView):
@ -982,7 +975,7 @@ class StockItemTestResultDetail(generics.RetrieveUpdateDestroyAPIView):
""" """
queryset = StockItemTestResult.objects.all() queryset = StockItemTestResult.objects.all()
serializer_class = StockItemTestResultSerializer serializer_class = StockSerializers.StockItemTestResultSerializer
class StockItemTestResultList(generics.ListCreateAPIView): class StockItemTestResultList(generics.ListCreateAPIView):
@ -991,7 +984,7 @@ class StockItemTestResultList(generics.ListCreateAPIView):
""" """
queryset = StockItemTestResult.objects.all() queryset = StockItemTestResult.objects.all()
serializer_class = StockItemTestResultSerializer serializer_class = StockSerializers.StockItemTestResultSerializer
filter_backends = [ filter_backends = [
DjangoFilterBackend, DjangoFilterBackend,
@ -1039,7 +1032,7 @@ class StockTrackingDetail(generics.RetrieveAPIView):
""" """
queryset = StockItemTracking.objects.all() queryset = StockItemTracking.objects.all()
serializer_class = StockTrackingSerializer serializer_class = StockSerializers.StockTrackingSerializer
class StockTrackingList(generics.ListAPIView): class StockTrackingList(generics.ListAPIView):
@ -1052,7 +1045,7 @@ class StockTrackingList(generics.ListAPIView):
""" """
queryset = StockItemTracking.objects.all() queryset = StockItemTracking.objects.all()
serializer_class = StockTrackingSerializer serializer_class = StockSerializers.StockTrackingSerializer
def get_serializer(self, *args, **kwargs): def get_serializer(self, *args, **kwargs):
try: try:
@ -1088,7 +1081,7 @@ class StockTrackingList(generics.ListAPIView):
if 'location' in deltas: if 'location' in deltas:
try: try:
location = StockLocation.objects.get(pk=deltas['location']) location = StockLocation.objects.get(pk=deltas['location'])
serializer = LocationSerializer(location) serializer = StockSerializers.LocationSerializer(location)
deltas['location_detail'] = serializer.data deltas['location_detail'] = serializer.data
except: except:
pass pass
@ -1097,7 +1090,7 @@ class StockTrackingList(generics.ListAPIView):
if 'stockitem' in deltas: if 'stockitem' in deltas:
try: try:
stockitem = StockItem.objects.get(pk=deltas['stockitem']) stockitem = StockItem.objects.get(pk=deltas['stockitem'])
serializer = StockItemSerializer(stockitem) serializer = StockSerializers.StockItemSerializer(stockitem)
deltas['stockitem_detail'] = serializer.data deltas['stockitem_detail'] = serializer.data
except: except:
pass pass
@ -1179,7 +1172,7 @@ class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
""" """
queryset = StockLocation.objects.all() queryset = StockLocation.objects.all()
serializer_class = LocationSerializer serializer_class = StockSerializers.LocationSerializer
stock_api_urls = [ stock_api_urls = [

View File

@ -2,27 +2,29 @@
JSON serializers for Stock app JSON serializers for Stock app
""" """
from rest_framework import serializers # -*- coding: utf-8 -*-
from __future__ import unicode_literals
from decimal import Decimal
from datetime import datetime, timedelta
from django.db import transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db.models.functions import Coalesce
from django.db.models import Case, When, Value
from django.db.models import BooleanField
from django.db.models import Q
from rest_framework import serializers
from rest_framework.serializers import ValidationError
from sql_util.utils import SubquerySum, SubqueryCount
from .models import StockItem, StockLocation from .models import StockItem, StockLocation
from .models import StockItemTracking from .models import StockItemTracking
from .models import StockItemAttachment from .models import StockItemAttachment
from .models import StockItemTestResult from .models import StockItemTestResult
from django.db.models.functions import Coalesce
from django.db.models import Case, When, Value
from django.db.models import BooleanField
from django.db.models import Q
from sql_util.utils import SubquerySum, SubqueryCount
from decimal import Decimal
from datetime import datetime, timedelta
import common.models import common.models
from common.settings import currency_code_default, currency_code_mappings from common.settings import currency_code_default, currency_code_mappings
@ -396,3 +398,92 @@ class StockTrackingSerializer(InvenTreeModelSerializer):
'label', 'label',
'tracking_type', 'tracking_type',
] ]
class StockAdjustmentItemSerializer(serializers.Serializer):
"""
Serializer for a single StockItem within a stock adjument request.
Fields:
- item: StockItem object
- quantity: Numerical quantity
"""
class Meta:
fields = [
'item',
'quantity'
]
pk = serializers.PrimaryKeyRelatedField(
queryset=StockItem.objects.all(),
many=False,
allow_null=False,
required=True,
label=_('StockItem primary key value')
)
quantity = serializers.DecimalField(
max_digits=15,
decimal_places=5,
min_value=0,
required=True
)
class StockAdjustmentSerializer(serializers.Serializer):
"""
Base class for managing stock adjustment actions via the API
"""
class Meta:
fields = [
'items',
'notes',
]
items = StockAdjustmentItemSerializer(many=True)
notes = serializers.CharField(
required=False,
allow_blank=False,
)
def validate(self, data):
super().validate(data)
items = data.get('items', [])
if len(items) == 0:
raise ValidationError(_("A list of stock items must be provided"))
return data
class StockCountSerializer(StockAdjustmentSerializer):
"""
Serializer for counting stock items
"""
def save(self):
"""
Perform the database transactions to count the stock
"""
request = self.context['request']
data = self.validated_data
items = data['items']
notes = data['notes']
with transaction.atomic():
for item in items:
stock_item = item['pk']
quantity = item['quantity']
stock_item.stocktake(
quantity,
request.user,
notes=notes
)