Esempio n. 1
0
class POLineItemSerializer(InvenTreeModelSerializer):
    def __init__(self, *args, **kwargs):

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

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

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

    quantity = serializers.FloatField()
    received = serializers.FloatField()

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

    class Meta:
        model = PurchaseOrderLineItem

        fields = [
            'pk',
            'quantity',
            'reference',
            'notes',
            'order',
            'part',
            'part_detail',
            'supplier_part_detail',
            'received',
        ]
Esempio n. 2
0
class SupplierPartSerializer(InvenTreeModelSerializer):
    """ Serializer for SupplierPart object """

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

    supplier_detail = CompanyBriefSerializer(source='supplier',
                                             many=False,
                                             read_only=True)

    manufacturer_detail = CompanyBriefSerializer(source='manufacturer',
                                                 many=False,
                                                 read_only=True)

    pretty_name = serializers.CharField(read_only=True)

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

        part_detail = kwargs.pop('part_detail', False)
        supplier_detail = kwargs.pop('supplier_detail', False)
        manufacturer_detail = kwargs.pop('manufacturer_detail', False)
        prettify = kwargs.pop('pretty', False)

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

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

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

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

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

    supplier = serializers.PrimaryKeyRelatedField(
        queryset=Company.objects.filter(is_supplier=True))

    manufacturer = serializers.PrimaryKeyRelatedField(
        queryset=Company.objects.filter(is_manufacturer=True))

    class Meta:
        model = SupplierPart
        fields = [
            'pk',
            'part',
            'part_detail',
            'pretty_name',
            'supplier',
            'supplier_detail',
            'SKU',
            'manufacturer',
            'manufacturer_detail',
            'description',
            'MPN',
            'link',
        ]
Esempio n. 3
0
class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
    """
    Serializer for the SalesOrderAllocation model.
    This includes some fields from the related model objects.
    """

    part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
    order = serializers.PrimaryKeyRelatedField(source='line.order', many=False, read_only=True)
    serial = serializers.CharField(source='get_serial', read_only=True)
    quantity = serializers.FloatField(read_only=False)
    location = serializers.PrimaryKeyRelatedField(source='item.location', many=False, read_only=True)

    # Extra detail fields
    order_detail = SalesOrderSerializer(source='line.order', many=False, read_only=True)
    part_detail = PartBriefSerializer(source='item.part', many=False, read_only=True)
    item_detail = stock.serializers.StockItemSerializer(source='item', many=False, read_only=True)
    location_detail = stock.serializers.LocationSerializer(source='item.location', many=False, read_only=True)

    shipment_date = serializers.DateField(source='shipment.shipment_date', read_only=True)

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

        order_detail = kwargs.pop('order_detail', False)
        part_detail = kwargs.pop('part_detail', True)
        item_detail = kwargs.pop('item_detail', False)
        location_detail = kwargs.pop('location_detail', False)

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

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

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

        if not item_detail:
            self.fields.pop('item_detail')

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

    class Meta:
        model = order.models.SalesOrderAllocation

        fields = [
            'pk',
            'line',
            'serial',
            'quantity',
            'location',
            'location_detail',
            'item',
            'item_detail',
            'order',
            'order_detail',
            'part',
            'part_detail',
            'shipment',
            'shipment_date',
        ]
Esempio n. 4
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',
        ]
