diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 11694299af..6bc6160af6 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -480,6 +480,8 @@ class BuildItem(models.Model): self.stock_item = item self.save() + # TODO - If the item__part object is not trackable, delete the stock item here + item.status = StockStatus.ASSIGNED_TO_BUILD item.build_order = self.build item.save() diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index d8568ddf3b..0f84d6bc32 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -348,14 +348,17 @@ class SOLineItemList(generics.ListCreateAPIView): queryset = super().get_queryset(*args, **kwargs) - return queryset.prefetch_related( + queryset = queryset.prefetch_related( 'part', 'part__stock_items', 'allocations', 'allocations__item__location', 'order', + 'order__stock_items', ) + return queryset + permission_classes = [permissions.IsAuthenticated] filter_backends = [DjangoFilterBackend] diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index a4501bd9eb..a7a176a7bf 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -324,8 +324,11 @@ class SalesOrder(Order): for allocation in line.allocations.all(): allocation.complete_allocation(user) - # TODO - Remove the allocation from the database - # allocation.delete() + # Remove the allocation from the database once it has been 'fulfilled' + if allocation.item.sales_order == self.order: + allocation.delete() + else: + raise ValidationError("Could not complete order - allocation item not fulfilled") # Ensure the order status is marked as "Shipped" self.status = SalesOrderStatus.SHIPPED @@ -458,6 +461,15 @@ class SalesOrderLineItem(OrderLineItem): part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, help_text=_('Part'), limit_choices_to={'salable': True}) + def fulfilled_quantity(self): + """ + Return the total stock quantity fulfilled against this line item. + """ + + query = self.order.stock_items.filter(part=self.part).aggregate(fulfilled=Coalesce(Sum('quantity'), Decimal(0))) + + return query['fulfilled'] + def allocated_quantity(self): """ Return the total stock quantity allocated to this LineItem. diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index 95ebae34da..0738b9dfbe 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -211,6 +211,7 @@ class SOLineItemSerializer(InvenTreeModelSerializer): quantity = serializers.FloatField() allocated = serializers.FloatField(source='allocated_quantity', read_only=True) + fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True) class Meta: model = SalesOrderLineItem @@ -220,6 +221,7 @@ class SOLineItemSerializer(InvenTreeModelSerializer): 'allocated', 'allocations', 'quantity', + 'fulfilled', 'reference', 'notes', 'order', diff --git a/InvenTree/part/templates/part/stock.html b/InvenTree/part/templates/part/stock.html index a692b11b01..03174f45c3 100644 --- a/InvenTree/part/templates/part/stock.html +++ b/InvenTree/part/templates/part/stock.html @@ -5,7 +5,7 @@ {% include 'part/tabs.html' with tab='stock' %} -
Serial | -Status | -
---|---|
{{ track.serial }} | -{{ track.get_status_display }} | -