2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 12:06:44 +00:00

Improve error checking for initial stock creation when creating a new part

- Use @transaction.atomic
- Raise proper field errors
This commit is contained in:
Oliver Walters 2021-08-14 00:09:08 +10:00
parent 1396c349c8
commit cb11df4dba
2 changed files with 65 additions and 11 deletions

View File

@ -9,12 +9,14 @@ from django.conf.urls import url, include
from django.urls import reverse from django.urls import reverse
from django.http import JsonResponse from django.http import JsonResponse
from django.db.models import Q, F, Count, Min, Max, Avg from django.db.models import Q, F, Count, Min, Max, Avg
from django.db import transaction
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import status from rest_framework import status
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import filters, serializers from rest_framework import filters, serializers
from rest_framework import generics from rest_framework import generics
from rest_framework.exceptions import ValidationError
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
from django_filters import rest_framework as rest_filters from django_filters import rest_framework as rest_filters
@ -23,7 +25,7 @@ from djmoney.money import Money
from djmoney.contrib.exchange.models import convert_money from djmoney.contrib.exchange.models import convert_money
from djmoney.contrib.exchange.exceptions import MissingRate from djmoney.contrib.exchange.exceptions import MissingRate
from decimal import Decimal from decimal import Decimal, InvalidOperation
from .models import Part, PartCategory, BomItem from .models import Part, PartCategory, BomItem
from .models import PartParameter, PartParameterTemplate from .models import PartParameter, PartParameterTemplate
@ -31,7 +33,9 @@ from .models import PartAttachment, PartTestTemplate
from .models import PartSellPriceBreak, PartInternalPriceBreak from .models import PartSellPriceBreak, PartInternalPriceBreak
from .models import PartCategoryParameterTemplate from .models import PartCategoryParameterTemplate
from stock.models import StockItem
from stock.models import StockItem, StockLocation
from common.models import InvenTreeSetting from common.models import InvenTreeSetting
from build.models import Build from build.models import Build
@ -630,6 +634,7 @@ class PartList(generics.ListCreateAPIView):
else: else:
return Response(data) return Response(data)
@transaction.atomic
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
""" """
We wish to save the user who created this part! We wish to save the user who created this part!
@ -637,6 +642,8 @@ class PartList(generics.ListCreateAPIView):
Note: Implementation copied from DRF class CreateModelMixin Note: Implementation copied from DRF class CreateModelMixin
""" """
#TODO: Unit tests for this function!
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
@ -680,22 +687,50 @@ class PartList(generics.ListCreateAPIView):
pass pass
# Optionally create initial stock item # Optionally create initial stock item
try: initial_stock = str2bool(request.data.get('initial_stock', False))
initial_stock = Decimal(request.data.get('initial_stock', 0))
if initial_stock > 0 and part.default_location is not None: if initial_stock:
try:
print("q:", request.data.get('initial_stock_quantity'))
initial_stock_quantity = Decimal(request.data.get('initial_stock_quantity', None))
if initial_stock_quantity <= 0:
raise ValidationError({
'initial_stock_quantity': [_('Must be greater than zero')],
})
except (ValueError, InvalidOperation): # Invalid quantity provided
raise ValidationError({
'initial_stock_quantity': [_('Must be a valid quantity')],
})
# If an initial stock quantity is specified...
if initial_stock_quantity > 0:
initial_stock_location = request.data.get('initial_stock_location', None)
try:
initial_stock_location = StockLocation.objects.get(pk=initial_stock_location)
except (ValueError, StockLocation.DoesNotExist):
initial_stock_location = None
if initial_stock_location is None:
if part.default_location is not None:
initial_stock_location = part.default_location
else:
raise ValidationError({
'initial_stock_location': [_('Specify location for initial part stock')],
})
stock_item = StockItem( stock_item = StockItem(
part=part, part=part,
quantity=initial_stock, quantity=initial_stock_quantity,
location=part.default_location, location=initial_stock_location,
) )
stock_item.save(user=request.user) stock_item.save(user=request.user)
except:
pass
headers = self.get_success_headers(serializer.data) headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

View File

@ -117,10 +117,29 @@ function partFields(options={}) {
delete fields["default_supplier"]; delete fields["default_supplier"];
if (global_settings.PART_CREATE_INITIAL) { if (global_settings.PART_CREATE_INITIAL) {
fields.initial_stock = { fields.initial_stock = {
type: 'boolean',
label: '{% trans "Create Initial Stock" %}',
help_text: '{% trans "Create an initial stock item for this part" %}',
group: 'create',
};
fields.initial_stock_quantity = {
type: 'decimal', type: 'decimal',
label: '{% trans "Initial Stock Quantity" %}', label: '{% trans "Initial Stock Quantity" %}',
help_text: '{% trans "Initialize part stock with specified quantity" %}', help_text: '{% trans "Specify initial stock quantity for this part" %}',
group: 'create',
};
// TODO - Allow initial location of stock to be specified
fields.initial_stock_location = {
label: '{% trans "Location" %}',
help_text: '{% trans "Select destination stock location" %}',
type: 'related field',
required: true,
api_url: `/api/stock/location/`,
model: 'stocklocation',
group: 'create', group: 'create',
}; };
} }