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:
parent
1396c349c8
commit
cb11df4dba
@ -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)
|
||||||
|
@ -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',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user