Beispiel #1
0
class PartInternalPriceSerializer(InvenTreeModelSerializer):
    """
    Serializer for internal prices for Part model.
    """

    quantity = InvenTreeDecimalField()

    price = InvenTreeMoneySerializer(allow_null=True)

    price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        default=currency_code_default,
        label=_('Currency'),
        help_text=_('Purchase currency of this stock item'),
    )

    price_string = serializers.CharField(source='price', read_only=True)

    class Meta:
        model = PartInternalPriceBreak
        fields = [
            'pk',
            'part',
            'quantity',
            'price',
            'price_currency',
            'price_string',
        ]
Beispiel #2
0
class SOLineItemSerializer(InvenTreeModelSerializer):
    """ Serializer for a SalesOrderLineItem object """

    def __init__(self, *args, **kwargs):

        part_detail = kwargs.pop('part_detail', False)
        order_detail = kwargs.pop('order_detail', False)
        allocations = kwargs.pop('allocations', False)

        super().__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if order_detail is not True:
            self.fields.pop('order_detail')

        if allocations is not True:
            self.fields.pop('allocations')

    order_detail = SalesOrderSerializer(source='order', many=False, read_only=True)
    part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
    allocations = SalesOrderAllocationSerializer(many=True, read_only=True, location_detail=True)

    quantity = InvenTreeDecimalField()

    allocated = serializers.FloatField(source='allocated_quantity', read_only=True)

    shipped = InvenTreeDecimalField(read_only=True)

    sale_price = InvenTreeMoneySerializer(
        allow_null=True
    )

    sale_price_string = serializers.CharField(source='sale_price', read_only=True)

    sale_price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        help_text=_('Sale price currency'),
    )

    class Meta:
        model = order.models.SalesOrderLineItem

        fields = [
            'pk',
            'allocated',
            'allocations',
            'quantity',
            'reference',
            'notes',
            'order',
            'order_detail',
            'part',
            'part_detail',
            'sale_price',
            'sale_price_currency',
            'sale_price_string',
            'shipped',
        ]
Beispiel #3
0
class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
    """ Serializer for SupplierPriceBreak object """

    quantity = InvenTreeDecimalField()

    price = InvenTreeMoneySerializer(
        allow_null=True,
        required=True,
        label=_('Price'),
    )

    price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        default=currency_code_default,
        label=_('Currency'),
    )

    class Meta:
        model = SupplierPriceBreak
        fields = [
            'pk',
            'part',
            'quantity',
            'price',
            'price_currency',
        ]
Beispiel #4
0
def money_kwargs():
    """ returns the database settings for MoneyFields """
    from common.settings import currency_code_mappings, currency_code_default

    kwargs = {}
    kwargs['currency_choices'] = currency_code_mappings()
    kwargs['default_currency'] = currency_code_default()
    return kwargs
Beispiel #5
0
class CompanySerializer(InvenTreeModelSerializer):
    """Serializer for Company object (full detail)"""

    @staticmethod
    def annotate_queryset(queryset):
        """Annoate the supplied queryset with aggregated information"""
        # Add count of parts manufactured
        queryset = queryset.annotate(
            parts_manufactured=SubqueryCount('manufactured_parts')
        )

        queryset = queryset.annotate(
            parts_supplied=SubqueryCount('supplied_parts')
        )

        return queryset

    url = serializers.CharField(source='get_absolute_url', read_only=True)

    image = InvenTreeImageSerializerField(required=False, allow_null=True)

    parts_supplied = serializers.IntegerField(read_only=True)
    parts_manufactured = serializers.IntegerField(read_only=True)

    currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        initial=currency_code_default,
        help_text=_('Default currency used for this supplier'),
        label=_('Currency Code'),
        required=True,
    )

    class Meta:
        """Metaclass options."""

        model = Company
        fields = [
            'pk',
            'url',
            'name',
            'description',
            'website',
            'name',
            'phone',
            'address',
            'email',
            'currency',
            'contact',
            'link',
            'image',
            'is_customer',
            'is_manufacturer',
            'is_supplier',
            'notes',
            'parts_supplied',
            'parts_manufactured',
        ]
