From 943b27e1958aee67607c22f5a32feb740e9e932a Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 20 Dec 2021 19:29:08 +1100 Subject: [PATCH] Adds "items" list to API endpoint --- InvenTree/order/models.py | 28 ++++++++++++----------- InvenTree/stock/models.py | 42 ++++++++++++++++++++++++++++++++++ InvenTree/stock/serializers.py | 39 +++++++++++++++++++++++++++++-- 3 files changed, 94 insertions(+), 15 deletions(-) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 3a7d39d7b5..e798ee4e30 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -628,28 +628,30 @@ class SalesOrder(Order): Throws a ValidationError if cannot be completed. """ - # Order without line items cannot be completed - if self.lines.count() == 0: - if raise_error: + try: + + # Order without line items cannot be completed + if self.lines.count() == 0: raise ValidationError(_('Order cannot be completed as no parts have been assigned')) - # Only a PENDING order can be marked as SHIPPED - elif self.status != SalesOrderStatus.PENDING: - if raise_error: + # Only a PENDING order can be marked as SHIPPED + elif self.status != SalesOrderStatus.PENDING: raise ValidationError(_('Only a pending order can be marked as complete')) - elif self.pending_shipment_count > 0: - if raise_error: + elif self.pending_shipment_count > 0: raise ValidationError(_("Order cannot be completed as there are incomplete shipments")) - elif self.pending_line_count > 0: - if raise_error: + elif self.pending_line_count > 0: raise ValidationError(_("Order cannot be completed as there are incomplete line items")) - else: - return True + except ValidationError as e: - return False + if raise_error: + raise e + else: + return False + + return True def complete_order(self, user): """ diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 7f385e7136..2c9773e6e7 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -1153,6 +1153,48 @@ class StockItem(MPTTModel): result.stock_item = self result.save() + def can_merge(self, other=None, raise_error=False): + """ + Check if this stock item can be merged into another + """ + + try: + if not self.in_stock: + raise ValidationError(_("Item must be in stock")) + + if self.serialized: + raise ValidationError(_("Serialized stock cannot be merged")) + + except ValidationError as e: + if raise_error: + raise e + else: + return False + + return True + + @transaction.atomic + def merge_stock_item(self, other, **kwargs): + """ + Merge another stock item into this one; the two become one! + + *This* stock item subsumes the other, which is essentially deleted: + + - The quantity of this StockItem is increased + - Tracking history for the *other* item is deleted + - Any allocations (build order, sales order) are moved to this StockItem + """ + + # If the stock item cannot be merged, return + if not self.can_merge(other): + return + + user = kwargs.get('user', None) + location = kwargs.get('location', None) + + # TODO: Merge! + + @transaction.atomic def splitStock(self, quantity, location, user, **kwargs): """ Split this stock item into two items, in the same location. diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 551d660f7b..cf30252cac 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -674,6 +674,33 @@ class StockAssignmentSerializer(serializers.Serializer): ) +class StockMergeItemSerializer(serializers.Serializer): + """ + Serializer for a single StockItem within the StockMergeSerializer class. + + Here, the individual StockItem is being checked for merge compatibility. + """ + + class Meta: + fields = [ + 'item', + ] + + item = serializers.PrimaryKeyRelatedField( + queryset=StockItem.objects.all(), + many=False, + allow_null=False, + required=True, + label=_('Stock Item'), + ) + + def validate_item(self, item): + + # Check that the stock item is able to be merged + item.can_merge(raise_error=True) + + return item + class StockMergeSerializer(serializers.Serializer): """ Serializer for merging two (or more) stock items together @@ -681,10 +708,15 @@ class StockMergeSerializer(serializers.Serializer): class Meta: fields = [ - # 'items', + 'items', 'location', ] + items = StockMergeItemSerializer( + many=True, + required=True, + ) + location = serializers.PrimaryKeyRelatedField( queryset=StockLocation.objects.all(), many=False, @@ -698,7 +730,10 @@ class StockMergeSerializer(serializers.Serializer): data = super().validate(data) - # TODO: Custom data validation + items = data['items'] + + if len(items) < 2: + raise ValidationError(_('At least two stock items must be provided')) return data