2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-05-03 13:58:47 +00:00

Add "batch code" and "serial numbers" serializer fields when receiving stock items against a purchase order

This commit is contained in:
Oliver 2022-02-28 22:47:41 +11:00
parent 010ce48ce0
commit 73484192a5
2 changed files with 107 additions and 35 deletions

View File

@ -398,12 +398,22 @@ class PurchaseOrder(Order):
return self.lines.count() > 0 and self.pending_line_items().count() == 0 return self.lines.count() > 0 and self.pending_line_items().count() == 0
@transaction.atomic @transaction.atomic
def receive_line_item(self, line, location, quantity, user, status=StockStatus.OK, purchase_price=None, **kwargs): def receive_line_item(self, line, location, quantity, user, status=StockStatus.OK, **kwargs):
""" Receive a line item (or partial line item) against this PO """
Receive a line item (or partial line item) against this PO
""" """
# Extract optional batch code for the new stock item
batch_code = kwargs.get('batch_code', '')
# Extract optional list of serial numbers
serials = kwargs.get('serials', None)
# Extract optional notes field
notes = kwargs.get('notes', '') notes = kwargs.get('notes', '')
barcode = kwargs.get('barcode', '')
# Extract optional barcode field
barcode = kwargs.get('barcode', None)
# Prevent null values for barcode # Prevent null values for barcode
if barcode is None: if barcode is None:
@ -427,33 +437,44 @@ class PurchaseOrder(Order):
# Create a new stock item # Create a new stock item
if line.part and quantity > 0: if line.part and quantity > 0:
stock = stock_models.StockItem(
part=line.part.part,
supplier_part=line.part,
location=location,
quantity=quantity,
purchase_order=self,
status=status,
purchase_price=line.purchase_price,
uid=barcode
)
stock.save(add_note=False) # Determine if we should individually serialize the items, or not
if type(serials) is list and len(serials) > 0:
quantity = 1
else:
serials = [None]
tracking_info = { for sn in serials:
'status': status,
'purchaseorder': self.pk,
}
stock.add_tracking_entry( stock = stock_models.StockItem(
StockHistoryCode.RECEIVED_AGAINST_PURCHASE_ORDER, part=line.part.part,
user, supplier_part=line.part,
notes=notes, location=location,
deltas=tracking_info, quantity=quantity,
location=location, purchase_order=self,
purchaseorder=self, status=status,
quantity=quantity batch=batch_code,
) serial=sn,
purchase_price=line.purchase_price,
uid=barcode
)
stock.save(add_note=False)
tracking_info = {
'status': status,
'purchaseorder': self.pk,
}
stock.add_tracking_entry(
StockHistoryCode.RECEIVED_AGAINST_PURCHASE_ORDER,
user,
notes=notes,
deltas=tracking_info,
location=location,
purchaseorder=self,
quantity=quantity
)
# Update the number of parts received against the particular line item # Update the number of parts received against the particular line item
line.received += quantity line.received += quantity

View File

@ -5,6 +5,8 @@ JSON serializers for the Order API
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals from __future__ import unicode_literals
from decimal import Decimal
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
@ -203,6 +205,17 @@ class POLineItemReceiveSerializer(serializers.Serializer):
A serializer for receiving a single purchase order line item against a purchase order A serializer for receiving a single purchase order line item against a purchase order
""" """
class Meta:
fields = [
'barcode',
'line_item',
'location',
'quantity',
'status',
'batch_code'
'serial_numbers',
]
line_item = serializers.PrimaryKeyRelatedField( line_item = serializers.PrimaryKeyRelatedField(
queryset=order.models.PurchaseOrderLineItem.objects.all(), queryset=order.models.PurchaseOrderLineItem.objects.all(),
many=False, many=False,
@ -241,6 +254,22 @@ class POLineItemReceiveSerializer(serializers.Serializer):
return quantity return quantity
batch_code = serializers.CharField(
label=_('Batch Code'),
help_text=_('Enter batch code for incoming stock items'),
required=False,
default='',
allow_blank=True,
)
serial_numbers = serializers.CharField(
label=_('Serial Numbers'),
help_text=_('Enter serial numbers for incoming stock items'),
required=False,
default='',
allow_blank=True,
)
status = serializers.ChoiceField( status = serializers.ChoiceField(
choices=list(StockStatus.items()), choices=list(StockStatus.items()),
default=StockStatus.OK, default=StockStatus.OK,
@ -270,15 +299,35 @@ class POLineItemReceiveSerializer(serializers.Serializer):
return barcode return barcode
class Meta: def validate(self, data):
fields = [
'barcode',
'line_item',
'location',
'quantity',
'status',
]
data = super().validate(data)
line_item = data['line_item']
quantity = data['quantity']
serial_numbers = data.get('serial_numbers', '').strip()
base_part = line_item.part.part
# Does the quantity need to be "integer" (for trackable parts?)
if base_part.trackable:
if Decimal(quantity) != int(quantity):
raise ValidationError({
'quantity': _('An integer quantity must be provided for trackable parts'),
})
# If serial numbers are provided
if serial_numbers:
try:
# Pass the serial numbers through to the parent serializer once validated
data['serials'] = extract_serial_numbers(serial_numbers, quantity, base_part.getLatestSerialNumberInt())
except DjangoValidationError as e:
raise ValidationError({
'serial_numbers': e.messages,
})
return data
class POReceiveSerializer(serializers.Serializer): class POReceiveSerializer(serializers.Serializer):
""" """
@ -366,6 +415,8 @@ class POReceiveSerializer(serializers.Serializer):
request.user, request.user,
status=item['status'], status=item['status'],
barcode=item.get('barcode', ''), barcode=item.get('barcode', ''),
batch_code=item.get('batch_code', ''),
serials=item.get('serials', None),
) )
except (ValidationError, DjangoValidationError) as exc: except (ValidationError, DjangoValidationError) as exc:
# Catch model errors and re-throw as DRF errors # Catch model errors and re-throw as DRF errors