Beispiel #6
0
class AbstractExtraLineSerializer(serializers.Serializer):
    """ Abstract Serializer for a ExtraLine object """
    def __init__(self, *args, **kwargs):

        order_detail = kwargs.pop('order_detail', False)

        super().__init__(*args, **kwargs)

        if order_detail is not True:
            self.fields.pop('order_detail')

    quantity = serializers.FloatField()

    price = InvenTreeMoneySerializer(allow_null=True)

    price_string = serializers.CharField(source='price', read_only=True)

    price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        help_text=_('Price currency'),
    )
Beispiel #7
0
class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
    """ Serializer for SupplierPriceBreak object """

    quantity = serializers.FloatField()

    price = serializers.CharField()

    price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        default=currency_code_default,
        label=_('Currency'),
    )

    class Meta:
        model = SupplierPriceBreak
        fields = [
            'pk',
            'part',
            'quantity',
            'price',
            'price_currency',
        ]
Beispiel #8
0
class POLineItemSerializer(InvenTreeModelSerializer):

    @staticmethod
    def annotate_queryset(queryset):
        """
        Add some extra annotations to this queryset:

        - Total price = purchase_price * quantity
        """

        queryset = queryset.annotate(
            total_price=ExpressionWrapper(
                F('purchase_price') * F('quantity'),
                output_field=models.DecimalField()
            )
        )

        return queryset

    def __init__(self, *args, **kwargs):

        part_detail = kwargs.pop('part_detail', False)

        order_detail = kwargs.pop('order_detail', False)

        super().__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')
            self.fields.pop('supplier_part_detail')

        if order_detail is not True:
            self.fields.pop('order_detail')

    quantity = serializers.FloatField(default=1)
    received = serializers.FloatField(default=0)

    total_price = serializers.FloatField(read_only=True)

    part_detail = PartBriefSerializer(source='get_base_part', many=False, read_only=True)
    supplier_part_detail = SupplierPartSerializer(source='part', many=False, read_only=True)

    purchase_price = InvenTreeMoneySerializer(
        allow_null=True
    )

    purchase_price_string = serializers.CharField(source='purchase_price', read_only=True)

    destination_detail = stock.serializers.LocationBriefSerializer(source='get_destination', read_only=True)

    purchase_price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        help_text=_('Purchase price currency'),
    )

    order_detail = POSerializer(source='order', read_only=True, many=False)

    class Meta:
        model = order.models.PurchaseOrderLineItem

        fields = [
            'pk',
            'quantity',
            'reference',
            'notes',
            'order',
            'order_detail',
            'part',
            'part_detail',
            'supplier_part_detail',
            'received',
            'purchase_price',
            'purchase_price_currency',
            'purchase_price_string',
            'destination',
            'destination_detail',
            'total_price',
        ]
