diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 4ee1707668..305810e452 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -1467,7 +1467,11 @@ class SalesOrderExtraLine(OrderExtraLine): """Return the API URL associated with the SalesOrderExtraLine model""" return reverse('api-so-extra-line-list') - order = models.ForeignKey(SalesOrder, on_delete=models.CASCADE, related_name='extra_lines', verbose_name=_('Order'), help_text=_('Sales Order')) + order = models.ForeignKey( + SalesOrder, on_delete=models.CASCADE, + related_name='extra_lines', + verbose_name=_('Order'), help_text=_('Sales Order') + ) class SalesOrderAllocation(models.Model): diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index 86281a9761..e8addc3466 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -31,8 +31,8 @@ from part.serializers import PartBriefSerializer from users.serializers import OwnerSerializer -class AbstractOrderSerializer(serializers.Serializer): - """Abstract field definitions for OrderSerializers.""" +class TotalPriceMixin(serializers.Serializer): + """Serializer mixin which provides total price fields""" total_price = InvenTreeMoneySerializer( allow_null=True, @@ -42,6 +42,64 @@ class AbstractOrderSerializer(serializers.Serializer): total_price_currency = InvenTreeCurrencySerializer(read_only=True) +class AbstractOrderSerializer(serializers.Serializer): + """Abstract serializer class which provides fields common to all order types""" + + # Number of line items in this order + line_items = serializers.IntegerField(read_only=True) + + # Human-readable status text (read-only) + status_text = serializers.CharField(source='get_status_display', read_only=True) + + # status field cannot be set directly + status = serializers.IntegerField(read_only=True) + + # Reference string is *required* + reference = serializers.CharField(required=True) + + # Detail for point-of-contact field + contact_detail = ContactSerializer(source='contact', many=False, read_only=True) + + # Detail for responsible field + responsible_detail = OwnerSerializer(source='responsible', read_only=True, many=False) + + def validate_reference(self, reference): + """Custom validation for the reference field""" + + self.Meta.model.validate_reference_field(reference) + return reference + + @staticmethod + def annotate_queryset(queryset): + """Add extra information to the queryset""" + + queryset = queryset.annotate( + line_items=SubqueryCount('lines') + ) + + return queryset + + @staticmethod + def order_fields(extra_fields): + """Construct a set of fields for this serializer""" + + return [ + 'pk', + 'creation_date', + 'description', + 'line_items', + 'link', + 'reference', + 'responsible', + 'responsible_detail', + 'contact', + 'contact_detail', + 'status', + 'status_text', + 'notes', + ] + extra_fields + + class AbstractExtraLineSerializer(serializers.Serializer): """Abstract Serializer for a ExtraLine object.""" @@ -79,7 +137,7 @@ class AbstractExtraLineMeta: ] -class PurchaseOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer): +class PurchaseOrderSerializer(TotalPriceMixin, AbstractOrderSerializer, InvenTreeModelSerializer): """Serializer for a PurchaseOrder object.""" class Meta: @@ -87,33 +145,19 @@ class PurchaseOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer) model = order.models.PurchaseOrder - fields = [ - 'pk', + fields = AbstractOrderSerializer.order_fields([ 'issue_date', 'complete_date', - 'creation_date', - 'description', - 'line_items', - 'link', 'overdue', - 'reference', - 'responsible', - 'responsible_detail', - 'contact', - 'contact_detail', 'supplier', 'supplier_detail', 'supplier_reference', - 'status', - 'status_text', 'target_date', - 'notes', 'total_price', 'total_price_currency', - ] + ]) read_only_fields = [ - 'status' 'issue_date', 'complete_date', 'creation_date', @@ -135,9 +179,7 @@ class PurchaseOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer) - Number of lines in the PurchaseOrder - Overdue status of the PurchaseOrder """ - queryset = queryset.annotate( - line_items=SubqueryCount('lines') - ) + queryset = AbstractOrderSerializer.annotate_queryset(queryset) queryset = queryset.annotate( overdue=Case( @@ -150,28 +192,10 @@ class PurchaseOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer) return queryset - contact_detail = ContactSerializer(source='contact', many=False, read_only=True) - supplier_detail = CompanyBriefSerializer(source='supplier', many=False, read_only=True) - line_items = serializers.IntegerField(read_only=True) - - status_text = serializers.CharField(source='get_status_display', read_only=True) - overdue = serializers.BooleanField(required=False, read_only=True) - reference = serializers.CharField(required=True) - - def validate_reference(self, reference): - """Custom validation for the reference field""" - - # Ensure that the reference matches the required pattern - order.models.PurchaseOrder.validate_reference_field(reference) - - return reference - - responsible_detail = OwnerSerializer(source='responsible', read_only=True, many=False) - class PurchaseOrderCancelSerializer(serializers.Serializer): """Serializer for cancelling a PurchaseOrder.""" @@ -649,7 +673,7 @@ class PurchaseOrderAttachmentSerializer(InvenTreeAttachmentSerializer): ]) -class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer): +class SalesOrderSerializer(TotalPriceMixin, AbstractOrderSerializer, InvenTreeModelSerializer): """Serializer for the SalesOrder model class""" class Meta: @@ -657,28 +681,16 @@ class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer): model = order.models.SalesOrder - fields = [ - 'pk', - 'creation_date', + fields = AbstractOrderSerializer.order_fields([ 'customer', 'customer_detail', - 'contact', - 'contact_detail', 'customer_reference', - 'description', - 'line_items', - 'link', - 'notes', 'overdue', - 'reference', - 'responsible', - 'status', - 'status_text', 'shipment_date', 'target_date', 'total_price', 'total_price_currency', - ] + ]) read_only_fields = [ 'status', @@ -702,9 +714,7 @@ class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer): - Number of line items in the SalesOrder - Overdue status of the SalesOrder """ - queryset = queryset.annotate( - line_items=SubqueryCount('lines') - ) + queryset = AbstractOrderSerializer.annotate_queryset(queryset) queryset = queryset.annotate( overdue=Case( @@ -717,26 +727,10 @@ class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer): return queryset - contact_detail = ContactSerializer(source='contact', many=False, read_only=True) - customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True) - line_items = serializers.IntegerField(read_only=True) - - status_text = serializers.CharField(source='get_status_display', read_only=True) - overdue = serializers.BooleanField(required=False, read_only=True) - reference = serializers.CharField(required=True) - - def validate_reference(self, reference): - """Custom validation for the reference field""" - - # Ensure that the reference matches the required pattern - order.models.SalesOrder.validate_reference_field(reference) - - return reference - class SalesOrderAllocationSerializer(InvenTreeModelSerializer): """Serializer for the SalesOrderAllocation model. @@ -1388,13 +1382,13 @@ class SalesOrderShipmentAllocationSerializer(serializers.Serializer): class SalesOrderExtraLineSerializer(AbstractExtraLineSerializer, InvenTreeModelSerializer): """Serializer for a SalesOrderExtraLine object.""" - order_detail = SalesOrderSerializer(source='order', many=False, read_only=True) - class Meta(AbstractExtraLineMeta): """Metaclass options.""" model = order.models.SalesOrderExtraLine + order_detail = SalesOrderSerializer(source='order', many=False, read_only=True) + class SalesOrderAttachmentSerializer(InvenTreeAttachmentSerializer): """Serializers for the SalesOrderAttachment model.""" @@ -1409,7 +1403,7 @@ class SalesOrderAttachmentSerializer(InvenTreeAttachmentSerializer): ]) -class ReturnOrderSerializer(InvenTreeModelSerializer): +class ReturnOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer): """Serializer for the ReturnOrder model class""" class Meta: @@ -1417,26 +1411,13 @@ class ReturnOrderSerializer(InvenTreeModelSerializer): model = order.models.ReturnOrder - fields = [ - 'pk', - 'creation_date', - 'contact', - 'contact_detail', + fields = AbstractOrderSerializer.order_fields([ 'customer', 'customer_detail', 'customer_reference', - 'description', - 'link', - 'notes', - 'reference', - 'responsible', - 'responsible_detail', - 'status', - 'status_text', - ] + ]) read_only_fields = [ - 'status', 'creation_date', ] @@ -1454,26 +1435,11 @@ class ReturnOrderSerializer(InvenTreeModelSerializer): def annotate_queryset(queryset): """Custom annotation for the serializer queryset""" - # TODO + queryset = AbstractOrderSerializer.annotate_queryset(queryset) return queryset - contact_detail = ContactSerializer(source='contact', many=False, read_only=True) - customer_detail = CompanyBriefSerializer(source='customer', many=False, read_only=True) - status_text = serializers.CharField(source='get_status_display', read_only=True) - - responsible_detail = OwnerSerializer(source='responsible', read_only=True, many=False) - - reference = serializers.CharField(required=True) - - def validate_reference(self, reference): - """Custom validation for the reference field""" - - order.models.ReturnOrder.validate_reference_field(reference) - - return reference - class ReturnOrderAttachmentSerializer(InvenTreeAttachmentSerializer): """Serializer for the ReturnOrderAttachment model"""