PEP fixes for /stock

This commit is contained in:
Oliver Walters 2019-04-14 09:23:24 +10:00
parent c3312ac935
commit 76b0d17b11
5 changed files with 93 additions and 49 deletions

View File

@ -1,11 +1,8 @@
from django_filters.rest_framework import FilterSet, DjangoFilterBackend
from django_filters import NumberFilter
from rest_framework import generics, permissions, response, filters
from django.conf.urls import url, include
# from InvenTree.models import FilterChildren
from .models import StockLocation, StockItem
from .models import StockItemTracking
@ -19,8 +16,8 @@ from InvenTree.serializers import DraftRUDView
from rest_framework.serializers import ValidationError
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
from django.contrib.auth.models import User
from rest_framework import generics, response, filters, permissions
class StockCategoryTree(TreeSerializer):
title = 'Stock'
@ -69,26 +66,26 @@ class StockStocktake(APIView):
def post(self, request, *args, **kwargs):
if not 'action' in request.data:
if 'action' not in request.data:
raise ValidationError({'action': 'Stocktake action must be provided'})
action = request.data['action']
ACTIONS = ['stocktake', 'remove', 'add']
if not action in ACTIONS:
if action not in ACTIONS:
raise ValidationError({'action': 'Action must be one of ' + ','.join(ACTIONS)})
if not 'items[]' in request.data:
elif 'items[]' not in request.data:
raise ValidationError({'items[]:' 'Request must contain list of items'})
items = []
# Ensure each entry is valid
for entry in request.data['items[]']:
if not 'pk' in entry:
if 'pk' not in entry:
raise ValidationError({'pk': 'Each entry must contain pk field'})
if not 'quantity' in entry:
elif 'quantity' not in entry:
raise ValidationError({'quantity': 'Each entry must contain quantity field'})
item = {}
@ -112,7 +109,6 @@ class StockStocktake(APIView):
if 'notes' in request.data:
notes = request.data['notes']
for item in items:
quantity = int(item['quantity'])
@ -136,7 +132,7 @@ class StockMove(APIView):
data = request.data
if not u'location' in data:
if u'location' not in data:
raise ValidationError({'location': 'Destination must be specified'})
loc_id = data.get(u'location')
@ -146,7 +142,7 @@ class StockMove(APIView):
except StockLocation.DoesNotExist:
raise ValidationError({'location': 'Location does not exist'})
if not u'parts[]' in data:
if u'parts[]' not in data:
raise ValidationError({'parts[]': 'Parts list must be specified'})
part_list = data.get(u'parts[]')
@ -160,7 +156,7 @@ class StockMove(APIView):
part = StockItem.objects.get(pk=pid)
parts.append(part)
except StockItem.DoesNotExist:
errors.append({'part': 'Part {id} does not exist'.format(id=part_id)})
errors.append({'part': 'Part {id} does not exist'.format(id=pid)})
if len(errors) > 0:
raise ValidationError(errors)
@ -227,7 +223,7 @@ class StockList(generics.ListCreateAPIView):
'supplier_part',
'customer',
'belongs_to',
#'status',
# 'status' TODO - There are some issues filtering based on an enumeration field
]
@ -250,7 +246,7 @@ class StockTrackingList(generics.ListCreateAPIView):
queryset = StockItemTracking.objects.all()
serializer_class = StockTrackingSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly,]
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
filter_backends = [
DjangoFilterBackend,
@ -275,7 +271,6 @@ class StockTrackingList(generics.ListCreateAPIView):
]
class LocationDetail(generics.RetrieveUpdateDestroyAPIView):
"""
@ -303,9 +298,7 @@ location_endpoints = [
url(r'^$', LocationDetail.as_view(), name='stocklocation-detail'),
]
stock_api_urls = [
url(r'location/?', StockLocationList.as_view(), name='api-location-list'),
url(r'location/(?P<pk>\d+)/', include(location_endpoints)),
@ -322,4 +315,4 @@ stock_api_urls = [
url(r'^(?P<pk>\d+)/', include(stock_endpoints)),
url(r'^.*$', StockList.as_view(), name='api-stock-list'),
]
]

View File

@ -67,4 +67,4 @@ class EditStockItemForm(HelperForm):
'batch',
'status',
'notes'
]
]

View File

@ -69,12 +69,11 @@ class StockItem(models.Model):
if add_note:
# This StockItem is being saved for the first time
self.add_transaction_note(
'Created stock item',
None,
system=True
'Created stock item',
None,
system=True
)
def clean(self):
# The 'supplier_part' field must point to the same part!
@ -227,8 +226,10 @@ class StockItem(models.Model):
if location.pk == self.location.pk:
return False # raise forms.ValidationError("Cannot move item to its current location")
msg = "Moved to {loc} (from {src})".format(loc=location.name,
src=self.location.name)
msg = "Moved to {loc} (from {src})".format(
loc=location.name,
src=self.location.name
)
self.location = location
self.save()
@ -240,7 +241,6 @@ class StockItem(models.Model):
return True
@transaction.atomic
def stocktake(self, count, user, notes=''):
""" Perform item stocktake.

View File