Beispiel #9
0
class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
    """Serializer for a StockItem.

    - Includes serialization for the linked part
    - Includes serialization for the item location
    """

    part = serializers.PrimaryKeyRelatedField(
        queryset=part_models.Part.objects.all(),
        many=False,
        allow_null=False,
        help_text=_("Base Part"),
        label=_("Part"),
    )

    def validate_part(self, part):
        """Ensure the provided Part instance is valid"""

        if part.virtual:
            raise ValidationError(
                _("Stock item cannot be created for virtual parts"))

        return part

    def update(self, instance, validated_data):
        """Custom update method to pass the user information through to the instance."""
        instance._user = self.context['user']

        return super().update(instance, validated_data)

    @staticmethod
    def annotate_queryset(queryset):
        """Add some extra annotations to the queryset, performing database queries as efficiently as possible."""

        queryset = queryset.prefetch_related(
            'sales_order',
            'purchase_order',
        )

        # Annotate the queryset with the total allocated to sales orders
        queryset = queryset.annotate(
            allocated=Coalesce(
                SubquerySum('sales_order_allocations__quantity'), Decimal(0)) +
            Coalesce(SubquerySum('allocations__quantity'), Decimal(0)))

        # Annotate the queryset with the number of tracking items
        queryset = queryset.annotate(
            tracking_items=SubqueryCount('tracking_info'))

        # Add flag to indicate if the StockItem has expired
        queryset = queryset.annotate(
            expired=Case(When(
                StockItem.EXPIRED_FILTER,
                then=Value(True, output_field=BooleanField()),
            ),
                         default=Value(False, output_field=BooleanField())))

        # Add flag to indicate if the StockItem is stale
        stale_days = common.models.InvenTreeSetting.get_setting(
            'STOCK_STALE_DAYS')
        stale_date = datetime.now().date() + timedelta(days=stale_days)
        stale_filter = StockItem.IN_STOCK_FILTER & ~Q(expiry_date=None) & Q(
            expiry_date__lt=stale_date)

        queryset = queryset.annotate(stale=Case(
            When(
                stale_filter,
                then=Value(True, output_field=BooleanField()),
            ),
            default=Value(False, output_field=BooleanField()),
        ))

        return queryset

    status_text = serializers.CharField(source='get_status_display',
                                        read_only=True)

    supplier_part_detail = SupplierPartSerializer(source='supplier_part',
                                                  many=False,
                                                  read_only=True)

    part_detail = PartBriefSerializer(source='part',
                                      many=False,
                                      read_only=True)

    location_detail = LocationBriefSerializer(source='location',
                                              many=False,
                                              read_only=True)

    quantity = InvenTreeDecimalField()

    # Annotated fields
    tracking_items = serializers.IntegerField(read_only=True, required=False)
    allocated = serializers.FloatField(required=False)
    expired = serializers.BooleanField(required=False, read_only=True)
    stale = serializers.BooleanField(required=False, read_only=True)

    purchase_price = InvenTree.serializers.InvenTreeMoneySerializer(
        label=_('Purchase Price'),
        max_digits=19,
        decimal_places=4,
        allow_null=True,
        help_text=_('Purchase price of this stock item'),
    )

    purchase_price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        default=currency_code_default,
        label=_('Currency'),
        help_text=_('Purchase currency of this stock item'),
    )

    purchase_price_string = serializers.SerializerMethodField()

    def get_purchase_price_string(self, obj):
        """Return purchase price as string."""
        if obj.purchase_price:
            obj.purchase_price.decimal_places_display = 4
            return str(obj.purchase_price)

        return '-'

    purchase_order_reference = serializers.CharField(
        source='purchase_order.reference', read_only=True)
    sales_order_reference = serializers.CharField(
        source='sales_order.reference', read_only=True)

    def __init__(self, *args, **kwargs):
        """Add detail fields."""
        part_detail = kwargs.pop('part_detail', False)
        location_detail = kwargs.pop('location_detail', False)
        supplier_part_detail = kwargs.pop('supplier_part_detail', False)

        super(StockItemSerializer, self).__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if location_detail is not True:
            self.fields.pop('location_detail')

        if supplier_part_detail is not True:
            self.fields.pop('supplier_part_detail')

    class Meta:
        """Metaclass options."""

        model = StockItem
        fields = [
            'allocated',
            'batch',
            'belongs_to',
            'build',
            'customer',
            'delete_on_deplete',
            'expired',
            'expiry_date',
            'is_building',
            'link',
            'location',
            'location_detail',
            'notes',
            'owner',
            'packaging',
            'part',
            'part_detail',
            'purchase_order',
            'purchase_order_reference',
            'pk',
            'quantity',
            'sales_order',
            'sales_order_reference',
            'serial',
            'stale',
            'status',
            'status_text',
            'stocktake_date',
            'supplier_part',
            'supplier_part_detail',
            'tracking_items',
            'uid',
            'updated',
            'purchase_price',
            'purchase_price_currency',
            'purchase_price_string',
        ]
        """
        These fields are read-only in this context.
        They can be updated by accessing the appropriate API endpoints
        """
        read_only_fields = [
            'allocated',
            'stocktake_date',
            'stocktake_user',
            'updated',
        ]
