mirror of
https://github.com/inventree/InvenTree.git
synced 2025-08-19 18:05:54 +00:00
Adds API endpoint for serialization of stock items
This commit is contained in:
@@ -9,6 +9,7 @@ from decimal import Decimal
|
||||
from datetime import datetime, timedelta
|
||||
from django.db import transaction
|
||||
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db.models.functions import Coalesce
|
||||
from django.db.models import Case, When, Value
|
||||
@@ -27,14 +28,15 @@ from .models import StockItemTestResult
|
||||
|
||||
import common.models
|
||||
from common.settings import currency_code_default, currency_code_mappings
|
||||
|
||||
from company.serializers import SupplierPartSerializer
|
||||
|
||||
import InvenTree.helpers
|
||||
import InvenTree.serializers
|
||||
|
||||
from part.serializers import PartBriefSerializer
|
||||
from InvenTree.serializers import UserSerializerBrief, InvenTreeModelSerializer, InvenTreeMoneySerializer
|
||||
from InvenTree.serializers import InvenTreeAttachmentSerializer, InvenTreeAttachmentSerializerField
|
||||
|
||||
|
||||
class LocationBriefSerializer(InvenTreeModelSerializer):
|
||||
class LocationBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
"""
|
||||
Provides a brief serializer for a StockLocation object
|
||||
"""
|
||||
@@ -48,7 +50,7 @@ class LocationBriefSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class StockItemSerializerBrief(InvenTreeModelSerializer):
|
||||
class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
""" Brief serializers for a StockItem """
|
||||
|
||||
location_name = serializers.CharField(source='location', read_only=True)
|
||||
@@ -70,7 +72,7 @@ class StockItemSerializerBrief(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class StockItemSerializer(InvenTreeModelSerializer):
|
||||
class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
""" Serializer for a StockItem:
|
||||
|
||||
- Includes serialization for the linked part
|
||||
@@ -146,7 +148,7 @@ class StockItemSerializer(InvenTreeModelSerializer):
|
||||
|
||||
required_tests = serializers.IntegerField(source='required_test_count', read_only=True, required=False)
|
||||
|
||||
purchase_price = InvenTreeMoneySerializer(
|
||||
purchase_price = InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
label=_('Purchase Price'),
|
||||
max_digits=19, decimal_places=4,
|
||||
allow_null=True,
|
||||
@@ -247,14 +249,127 @@ class StockItemSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class StockQuantitySerializer(InvenTreeModelSerializer):
|
||||
class SerializeStockItemSerializer(serializers.Serializer):
|
||||
"""
|
||||
A DRF serializer for "serializing" a StockItem.
|
||||
|
||||
(Sorry for the confusing naming...)
|
||||
|
||||
Here, "serializing" means splitting out a single StockItem,
|
||||
into multiple single-quantity items with an assigned serial number
|
||||
|
||||
Note: The base StockItem object is provided to the serializer context
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = StockItem
|
||||
fields = ('quantity',)
|
||||
fields = [
|
||||
'quantity',
|
||||
'serial_numbers',
|
||||
'destination',
|
||||
'notes',
|
||||
]
|
||||
|
||||
quantity = serializers.IntegerField(
|
||||
min_value=0,
|
||||
required=True,
|
||||
label=_('Quantity'),
|
||||
help_text=_('Enter number of stock items to serialize'),
|
||||
)
|
||||
|
||||
def validate_quantity(self, quantity):
|
||||
|
||||
item = self.context['item']
|
||||
|
||||
if quantity < 0:
|
||||
raise ValidationError(_("Quantity must be greater than zero"))
|
||||
|
||||
if quantity > item.quantity:
|
||||
q = item.quantity
|
||||
raise ValidationError(_(f"Quantity must not exceed available stock quantity ({q})"))
|
||||
|
||||
return quantity
|
||||
|
||||
serial_numbers = serializers.CharField(
|
||||
label=_('Serial Numbers'),
|
||||
help_text=_('Enter serial numbers for new items'),
|
||||
allow_blank=False,
|
||||
required=True,
|
||||
)
|
||||
|
||||
destination = serializers.PrimaryKeyRelatedField(
|
||||
queryset=StockLocation.objects.all(),
|
||||
many=False,
|
||||
required=True,
|
||||
allow_null=False,
|
||||
label=_('Location'),
|
||||
help_text=_('Destination stock location'),
|
||||
)
|
||||
|
||||
notes = serializers.CharField(
|
||||
required=False,
|
||||
allow_blank=True,
|
||||
label=_("Notes"),
|
||||
help_text=_("Optional note field")
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
"""
|
||||
Check that the supplied serial numbers are valid
|
||||
"""
|
||||
|
||||
data = super().validate(data)
|
||||
|
||||
item = self.context['item']
|
||||
|
||||
if not item.part.trackable:
|
||||
raise ValidationError(_("Serial numbers cannot be assigned to this part"))
|
||||
|
||||
# Ensure the serial numbers are valid!
|
||||
quantity = data['quantity']
|
||||
serial_numbers = data['serial_numbers']
|
||||
|
||||
try:
|
||||
serials = InvenTree.helpers.extract_serial_numbers(serial_numbers, quantity)
|
||||
except DjangoValidationError as e:
|
||||
raise ValidationError({
|
||||
'serial_numbers': e.messages,
|
||||
})
|
||||
|
||||
existing = item.part.find_conflicting_serial_numbers(serials)
|
||||
|
||||
if len(existing) > 0:
|
||||
exists = ','.join([str(x) for x in existing])
|
||||
error = _('Serial numbers already exist') + ": " + exists
|
||||
|
||||
raise ValidationError({
|
||||
'serial_numbers': error,
|
||||
})
|
||||
|
||||
return data
|
||||
|
||||
def save(self):
|
||||
|
||||
item = self.context['item']
|
||||
request = self.context['request']
|
||||
user = request.user
|
||||
|
||||
data = self.validated_data
|
||||
|
||||
serials = InvenTree.helpers.extract_serial_numbers(
|
||||
data['serial_numbers'],
|
||||
data['quantity'],
|
||||
)
|
||||
|
||||
item.serializeStock(
|
||||
data['quantity'],
|
||||
serials,
|
||||
user,
|
||||
notes=data.get('notes', ''),
|
||||
location=data['destination'],
|
||||
)
|
||||
|
||||
|
||||
class LocationSerializer(InvenTreeModelSerializer):
|
||||
class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
""" Detailed information about a stock location
|
||||
"""
|
||||
|
||||
@@ -278,7 +393,7 @@ class LocationSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class StockItemAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
class StockItemAttachmentSerializer(InvenTree.serializers.InvenTreeAttachmentSerializer):
|
||||
""" Serializer for StockItemAttachment model """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -289,9 +404,9 @@ class StockItemAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
if user_detail is not True:
|
||||
self.fields.pop('user_detail')
|
||||
|
||||
user_detail = UserSerializerBrief(source='user', read_only=True)
|
||||
user_detail = InvenTree.serializers.UserSerializerBrief(source='user', read_only=True)
|
||||
|
||||
attachment = InvenTreeAttachmentSerializerField(required=True)
|
||||
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(required=True)
|
||||
|
||||
# TODO: Record the uploading user when creating or updating an attachment!
|
||||
|
||||
@@ -316,14 +431,14 @@ class StockItemAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
]
|
||||
|
||||
|
||||
class StockItemTestResultSerializer(InvenTreeModelSerializer):
|
||||
class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
""" Serializer for the StockItemTestResult model """
|
||||
|
||||
user_detail = UserSerializerBrief(source='user', read_only=True)
|
||||
user_detail = InvenTree.serializers.UserSerializerBrief(source='user', read_only=True)
|
||||
|
||||
key = serializers.CharField(read_only=True)
|
||||
|
||||
attachment = InvenTreeAttachmentSerializerField(required=False)
|
||||
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
user_detail = kwargs.pop('user_detail', False)
|
||||
@@ -357,7 +472,7 @@ class StockItemTestResultSerializer(InvenTreeModelSerializer):
|
||||
]
|
||||
|
||||
|
||||
class StockTrackingSerializer(InvenTreeModelSerializer):
|
||||
class StockTrackingSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
""" Serializer for StockItemTracking model """
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -377,7 +492,7 @@ class StockTrackingSerializer(InvenTreeModelSerializer):
|
||||
|
||||
item_detail = StockItemSerializerBrief(source='item', many=False, read_only=True)
|
||||
|
||||
user_detail = UserSerializerBrief(source='user', many=False, read_only=True)
|
||||
user_detail = InvenTree.serializers.UserSerializerBrief(source='user', many=False, read_only=True)
|
||||
|
||||
deltas = serializers.JSONField(read_only=True)
|
||||
|
||||
|
Reference in New Issue
Block a user