diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 789ba9b9b7..dc2be0a378 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -9,12 +9,14 @@ from django.conf.urls import url, include from django.urls import reverse from django.http import JsonResponse 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 rest_framework import status from rest_framework.response import Response from rest_framework import filters, serializers from rest_framework import generics +from rest_framework.exceptions import ValidationError from django_filters.rest_framework import DjangoFilterBackend 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.exceptions import MissingRate -from decimal import Decimal +from decimal import Decimal, InvalidOperation from .models import Part, PartCategory, BomItem from .models import PartParameter, PartParameterTemplate @@ -31,7 +33,9 @@ from .models import PartAttachment, PartTestTemplate from .models import PartSellPriceBreak, PartInternalPriceBreak from .models import PartCategoryParameterTemplate -from stock.models import StockItem + +from stock.models import StockItem, StockLocation + from common.models import InvenTreeSetting from build.models import Build @@ -630,6 +634,7 @@ class PartList(generics.ListCreateAPIView): else: return Response(data) + @transaction.atomic def create(self, request, *args, **kwargs): """ 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 """ + #TODO: Unit tests for this function! + serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) @@ -680,22 +687,50 @@ class PartList(generics.ListCreateAPIView): pass # Optionally create initial stock item - try: - initial_stock = Decimal(request.data.get('initial_stock', 0)) + initial_stock = str2bool(request.data.get('initial_stock', False)) - 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( part=part, - quantity=initial_stock, - location=part.default_location, + quantity=initial_stock_quantity, + location=initial_stock_location, ) stock_item.save(user=request.user) - except: - pass - headers = self.get_success_headers(serializer.data) return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers) diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js index e777968711..0fd43934fe 100644 --- a/InvenTree/templates/js/translated/part.js +++ b/InvenTree/templates/js/translated/part.js @@ -117,10 +117,29 @@ function partFields(options={}) { delete fields["default_supplier"]; if (global_settings.PART_CREATE_INITIAL) { + 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', 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', }; }