Beispiel #10
0
class SalesOrderLineItemSerializer(InvenTreeModelSerializer):
    """ Serializer for a SalesOrderLineItem object """
    @staticmethod
    def annotate_queryset(queryset):
        """
        Add some extra annotations to this queryset:

        - "Overdue" status (boolean field)
        """

        queryset = queryset.annotate(overdue=Case(
            When(
                Q(order__status__in=SalesOrderStatus.OPEN)
                & order.models.OrderLineItem.OVERDUE_FILTER,
                then=Value(True, output_field=BooleanField()),
            ),
            default=Value(False, output_field=BooleanField()),
        ))

    def __init__(self, *args, **kwargs):

        part_detail = kwargs.pop('part_detail', False)
        order_detail = kwargs.pop('order_detail', False)
        allocations = kwargs.pop('allocations', False)

        super().__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if order_detail is not True:
            self.fields.pop('order_detail')

        if allocations is not True:
            self.fields.pop('allocations')

    order_detail = SalesOrderSerializer(source='order',
                                        many=False,
                                        read_only=True)
    part_detail = PartBriefSerializer(source='part',
                                      many=False,
                                      read_only=True)
    allocations = SalesOrderAllocationSerializer(many=True,
                                                 read_only=True,
                                                 location_detail=True)

    overdue = serializers.BooleanField(required=False, read_only=True)

    quantity = InvenTreeDecimalField()

    allocated = serializers.FloatField(source='allocated_quantity',
                                       read_only=True)

    shipped = InvenTreeDecimalField(read_only=True)

    sale_price = InvenTreeMoneySerializer(allow_null=True)

    sale_price_string = serializers.CharField(source='sale_price',
                                              read_only=True)

    sale_price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        help_text=_('Sale price currency'),
    )

    class Meta:
        model = order.models.SalesOrderLineItem

        fields = [
            'pk',
            'allocated',
            'allocations',
            'quantity',
            'reference',
            'notes',
            'order',
            'order_detail',
            'overdue',
            'part',
            'part_detail',
            'sale_price',
            'sale_price_currency',
            'sale_price_string',
            'shipped',
            'target_date',
        ]
Beispiel #11
0
class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
    @staticmethod
    def annotate_queryset(queryset):
        """
        Add some extra annotations to this queryset:

        - Total price = purchase_price * quantity
        - "Overdue" status (boolean field)
        """

        queryset = queryset.annotate(
            total_price=ExpressionWrapper(F('purchase_price') * F('quantity'),
                                          output_field=models.DecimalField()))

        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):

        part_detail = kwargs.pop('part_detail', False)

        order_detail = kwargs.pop('order_detail', False)

        super().__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')
            self.fields.pop('supplier_part_detail')

        if order_detail is not True:
            self.fields.pop('order_detail')

    quantity = serializers.FloatField(min_value=0, required=True)

    def validate_quantity(self, quantity):

        if quantity <= 0:
            raise ValidationError(_("Quantity must be greater than zero"))

        return quantity

    def validate_purchase_order(self, purchase_order):

        if purchase_order.status not in PurchaseOrderStatus.OPEN:
            raise ValidationError(_('Order is not open'))

        return purchase_order

    received = serializers.FloatField(default=0, read_only=True)

    overdue = serializers.BooleanField(required=False, read_only=True)

    total_price = serializers.FloatField(read_only=True)

    part_detail = PartBriefSerializer(source='get_base_part',
                                      many=False,
                                      read_only=True)

    supplier_part_detail = SupplierPartSerializer(source='part',
                                                  many=False,
                                                  read_only=True)

    purchase_price = InvenTreeMoneySerializer(allow_null=True)

    purchase_price_string = serializers.CharField(source='purchase_price',
                                                  read_only=True)

    destination_detail = stock.serializers.LocationBriefSerializer(
        source='get_destination', read_only=True)

    purchase_price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        help_text=_('Purchase price currency'),
    )

    order_detail = PurchaseOrderSerializer(source='order',
                                           read_only=True,
                                           many=False)

    def validate(self, data):

        data = super().validate(data)

        supplier_part = data.get('part', None)
        purchase_order = data.get('order', None)

        if not supplier_part:
            raise ValidationError({
                'part': _('Supplier part must be specified'),
            })

        if not purchase_order:
            raise ValidationError({
                'order':
                _('Purchase order must be specified'),
            })

        # Check that the supplier part and purchase order match
        if supplier_part is not None and supplier_part.supplier != purchase_order.supplier:
            raise ValidationError({
                'part':
                _('Supplier must match purchase order'),
                'order':
                _('Purchase order must match supplier'),
            })

        return data

    class Meta:
        model = order.models.PurchaseOrderLineItem

        fields = [
            'pk',
            'quantity',
            'reference',
            'notes',
            'order',
            'order_detail',
            'overdue',
            'part',
            'part_detail',
            'supplier_part_detail',
            'received',
            'purchase_price',
            'purchase_price_currency',
            'purchase_price_string',
            'destination',
            'destination_detail',
            'target_date',
            'total_price',
        ]