Esempio n. 5
0
class StockItemSerializer(serializers.ModelSerializer):
    """ Serializer for a StockItem:

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

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

    part_name = serializers.CharField(source='get_part_name', read_only=True)

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

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

        part_detail = kwargs.pop('part_detail', False)
        location_detail = kwargs.pop('location_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')

    class Meta:
        model = StockItem
        fields = [
            'pk',
            'url',
            'part',
            'part_name',
            'part_detail',
            'supplier_part',
            'location',
            'location_detail',
            'in_stock',
            'quantity',
            'serial',
            'batch',
            'status',
            'status_text',
            'notes',
        ]
        """ These fields are read-only in this context.
        They can be updated by accessing the appropriate API endpoints
        """
        read_only_fields = [
            'stocktake_date', 'stocktake_user', 'updated', 'quantity',
            'in_stock'
        ]
Esempio n. 6
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)

    # TODO: Once https://github.com/inventree/InvenTree/issues/1687 is fixed, remove default values
    quantity = serializers.FloatField(default=1)

    allocated = serializers.FloatField(source='allocated_quantity',
                                       read_only=True)
    fulfilled = serializers.FloatField(source='fulfilled_quantity',
                                       read_only=True)
    sale_price_string = serializers.CharField(source='sale_price',
                                              read_only=True)

    class Meta:
        model = SalesOrderLineItem

        fields = [
            'pk',
            'allocated',
            'allocations',
            'quantity',
            'fulfilled',
            'reference',
            'notes',
            'order',
            'order_detail',
            'part',
            'part_detail',
            'sale_price',
            'sale_price_currency',
            'sale_price_string',
        ]
Esempio n. 7
0
class SupplierPartSerializer(InvenTreeModelSerializer):
    """ Serializer for SupplierPart object """

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

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

    supplier_detail = CompanyBriefSerializer(source='supplier',
                                             many=False,
                                             read_only=True)
    manufacturer_detail = CompanyBriefSerializer(source='manufacturer',
                                                 many=False,
                                                 read_only=True)

    pricing = serializers.CharField(source='unit_pricing', read_only=True)

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

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

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

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

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

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

    class Meta:
        model = SupplierPart
        fields = [
            'pk',
            'url',
            'part',
            'part_detail',
            'supplier',
            'supplier_detail',
            'SKU',
            'manufacturer',
            'manufacturer_detail',
            'description',
            'MPN',
            'link',
            'pricing',
        ]
Esempio n. 8
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)

    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

        fields = [
            'pk',
            'allocated',
            'allocations',
            'quantity',
            'fulfilled',
            'reference',
            'notes',
            'order',
            'order_detail',
            'part',
            'part_detail',
        ]
Esempio n. 9
0
class BuildSerializer(InvenTreeModelSerializer):
    """ Serializes a Build object """

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

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

    quantity = serializers.FloatField()

    def __init__(self, *args, **kwargs):
        part_detail = kwargs.pop('part_detail', False)

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

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

    class Meta:
        model = Build
        fields = [
            'pk',
            'url',
            'title',
            'creation_date',
            'completed',
            'completion_date',
            'part',
            'part_detail',
            'reference',
            'sales_order',
            'quantity',
            'status',
            'status_text',
            'notes',
            'link',
        ]

        read_only_fields = [
            'completed',
            'creation_date',
            'completion_data',
            'status',
            'status_text',
        ]
Esempio n. 10
0
class POLineItemSerializer(InvenTreeModelSerializer):
    def __init__(self, *args, **kwargs):

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

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

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

    # TODO: Once https://github.com/inventree/InvenTree/issues/1687 is fixed, remove default values
    quantity = serializers.FloatField(default=1)
    received = serializers.FloatField(default=0)

    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_string = serializers.CharField(source='purchase_price',
                                                  read_only=True)

    destination = LocationBriefSerializer(source='get_destination',
                                          read_only=True)

    class Meta:
        model = PurchaseOrderLineItem

        fields = [
            'pk',
            'quantity',
            'reference',
            'notes',
            'order',
            'part',
            'part_detail',
            'supplier_part_detail',
            'received',
            'purchase_price',
            'purchase_price_currency',
            'purchase_price_string',
            'destination',
        ]
Esempio n. 11
0
class ManufacturerPartSerializer(InvenTreeModelSerializer):
    """Serializer for ManufacturerPart object."""

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

    manufacturer_detail = CompanyBriefSerializer(source='manufacturer', many=False, read_only=True)

    pretty_name = serializers.CharField(read_only=True)

    def __init__(self, *args, **kwargs):
        """Initialize this serializer with extra detail fields as required"""
        part_detail = kwargs.pop('part_detail', True)
        manufacturer_detail = kwargs.pop('manufacturer_detail', True)
        prettify = kwargs.pop('pretty', False)

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

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

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

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

    manufacturer = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_manufacturer=True))

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

        model = ManufacturerPart
        fields = [
            'pk',
            'part',
            'part_detail',
            'pretty_name',
            'manufacturer',
            'manufacturer_detail',
            'description',
            'MPN',
            'link',
        ]
Esempio n. 12
0
class StockItemSerializer(serializers.ModelSerializer):
    """ Serializer for a StockItem
    """
    url = serializers.CharField(source='get_absolute_url', read_only=True)

    part = PartBriefSerializer(many=False, read_only=True)
    location = LocationBriefSerializer(many=False, read_only=True)

    class Meta:
        model = StockItem
        fields = [
            'pk',
            'uuid',
            'url',
            'part',
            'supplier_part',
            'location',
            'in_stock',
            #'belongs_to',
            #'customer',
            'quantity',
            'serial',
            'batch',
            'status',
            'notes',
            #'updated',
            #'stocktake_date',
            #'stocktake_user',
            #'review_needed',
        ]
        """ These fields are read-only in this context.
        They can be updated by accessing the appropriate API endpoints
        """
        read_only_fields = [
            'stocktake_date',
            'stocktake_user',
            'updated',
            #'quantity',
        ]
Esempio n. 13
0
class StockItemSerializer(serializers.ModelSerializer):
    """ Serializer for a StockItem:

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

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

    part = PartBriefSerializer(many=False, read_only=True)
    location = LocationBriefSerializer(many=False, read_only=True)
    status_text = serializers.CharField(source='get_status_display',
                                        read_only=True)

    class Meta:
        model = StockItem
        fields = [
            'pk',
            'uuid',
            'url',
            'part',
            'supplier_part',
            'location',
            'in_stock',
            'quantity',
            'serial',
            'batch',
            'status',
            'status_text',
            'notes',
        ]
        """ These fields are read-only in this context.
        They can be updated by accessing the appropriate API endpoints
        """
        read_only_fields = [
            'stocktake_date', 'stocktake_user', 'updated', 'quantity',
            'in_stock'
        ]