@ -4,9 +4,13 @@ from .models import StockItem, StockLocation
from .models import StockItemTracking
from part.serializers import PartBriefSerializer
from InvenTree.serializers import UserSerializer, UserSerializerBrief
from InvenTree.serializers import UserSerializerBrief
class LocationBriefSerializer(serializers.ModelSerializer):
"""
Provides a brief serializer for a StockLocation object
"""
url = serializers.CharField(source='get_absolute_url', read_only=True)
@ -49,8 +53,12 @@ class StockTrackingSerializer(serializers.ModelSerializer):
class StockItemSerializer(serializers.ModelSerializer):
""" Serializer for a StockItem
"""
Serializer for a StockItem
- Includes serialization for the linked part
- Includes serialization for the item location
"""
url = serializers.CharField(source='get_absolute_url', read_only=True)
part = PartBriefSerializer(many=False, read_only=True)
@ -66,17 +74,11 @@ class StockItemSerializer(serializers.ModelSerializer):
'supplier_part',
'location',
'in_stock',
#'belongs_to',
#'customer',
'quantity',
'serial',
'batch',
'status',
'notes',
#'updated',
#'stocktake_date',
#'stocktake_user',
#'review_needed',
]
""" These fields are read-only in this context.
@ -86,7 +88,8 @@ class StockItemSerializer(serializers.ModelSerializer):
'stocktake_date',
'stocktake_user',
'updated',
#'quantity',
'quantity',
'in_stock'
]
@ -106,10 +109,10 @@ class LocationSerializer(serializers.ModelSerializer):
class Meta:
model = StockLocation
fields = [
'pk',
'url',
'name',
'description',
'parent',
'pathstring'
]
'pk',
'url',
'name',
'description',
'parent',
'pathstring'
]

View File

@ -10,15 +10,17 @@ from InvenTree.views import AjaxUpdateView, AjaxDeleteView, AjaxCreateView
from part.models import Part
from .models import StockItem, StockLocation
import datetime
from .forms import EditStockLocationForm
from .forms import CreateStockItemForm
from .forms import EditStockItemForm
from .forms import MoveStockItemForm
from .forms import StocktakeForm
class StockIndex(ListView):
"""
StockIndex view loads all StockLocation and StockItem object
"""
model = StockItem
template_name = 'stock/location.html'
context_obect_name = 'locations'
@ -36,6 +38,10 @@ class StockIndex(ListView):
class StockLocationDetail(DetailView):
"""
Detailed view of a single StockLocation object
"""
context_object_name = 'location'
template_name = 'stock/location.html'
queryset = StockLocation.objects.all()
@ -43,6 +49,10 @@ class StockLocationDetail(DetailView):
class StockItemDetail(DetailView):
"""
Detailed view of a single StockItem object
"""
context_object_name = 'item'
template_name = 'stock/item.html'
queryset = StockItem.objects.all()
@ -50,6 +60,11 @@ class StockItemDetail(DetailView):
class StockLocationEdit(AjaxUpdateView):
"""
View for editing details of a StockLocation.
This view is used with the EditStockLocationForm to deliver a modal form to the web view
"""
model = StockLocation
form_class = EditStockLocationForm
template_name = 'stock/location_edit.html'
@ -59,6 +74,10 @@ class StockLocationEdit(AjaxUpdateView):
class StockItemEdit(AjaxUpdateView):
"""
View for editing details of a single StockItem
"""
model = StockItem
form_class = EditStockItemForm
template_name = 'stock/item_edit.html'
@ -68,6 +87,11 @@ class StockItemEdit(AjaxUpdateView):
class StockLocationCreate(AjaxCreateView):
"""
View for creating a new StockLocation
A parent location (another StockLocation object) can be passed as a query parameter
"""
model = StockLocation
form_class = EditStockLocationForm
template_name = 'stock/location_create.html'
@ -87,6 +111,13 @@ class StockLocationCreate(AjaxCreateView):
class StockItemCreate(AjaxCreateView):
"""
View for creating a new StockItem
Parameters can be pre-filled by passing query items:
- part: The part of which the new StockItem is an instance
- location: The location of the new StockItem
"""
model = StockItem
form_class = CreateStockItemForm
template_name = 'stock/item_create.html'
@ -114,6 +145,11 @@ class StockItemCreate(AjaxCreateView):
class StockLocationDelete(AjaxDeleteView):
"""
View to delete a StockLocation
Presents a deletion confirmation form to the user
"""
model = StockLocation
success_url = '/stock'
template_name = 'stock/location_delete.html'
@ -122,6 +158,11 @@ class StockLocationDelete(AjaxDeleteView):
class StockItemDelete(AjaxDeleteView):
"""
View to delete a StockItem
Presents a deletion confirmation form to the user
"""
model = StockItem
success_url = '/stock/'
template_name = 'stock/item_delete.html'
@ -130,6 +171,11 @@ class StockItemDelete(AjaxDeleteView):
class StockItemMove(AjaxUpdateView):
"""
View to move a StockItem from one location to another
Performs some data validation to prevent illogical stock moves
"""
model = StockItem
template_name = 'modal_form.html'
context_object_name = 'item'
@ -156,7 +202,6 @@ class StockItemMove(AjaxUpdateView):
form.errors['location'] = ['Cannot move to an empty location']
except StockLocation.DoesNotExist:
loc_path = ''
form.errors['location'] = ['Location does not exist']
data = {
@ -167,6 +212,11 @@ class StockItemMove(AjaxUpdateView):
class StockItemStocktake(AjaxUpdateView):
"""
View to perform stocktake on a single StockItem
Updates the quantity, which will also create a new StockItemTracking item
"""
model = StockItem
template_name = 'modal_form.html'
context_object_name = 'item'
@ -188,5 +238,3 @@ class StockItemStocktake(AjaxUpdateView):
}
return self.renderJsonResponse(request, form, data)