Beispiel #12
0
class StockItemSerializer(InvenTreeModelSerializer):
    """ Serializer for a StockItem:

    - Includes serialization for the linked part
    - Includes serialization for the item location
    """
    @staticmethod
    def annotate_queryset(queryset):
        """
        Add some extra annotations to the queryset,
        performing database queries as efficiently as possible.
        """

        # Annotate the queryset with the total allocated to sales orders
        queryset = queryset.annotate(
            allocated=Coalesce(
                SubquerySum('sales_order_allocations__quantity'), Decimal(0)) +
            Coalesce(SubquerySum('allocations__quantity'), Decimal(0)))

        # Annotate the queryset with the number of tracking items
        queryset = queryset.annotate(
            tracking_items=SubqueryCount('tracking_info'))

        # Add flag to indicate if the StockItem has expired
        queryset = queryset.annotate(
            expired=Case(When(
                StockItem.EXPIRED_FILTER,
                then=Value(True, output_field=BooleanField()),
            ),
                         default=Value(False, output_field=BooleanField())))

        # Add flag to indicate if the StockItem is stale
        stale_days = common.models.InvenTreeSetting.get_setting(
            'STOCK_STALE_DAYS')
        stale_date = datetime.now().date() + timedelta(days=stale_days)
        stale_filter = StockItem.IN_STOCK_FILTER & ~Q(expiry_date=None) & Q(
            expiry_date__lt=stale_date)

        queryset = queryset.annotate(stale=Case(
            When(
                stale_filter,
                then=Value(True, output_field=BooleanField()),
            ),
            default=Value(False, output_field=BooleanField()),
        ))

        return queryset

    status_text = serializers.CharField(source='get_status_display',
                                        read_only=True)

    supplier_part_detail = SupplierPartSerializer(source='supplier_part',
                                                  many=False,
                                                  read_only=True)

    part_detail = PartBriefSerializer(source='part',
                                      many=False,
                                      read_only=True)

    location_detail = LocationBriefSerializer(source='location',
                                              many=False,
                                              read_only=True)

    tracking_items = serializers.IntegerField(source='tracking_info_count',
                                              read_only=True,
                                              required=False)

    quantity = serializers.FloatField()

    allocated = serializers.FloatField(source='allocation_count',
                                       required=False)

    expired = serializers.BooleanField(required=False, read_only=True)

    stale = serializers.BooleanField(required=False, read_only=True)

    serial = serializers.CharField(required=False)

    required_tests = serializers.IntegerField(source='required_test_count',
                                              read_only=True,
                                              required=False)

    purchase_price = InvenTreeMoneySerializer(label=_('Purchase Price'),
                                              max_digits=19,
                                              decimal_places=4,
                                              allow_null=True)

    purchase_price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        default=currency_code_default,
        label=_('Currency'),
    )

    purchase_price_string = serializers.SerializerMethodField()

    def get_purchase_price_string(self, obj):

        return str(obj.purchase_price) if obj.purchase_price else '-'

    purchase_order_reference = serializers.CharField(
        source='purchase_order.reference', read_only=True)

    sales_order_reference = serializers.CharField(
        source='sales_order.reference', read_only=True)

    def __init__(self, *args, **kwargs):

        part_detail = kwargs.pop('part_detail', False)
        location_detail = kwargs.pop('location_detail', False)
        supplier_part_detail = kwargs.pop('supplier_part_detail', False)
        test_detail = kwargs.pop('test_detail', False)

        super(StockItemSerializer, self).__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if location_detail is not True:
            self.fields.pop('location_detail')

        if supplier_part_detail is not True:
            self.fields.pop('supplier_part_detail')

        if test_detail is not True:
            self.fields.pop('required_tests')

    class Meta:
        model = StockItem
        fields = [
            'allocated',
            'batch',
            'belongs_to',
            'build',
            'customer',
            'expired',
            'expiry_date',
            'in_stock',
            'is_building',
            'link',
            'location',
            'location_detail',
            'notes',
            'packaging',
            'part',
            'part_detail',
            'purchase_order',
            'purchase_order_reference',
            'pk',
            'quantity',
            'required_tests',
            'sales_order',
            'sales_order_reference',
            'serial',
            'stale',
            'status',
            'status_text',
            'stocktake_date',
            'supplier_part',
            'supplier_part_detail',
            'tracking_items',
            'uid',
            'updated',
            'purchase_price',
            'purchase_price_currency',
            'purchase_price_string',
        ]
        """
        These fields are read-only in this context.
        They can be updated by accessing the appropriate API endpoints
        """
        read_only_fields = [
            'allocated', 'stocktake_date', 'stocktake_user', 'updated',
            'in_stock'
        ]