Esempio n. 14
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',
        ]
Esempio n. 15
0
class StockItemSerializer(InvenTreeModelSerializer):
    """ Serializer for a StockItem:

    - Includes serialization for the linked part
    - Includes serialization for the item location
    """
    @staticmethod
    def prefetch_queryset(queryset):
        """
        Prefetch related database tables,
        to reduce database hits.
        """

        return queryset.prefetch_related(
            'belongs_to',
            'build',
            'customer',
            'sales_order',
            'supplier_part',
            'supplier_part__supplier',
            'supplier_part__manufacturer_part__manufacturer',
            'allocations',
            'sales_order_allocations',
            'location',
            'part',
            'tracking_info',
        )

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

    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',
            'pk',
            'quantity',
            'required_tests',
            'sales_order',
            'serial',
            'stale',
            'status',
            'status_text',
            'stocktake_date',
            'supplier_part',
            'supplier_part_detail',
            'tracking_items',
            'uid',
            'updated',
        ]
        """ 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'
        ]
Esempio n. 16
0
class StockItemSerializer(InvenTreeModelSerializer):
    """ Serializer for a StockItem:

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

    @staticmethod
    def prefetch_queryset(queryset):
        """
        Prefetch related database tables,
        to reduce database hits.
        """

        return queryset.prefetch_related(
            'belongs_to',
            'build',
            'build_order',
            'sales_order',
            'supplier_part',
            'supplier_part__supplier',
            'supplier_part__manufacturer',
            'allocations',
            'sales_order_allocations',
            'location',
            'part',
            'tracking_info',
        )

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

        queryset = queryset.annotate(
            allocated=Coalesce(
                Sum('sales_order_allocations__quantity', distinct=True), 0) + Coalesce(
                Sum('allocations__quantity', distinct=True), 0),
            tracking_items=Count('tracking_info'),
        )

        return queryset

    status_text = serializers.CharField(source='get_status_display', read_only=True)
    
    part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
    location_detail = LocationBriefSerializer(source='location', many=False, read_only=True)
    supplier_part_detail = SupplierPartSerializer(source='supplier_part', many=False, read_only=True)

    tracking_items = serializers.IntegerField()

    quantity = serializers.FloatField()
    allocated = serializers.FloatField()

    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)

        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:
        model = StockItem
        fields = [
            'allocated',
            'batch',
            'build_order',
            'belongs_to',
            'in_stock',
            'link',
            'location',
            'location_detail',
            'notes',
            'part',
            'part_detail',
            'pk',
            'quantity',
            'sales_order',
            'serial',
            'supplier_part',
            'supplier_part_detail',
            'status',
            'status_text',
            'tracking_items',
            'uid',
        ]

        """ 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'
        ]
Esempio n. 17
0
class SupplierPartSerializer(InvenTreeModelSerializer):
    """ Serializer for SupplierPart object """

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

    supplier_detail = CompanyBriefSerializer(source='supplier',
                                             many=False,
                                             read_only=True)

    manufacturer_detail = CompanyBriefSerializer(
        source='manufacturer_part.manufacturer', many=False, read_only=True)

    pretty_name = serializers.CharField(read_only=True)

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

        part_detail = kwargs.pop('part_detail', True)
        supplier_detail = kwargs.pop('supplier_detail', True)
        manufacturer_detail = kwargs.pop('manufacturer_detail', True)

        prettify = kwargs.pop('pretty', False)

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

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

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

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

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

    supplier = serializers.PrimaryKeyRelatedField(
        queryset=Company.objects.filter(is_supplier=True))

    manufacturer = serializers.CharField(read_only=True)

    MPN = serializers.CharField(read_only=True)

    manufacturer_part_detail = ManufacturerPartSerializer(
        source='manufacturer_part', read_only=True)

    class Meta:
        model = SupplierPart
        fields = [
            'description',
            'link',
            'manufacturer',
            'manufacturer_detail',
            'manufacturer_part',
            'manufacturer_part_detail',
            'MPN',
            'note',
            'pk',
            'packaging',
            'part',
            'part_detail',
            'pretty_name',
            'SKU',
            'supplier',
            'supplier_detail',
        ]

    def create(self, validated_data):
        """ Extract manufacturer data and process ManufacturerPart """

        # Create SupplierPart
        supplier_part = super().create(validated_data)

        # Get ManufacturerPart raw data (unvalidated)
        manufacturer = self.initial_data.get('manufacturer', None)
        MPN = self.initial_data.get('MPN', None)

        if manufacturer and MPN:
            kwargs = {
                'manufacturer': manufacturer,
                'MPN': MPN,
            }
            supplier_part.save(**kwargs)

        return supplier_part
Esempio n. 18
0
    def list(self, request, *args, **kwargs):

        queryset = self.filter_queryset(self.get_queryset())

        serializer = self.get_serializer(queryset, many=True)

        data = serializer.data

        # Attempt to add extra context information to the historical data
        for item in data:
            deltas = item['deltas']

            if not deltas:
                deltas = {}

            # Add part detail
            if 'part' in deltas:
                try:
                    part = Part.objects.get(pk=deltas['part'])
                    serializer = PartBriefSerializer(part)
                    deltas['part_detail'] = serializer.data
                except:
                    pass

            # Add location detail
            if 'location' in deltas:
                try:
                    location = StockLocation.objects.get(pk=deltas['location'])
                    serializer = StockSerializers.LocationSerializer(location)
                    deltas['location_detail'] = serializer.data
                except:
                    pass

            # Add stockitem detail
            if 'stockitem' in deltas:
                try:
                    stockitem = StockItem.objects.get(pk=deltas['stockitem'])
                    serializer = StockSerializers.StockItemSerializer(
                        stockitem)
                    deltas['stockitem_detail'] = serializer.data
                except:
                    pass

            # Add customer detail
            if 'customer' in deltas:
                try:
                    customer = Company.objects.get(pk=deltas['customer'])
                    serializer = CompanySerializer(customer)
                    deltas['customer_detail'] = serializer.data
                except:
                    pass

            # Add purchaseorder detail
            if 'purchaseorder' in deltas:
                try:
                    order = PurchaseOrder.objects.get(
                        pk=deltas['purchaseorder'])
                    serializer = PurchaseOrderSerializer(order)
                    deltas['purchaseorder_detail'] = serializer.data
                except:
                    pass

        if request.is_ajax():
            return JsonResponse(data, safe=False)
        else:
            return Response(data)
Esempio n. 19
0
    def list(self, request, *args, **kwargs):
        """
        Override the 'list' method, as the StockLocation objects
        are very expensive to serialize.
        
        So, we fetch and serialize the required StockLocation objects only as required.
        """

        queryset = self.filter_queryset(self.get_queryset())

        serializer = self.get_serializer(queryset, many=True)

        data = serializer.data

        # Keep track of which related models we need to query
        location_ids = set()
        part_ids = set()
        supplier_part_ids = set()

        # Iterate through each StockItem and grab some data
        for item in data:
            loc = item['location']
            if loc:
                location_ids.add(loc)

            part = item['part']
            if part:
                part_ids.add(part)

            sp = item['supplier_part']

            if sp:
                supplier_part_ids.add(sp)

        # Do we wish to include Part detail?
        if str2bool(request.query_params.get('part_detail', False)):

            # Fetch only the required Part objects from the database
            parts = Part.objects.filter(pk__in=part_ids).prefetch_related(
                'category',
            )

            part_map = {}

            for part in parts:
                part_map[part.pk] = PartBriefSerializer(part).data

            # Now update each StockItem with the related Part data
            for stock_item in data:
                part_id = stock_item['part']
                stock_item['part_detail'] = part_map.get(part_id, None)

        # Do we wish to include SupplierPart detail?
        if str2bool(request.query_params.get('supplier_part_detail', False)):

            supplier_parts = SupplierPart.objects.filter(pk__in=supplier_part_ids)

            supplier_part_map = {}

            for part in supplier_parts:
                supplier_part_map[part.pk] = SupplierPartSerializer(part).data

            for stock_item in data:
                part_id = stock_item['supplier_part']
                stock_item['supplier_part_detail'] = supplier_part_map.get(part_id, None)

        # Do we wish to include StockLocation detail?
        if str2bool(request.query_params.get('location_detail', False)):

            # Fetch only the required StockLocation objects from the database
            locations = StockLocation.objects.filter(pk__in=location_ids).prefetch_related(
                'parent',
                'children',
            )

            location_map = {}

            # Serialize each StockLocation object
            for location in locations:
                location_map[location.pk] = LocationBriefSerializer(location).data

            # Now update each StockItem with the related StockLocation data
            for stock_item in data:
                loc_id = stock_item['location']
                stock_item['location_detail'] = location_map.get(loc_id, None)

        """
        Determine the response type based on the request.
        a) For HTTP requests (e.g. via the browseable API) return a DRF response
        b) For AJAX requests, simply return a JSON rendered response.

        Note: b) is about 100x quicker than a), because the DRF framework adds a lot of cruft
        """

        if request.is_ajax():
            return JsonResponse(data, safe=False)
        else:
            return Response(data)
Esempio n. 20
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',
        ]
Esempio n. 21
0
class SupplierPartSerializer(InvenTreeModelSerializer):
    """Serializer for SupplierPart object."""

    # Annotated field showing total in-stock quantity
    in_stock = serializers.FloatField(read_only=True)

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

    supplier_detail = CompanyBriefSerializer(source='supplier', many=False, read_only=True)

    manufacturer_detail = CompanyBriefSerializer(source='manufacturer_part.manufacturer', many=False, read_only=True)

    pretty_name = serializers.CharField(read_only=True)

    def __init__(self, *args, **kwargs):
        """Initialize this serializer with extra detail fields as required"""

        # Check if 'available' quantity was supplied
        self.has_available_quantity = 'available' in kwargs.get('data', {})

        part_detail = kwargs.pop('part_detail', True)
        supplier_detail = kwargs.pop('supplier_detail', True)
        manufacturer_detail = kwargs.pop('manufacturer_detail', True)

        prettify = kwargs.pop('pretty', False)

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

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

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

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

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

    supplier = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_supplier=True))

    manufacturer = serializers.CharField(read_only=True)

    MPN = serializers.CharField(read_only=True)

    manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', read_only=True)

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

        model = SupplierPart
        fields = [
            'available',
            'availability_updated',
            'description',
            'in_stock',
            'link',
            'manufacturer',
            'manufacturer_detail',
            'manufacturer_part',
            'manufacturer_part_detail',
            'MPN',
            'note',
            'pk',
            'packaging',
            'part',
            'part_detail',
            'pretty_name',
            'SKU',
            'supplier',
            'supplier_detail',
        ]

        read_only_fields = [
            'availability_updated',
        ]

    @staticmethod
    def annotate_queryset(queryset):
        """Annotate the SupplierPart queryset with extra fields:

        Fields:
            in_stock: Current stock quantity for each SupplierPart
        """

        queryset = queryset.annotate(
            in_stock=part.filters.annotate_total_stock()
        )

        return queryset

    def update(self, supplier_part, data):
        """Custom update functionality for the serializer"""

        available = data.pop('available', None)

        response = super().update(supplier_part, data)

        if available is not None and self.has_available_quantity:
            supplier_part.update_available_quantity(available)

        return response

    def create(self, validated_data):
        """Extract manufacturer data and process ManufacturerPart."""

        # Extract 'available' quantity from the serializer
        available = validated_data.pop('available', None)

        # Create SupplierPart
        supplier_part = super().create(validated_data)

        if available is not None and self.has_available_quantity:
            supplier_part.update_available_quantity(available)

        # Get ManufacturerPart raw data (unvalidated)
        manufacturer = self.initial_data.get('manufacturer', None)
        MPN = self.initial_data.get('MPN', None)

        if manufacturer and MPN:
            kwargs = {
                'manufacturer': manufacturer,
                'MPN': MPN,
            }
            supplier_part.save(**kwargs)

        return supplier_part
Esempio n. 22
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',
        ]
Esempio n. 23
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',
        ]
Esempio n. 24
0
class BuildSerializer(ReferenceIndexingSerializerMixin,
                      InvenTreeModelSerializer):
    """
    Serializes a Build object
    """

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

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

    quantity = InvenTreeDecimalField()

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

    issued_by_detail = UserSerializerBrief(source='issued_by', read_only=True)

    responsible_detail = OwnerSerializer(source='responsible', read_only=True)

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

        The following annoted fields are added:

        - overdue: True if the build is outstanding *and* the completion date has past

        """

        # Annotate a boolean 'overdue' flag

        queryset = queryset.annotate(
            overdue=Case(When(
                Build.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', True)

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

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

    class Meta:
        model = Build
        fields = [
            'pk',
            'url',
            'title',
            'batch',
            'creation_date',
            'completed',
            'completion_date',
            'destination',
            'parent',
            'part',
            'part_detail',
            'overdue',
            'reference',
            'sales_order',
            'quantity',
            'status',
            'status_text',
            'target_date',
            'take_from',
            'notes',
            'link',
            'issued_by',
            'issued_by_detail',
            'responsible',
            'responsible_detail',
        ]

        read_only_fields = [
            'completed',
            'creation_date',
            'completion_data',
            'status',
            'status_text',
        ]
Esempio n. 25
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',
        ]