From 528fa349b08f4346d15aec27af545c1d2932c310 Mon Sep 17 00:00:00 2001 From: Oliver Date: Sat, 9 Sep 2023 00:34:30 +1000 Subject: [PATCH] Transfer fields (#5518) * Add extra API fields when performing stock adjustments * Make optional fields actually optional * Update API version * Allow usage of optional fields when transferring stock items * Update api_version.py --- InvenTree/InvenTree/api_version.py | 5 +++- InvenTree/stock/models.py | 45 +++++++++++++++++++++++++----- InvenTree/stock/serializers.py | 42 ++++++++++++++++++++++++++-- 3 files changed, 82 insertions(+), 10 deletions(-) diff --git a/InvenTree/InvenTree/api_version.py b/InvenTree/InvenTree/api_version.py index 4a50d1331f..91f695b72c 100644 --- a/InvenTree/InvenTree/api_version.py +++ b/InvenTree/InvenTree/api_version.py @@ -2,11 +2,14 @@ # InvenTree API version -INVENTREE_API_VERSION = 132 +INVENTREE_API_VERSION = 133 """ Increment this API version number whenever there is a significant change to the API that any clients need to know about +v133 -> 2023-09-08 : https://github.com/inventree/InvenTree/pull/5518 + - Add extra optional fields which can be used for StockAdjustment endpoints + v132 -> 2023-09-07 : https://github.com/inventree/InvenTree/pull/5515 - Add 'issued_by' filter to BuildOrder API list endpoint diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 12092d3eba..9079c76894 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -1586,6 +1586,12 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo quantity: Number of stock items to remove from this entity, and pass to the next location: Where to move the new StockItem to + kwargs: + notes: Optional notes for tracking + batch: If provided, override the batch (default = existing batch) + status: If provided, override the status (default = existing status) + packaging: If provided, override the packaging (default = existing packaging) + Returns: The new StockItem object @@ -1624,6 +1630,16 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo else: new_stock.location = self.location + deltas = { + 'stockitem': self.pk, + } + + # Optional fields which can be supplied in a 'move' call + for field in StockItem.optional_transfer_fields(): + if field in kwargs: + setattr(new_stock, field, kwargs[field]) + deltas[field] = kwargs[field] + new_stock.save(add_note=False) # Add a stock tracking entry for the newly created item @@ -1633,9 +1649,7 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo quantity=quantity, notes=notes, location=location, - deltas={ - 'stockitem': self.pk, - } + deltas=deltas, ) # Copy the test results of this part to the new one @@ -1654,6 +1668,11 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo # Return a copy of the "new" stock item return new_stock + @classmethod + def optional_transfer_fields(cls): + """Returns a list of optional fields for a stock transfer""" + return ['batch', 'status', 'packaging'] + @transaction.atomic def move(self, location, notes, user, **kwargs): """Move part to a new location. @@ -1667,11 +1686,15 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo location: Destination location (cannot be null) notes: User notes user: Who is performing the move - kwargs: - quantity: If provided, override the quantity (default = total stock quantity) + + kwargs: + quantity: If provided, override the quantity (default = total stock quantity) + batch: If provided, override the batch (default = existing batch) + status: If provided, override the status (default = existing status) + packaging: If provided, override the packaging (default = existing packaging) """ try: - quantity = Decimal(kwargs.get('quantity', self.quantity)) + quantity = Decimal(kwargs.pop('quantity', self.quantity)) except InvalidOperation: return False @@ -1689,8 +1712,10 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo if quantity < self.quantity: # We need to split the stock! + kwargs['notes'] = notes + # Split the existing StockItem in two - self.splitStock(quantity, location, user, **{'notes': notes}) + self.splitStock(quantity, location, user, **kwargs) return True @@ -1708,6 +1733,12 @@ class StockItem(InvenTreeBarcodeMixin, InvenTreeNotesMixin, MetadataMixin, commo else: tracking_info['location'] = location.pk + # Optional fields which can be supplied in a 'move' call + for field in StockItem.optional_transfer_fields(): + if field in kwargs: + setattr(self, field, kwargs[field]) + tracking_info[field] = kwargs[field] + self.add_tracking_entry( tracking_code, user, diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 756b510b2c..cc6822750e 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -1138,9 +1138,16 @@ class StockMergeSerializer(serializers.Serializer): class StockAdjustmentItemSerializer(serializers.Serializer): """Serializer for a single StockItem within a stock adjument request. - Fields: + Required Fields: - item: StockItem object - quantity: Numerical quantity + + Optional Fields (may be used by external tools) + - status: Change StockItem status code + - packaging: Change StockItem packaging + - batch: Change StockItem batch code + + The optional fields can be used to adjust values for individual stock items """ class Meta: @@ -1167,6 +1174,28 @@ class StockAdjustmentItemSerializer(serializers.Serializer): required=True ) + batch = serializers.CharField( + max_length=100, + required=False, allow_blank=True, + label=_('Batch Code'), + help_text=_('Batch code for this stock item'), + ) + + status = serializers.ChoiceField( + choices=InvenTree.status_codes.StockStatus.items(), + default=InvenTree.status_codes.StockStatus.OK.value, + label=_('Status'), + help_text=_('Stock item status code'), + required=False, allow_blank=True, + ) + + packaging = serializers.CharField( + max_length=50, + required=False, allow_blank=True, + label=_('Packaging'), + help_text=_('Packaging this stock item is stored in'), + ) + class StockAdjustmentSerializer(serializers.Serializer): """Base class for managing stock adjustment actions via the API.""" @@ -1304,12 +1333,21 @@ class StockTransferSerializer(StockAdjustmentSerializer): with transaction.atomic(): for item in items: + # Required fields stock_item = item['pk'] quantity = item['quantity'] + # Optional fields + kwargs = {} + + for field_name in StockItem.optional_transfer_fields(): + if field_name in item: + kwargs[field_name] = item[field_name] + stock_item.move( location, notes, request.user, - quantity=quantity + quantity=quantity, + **kwargs )