Beispiel #13
0
class SalesOrderLineItemSerializer(InvenTreeModelSerializer):
    """Serializer for a SalesOrderLineItem object."""

    @staticmethod
    def annotate_queryset(queryset):
        """Add some extra annotations to this queryset:

        - "overdue" status (boolean field)
        - "available_quantity"
        """

        queryset = queryset.annotate(
            overdue=Case(
                When(
                    Q(order__status__in=SalesOrderStatus.OPEN) & order.models.SalesOrderLineItem.OVERDUE_FILTER, then=Value(True, output_field=BooleanField()),
                ),
                default=Value(False, output_field=BooleanField()),
            )
        )

        # Annotate each line with the available stock quantity
        # To do this, we need to look at the total stock and any allocations
        queryset = queryset.alias(
            total_stock=part.filters.annotate_total_stock(reference='part__'),
            allocated_to_sales_orders=part.filters.annotate_sales_order_allocations(reference='part__'),
            allocated_to_build_orders=part.filters.annotate_build_order_allocations(reference='part__'),
        )

        queryset = queryset.annotate(
            available_stock=ExpressionWrapper(
                F('total_stock') - F('allocated_to_sales_orders') - F('allocated_to_build_orders'),
                output_field=models.DecimalField()
            )
        )

        return queryset

    def __init__(self, *args, **kwargs):
        """Initializion routine for the serializer:

        - Add extra related serializer information if required
        """
        part_detail = kwargs.pop('part_detail', False)
        order_detail = kwargs.pop('order_detail', False)
        allocations = kwargs.pop('allocations', False)

        super().__init__(*args, **kwargs)

        if part_detail is not True:
            self.fields.pop('part_detail')

        if order_detail is not True:
            self.fields.pop('order_detail')

        if allocations is not True:
            self.fields.pop('allocations')

    order_detail = SalesOrderSerializer(source='order', many=False, read_only=True)
    part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
    allocations = SalesOrderAllocationSerializer(many=True, read_only=True, location_detail=True)

    # Annotated fields
    overdue = serializers.BooleanField(required=False, read_only=True)
    available_stock = serializers.FloatField(read_only=True)

    quantity = InvenTreeDecimalField()

    allocated = serializers.FloatField(source='allocated_quantity', read_only=True)

    shipped = InvenTreeDecimalField(read_only=True)

    sale_price = InvenTreeMoneySerializer(
        allow_null=True
    )

    sale_price_string = serializers.CharField(source='sale_price', read_only=True)

    sale_price_currency = serializers.ChoiceField(
        choices=currency_code_mappings(),
        help_text=_('Sale price currency'),
    )

    class Meta:
        """Metaclass options."""

        model = order.models.SalesOrderLineItem

        fields = [
            'pk',
            'allocated',
            'allocations',
            'available_stock',
            'quantity',
            'reference',
            'notes',
            'order',
            'order_detail',
            'overdue',
            'part',
            'part_detail',
            'sale_price',
            'sale_price_currency',
            'sale_price_string',
            'shipped',
            'target_date',
        ]