diff --git a/InvenTree/order/api.py b/InvenTree/order/api.py index b5622d7ce8..66c61bd0d7 100644 --- a/InvenTree/order/api.py +++ b/InvenTree/order/api.py @@ -274,7 +274,7 @@ class POLineItemFilter(rest_filters.FilterSet): model = models.PurchaseOrderLineItem fields = [ 'order', - 'part' + 'part', ] pending = rest_filters.BooleanFilter(label='pending', method='filter_pending') @@ -391,6 +391,7 @@ class POLineItemList(generics.ListCreateAPIView): 'reference', 'SKU', 'total_price', + 'target_date', ] search_fields = [ @@ -401,11 +402,6 @@ class POLineItemList(generics.ListCreateAPIView): 'reference', ] - filter_fields = [ - 'order', - 'part' - ] - class POLineItemDetail(generics.RetrieveUpdateDestroyAPIView): """ @@ -703,6 +699,7 @@ class SOLineItemList(generics.ListCreateAPIView): 'part__name', 'quantity', 'reference', + 'target_date', ] search_fields = [ diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 4c04c1ba74..1618d336af 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -799,6 +799,14 @@ class OrderLineItem(models.Model): target_date: An (optional) date for expected shipment of this line item. """ + """ + Query filter for determining if an individual line item is "overdue": + - Amount received is less than the required quantity + - Target date is not None + - Target date is in the past + """ + OVERDUE_FILTER = Q(received__lt=F('quantity')) & ~Q(target_date=None) & Q(target_date__lt=datetime.now().date()) + class Meta: abstract = True diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index 251b088188..be75bf1533 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -5,12 +5,14 @@ JSON serializers for the Order API # -*- coding: utf-8 -*- from __future__ import unicode_literals +from datetime import datetime + from django.utils.translation import ugettext_lazy as _ from django.core.exceptions import ValidationError as DjangoValidationError from django.db import models, transaction from django.db.models import Case, When, Value -from django.db.models import BooleanField, ExpressionWrapper, F +from django.db.models import BooleanField, ExpressionWrapper, F, Q from rest_framework import serializers from rest_framework.serializers import ValidationError @@ -26,7 +28,7 @@ from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeDecimalField from InvenTree.serializers import InvenTreeMoneySerializer from InvenTree.serializers import ReferenceIndexingSerializerMixin -from InvenTree.status_codes import StockStatus +from InvenTree.status_codes import StockStatus, PurchaseOrderStatus, SalesOrderStatus import order.models @@ -126,6 +128,7 @@ class POLineItemSerializer(InvenTreeModelSerializer): Add some extra annotations to this queryset: - Total price = purchase_price * quantity + - "Overdue" status (boolean field) """ queryset = queryset.annotate( @@ -135,6 +138,16 @@ class POLineItemSerializer(InvenTreeModelSerializer): ) ) + # Add an annotated 'overdue' field + queryset = queryset.annotate( + overdue=Case( + When(Q(order__status__in=PurchaseOrderStatus.OPEN) & order.models.OrderLineItem.OVERDUE_FILTER, + then=Value(True, output_field=BooleanField()), + ), + default=Value(False, output_field=BooleanField()), + ) + ) + return queryset def __init__(self, *args, **kwargs): @@ -155,6 +168,8 @@ class POLineItemSerializer(InvenTreeModelSerializer): quantity = serializers.FloatField(default=1) received = serializers.FloatField(default=0) + overdue = serializers.BooleanField() + total_price = serializers.FloatField(read_only=True) part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True) @@ -185,6 +200,7 @@ class POLineItemSerializer(InvenTreeModelSerializer): 'notes', 'order', 'order_detail', + 'overdue', 'part', 'part_detail', 'supplier_part_detail',