class AddToCartSerializer(serializers.Serializer):
    """
    Serialize fields used in the "Add to Cart" dialog box.
    """
    quantity = serializers.IntegerField(default=1, min_value=1)
    unit_price = MoneyField(read_only=True)
    subtotal = MoneyField(read_only=True)
    product = serializers.IntegerField(read_only=True, help_text="The product's primary key")
    extra = serializers.DictField(read_only=True)

    def __init__(self, instance=None, data=empty, **kwargs):
        context = kwargs.get('context', {})
        if 'product' in context:
            instance = self.get_instance(context, data, kwargs)
            if data == empty:
                quantity = self.fields['quantity'].default
            else:
                quantity = self.fields['quantity'].to_internal_value(data['quantity'])
            instance.setdefault('quantity', quantity)
            instance.setdefault('subtotal', instance['quantity'] * instance['unit_price'])
            super(AddToCartSerializer, self).__init__(instance, data, context=context)
        else:
            super(AddToCartSerializer, self).__init__(instance, data, **kwargs)

    def get_instance(self, context, data, extra_args):
        product = context['product']
        return {
            'product': product.id,
            'unit_price': product.get_price(context['request']),
        }
Exemple #2
0
class BaseItemSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(lookup_field='pk', view_name='shop:cart-detail')
    unit_price = MoneyField()
    line_total = MoneyField()
    summary = serializers.SerializerMethodField(
        help_text="Sub-serializer for fields to be shown in the product's summary.")
    extra_rows = ExtraCartRowList(read_only=True)

    class Meta:
        model = CartItemModel

    def create(self, validated_data):
        assert 'cart' in validated_data
        cart_item, _ = CartItemModel.objects.get_or_create(**validated_data)
        cart_item.save()
        return cart_item

    def to_representation(self, cart_item):
        cart_item.update(self.context['request'])
        representation = super(BaseItemSerializer, self).to_representation(cart_item)
        return representation

    def validate_product(self, product):
        if not product.active:
            msg = "Product `{}` is inactive, and can not be added to the cart."
            raise serializers.ValidationError(msg.format(product))
        return product

    def get_summary(self, cart_item):
        serializer_class = app_settings.PRODUCT_SUMMARY_SERIALIZER
        serializer = serializer_class(cart_item.product, context=self.context,
                                      read_only=True, label=self.root.label)
        return serializer.data
Exemple #3
0
class OrderListSerializer(serializers.ModelSerializer):
    number = serializers.CharField(
        source='get_number',
        read_only=True,
    )

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

    status = serializers.CharField(
        source='status_name',
        read_only=True,
    )

    subtotal = MoneyField()
    total = MoneyField()

    class Meta:
        model = OrderModel
        fields = [
            'number', 'url', 'created_at', 'updated_at', 'subtotal', 'total',
            'status', 'shipping_address_text', 'billing_address_text'
        ]
Exemple #4
0
class BaseCartSerializer(serializers.ModelSerializer):
    subtotal = MoneyField()
    total = MoneyField()
    extra_rows = ExtraCartRowList(read_only=True)

    class Meta:
        model = CartModel
Exemple #5
0
class BaseOrderItemSerializer(serializers.ModelSerializer):
    line_total = MoneyField()
    unit_price = MoneyField()
    product_code = serializers.CharField()

    class Meta:
        model = OrderItemModel
Exemple #6
0
class AddToCartSerializer(serializers.Serializer):
    """
    By default, this serializer is used by the view class :class:`shop.views.catalog.AddToCartView`,
    which handles the communication from the "Add to Cart" dialog box.

    If a product has variations, which influence the fields in the "Add to Cart" dialog box, then
    this serializer shall be overridden by a customized implementation. Such a customized "*Add to
    Cart*" serializer has to be connected to the ``AddToCartView``. This usually is achieved in
    the projects ``urls.py`` by changing the catalog's routing to:
    ```
    urlpatterns = [
        ...
        url(r'^(?P<slug>[\w-]+)/add-to-cart', AddToCartView.as_view(
            serializer_class=CustomAddToCartSerializer,
        )),
        ...
    ]
    ```
    """
    quantity = serializers.IntegerField(default=1, min_value=1)
    unit_price = MoneyField(read_only=True)
    subtotal = MoneyField(read_only=True)
    product = serializers.IntegerField(read_only=True, help_text="The product's primary key")
    product_code = serializers.CharField(read_only=True, help_text="Exact product code of the cart item")
    extra = serializers.DictField(read_only=True, default={})
    is_in_cart = serializers.BooleanField(read_only=True, default=False)

    def __init__(self, instance=None, data=empty, **kwargs):
        context = kwargs.get('context', {})
        if 'product' in context:
            instance = self.get_instance(context, data, kwargs)
            if data == empty:
                quantity = self.fields['quantity'].default
            else:
                quantity = self.fields['quantity'].to_internal_value(data['quantity'])
            instance.setdefault('quantity', quantity)
            instance.setdefault('subtotal', instance['quantity'] * instance['unit_price'])
            super(AddToCartSerializer, self).__init__(instance, data, context=context)
        else:
            super(AddToCartSerializer, self).__init__(instance, data, **kwargs)

    def get_instance(self, context, data, extra_args):
        """
        Method to store the ordered products in the cart item instance.
        Remember to override this method, if the ``product_code`` is part of the
        variation rather than being part of the product itself.
        """
        product = context['product']
        try:
            cart = CartModel.objects.get_from_request(context['request'])
        except CartModel.DoesNotExist:
            cart = None
        extra = data.get('extra', {}) if data is not empty else {}
        return {
            'product': product.id,
            'product_code': product.product_code,
            'unit_price': product.get_price(context['request']),
            'is_in_cart': bool(product.is_in_cart(cart)),
            'extra': extra,
        }
Exemple #7
0
class OrderDetailSerializer(OrderListSerializer):
    items = OrderItemSerializer(many=True, read_only=True)
    amount_paid = MoneyField(read_only=True)
    outstanding_amount = MoneyField(read_only=True)
    is_partially_paid = serializers.SerializerMethodField(
        method_name='get_partially_paid',
        help_text="Returns true, if order has been partially paid")

    def get_partially_paid(self, order):
        return order.amount_paid > 0
class OrderListSerializer(serializers.ModelSerializer):
    number = serializers.CharField(source='get_number', read_only=True)
    customer = CustomerSerializer(read_only=True)
    url = serializers.URLField(source='get_absolute_url', read_only=True)
    status = serializers.CharField(source='status_name', read_only=True)
    subtotal = MoneyField()
    total = MoneyField()
    extra = serializers.DictField(read_only=True)

    class Meta:
        model = OrderModel
        exclude = ('id', 'stored_request', '_subtotal', '_total',)
Exemple #9
0
class OrderDetailSerializer(OrderListSerializer):
    items = app_settings.ORDER_ITEM_SERIALIZER(
        many=True,
        read_only=True,
    )

    extra = serializers.DictField(read_only=True)
    amount_paid = MoneyField(read_only=True)
    outstanding_amount = MoneyField(read_only=True)
    cancelable = serializers.BooleanField(read_only=True)

    is_partially_paid = serializers.SerializerMethodField(
        method_name='get_partially_paid',
        help_text="Returns true, if order has been partially paid",
    )

    annotation = serializers.CharField(
        write_only=True,
        required=False,
    )

    reorder = serializers.BooleanField(
        write_only=True,
        default=False,
    )

    cancel = serializers.BooleanField(
        write_only=True,
        default=False,
    )

    class Meta:
        model = OrderModel
        exclude = ['id', 'customer', 'stored_request', '_subtotal', '_total']
        read_only_fields = ['shipping_address_text', 'billing_address_text']  # TODO: not part of OrderBase

    def get_partially_paid(self, order):
        return order.amount_paid > 0

    def update(self, order, validated_data):
        order.extra.setdefault('addenum', [])
        if validated_data.get('annotation'):
            timestamp = timezone.now().isoformat()
            order.extra['addenum'].append((timestamp, validated_data['annotation']))
            order.save()
        if validated_data['reorder'] is True:
            cart = CartModel.objects.get_from_request(self.context['request'])
            order.readd_to_cart(cart)
        if validated_data['cancel'] is True and order.cancelable():
            order.cancel_order()
            order.save()
        return order
class BaseCartSerializer(serializers.ModelSerializer):
    subtotal = MoneyField()
    total = MoneyField()
    extra_rows = ExtraCartRowList(read_only=True)

    class Meta:
        model = CartModel
        fields = ('subtotal', 'total', 'extra_rows')

    def to_representation(self, cart):
        cart.update(self.context['request'])
        representation = super(BaseCartSerializer, self).to_representation(cart)
        return representation
class AddToCartSerializer(serializers.Serializer):
    """
    By default, this serializer is used by the view class :class:`shop.views.catalog.AddToCartView`,
    which handles the communication from the "Add to Cart" dialog box.

    This serializer shall be replaced by an alternative implementation, if product variations are used
    on the same catalog's detail view.
    """
    quantity = serializers.IntegerField(default=1, min_value=1)
    unit_price = MoneyField(read_only=True)
    subtotal = MoneyField(read_only=True)
    product = serializers.IntegerField(read_only=True,
                                       help_text="The product's primary key")
    product_code = serializers.CharField(
        read_only=True, help_text="Exact product code of the cart item")
    extra = serializers.DictField(read_only=True, default={})

    def __init__(self, instance=None, data=empty, **kwargs):
        context = kwargs.get('context', {})
        if 'product' in context:
            instance = self.get_instance(context, data, kwargs)
            if data == empty:
                quantity = self.fields['quantity'].default
            else:
                quantity = self.fields['quantity'].to_internal_value(
                    data['quantity'])
            instance.setdefault('quantity', quantity)
            instance.setdefault('subtotal',
                                instance['quantity'] * instance['unit_price'])
            super(AddToCartSerializer, self).__init__(instance,
                                                      data,
                                                      context=context)
        else:
            super(AddToCartSerializer, self).__init__(instance, data, **kwargs)

    def get_instance(self, context, data, extra_args):
        """
        Method to store the ordered products in the cart item instance.
        Remember to override this method, if the ``product_code`` is part of the
        variation rather than being part of the product itself.
        """
        product = context['product']
        extra = data.get('extra', {}) if data is not empty else {}
        return {
            'product': product.id,
            'product_code': product.product_code,
            'unit_price': product.get_price(context['request']),
            'extra': extra,
        }
Exemple #12
0
class OrderItemSerializer(serializers.ModelSerializer):
    line_total = MoneyField()
    unit_price = MoneyField()
    summary = serializers.SerializerMethodField(
        help_text="Sub-serializer for fields to be shown in the product's summary.")

    class Meta:
        model = OrderItemModel
        exclude = ('id',)

    def get_summary(self, order_item):
        label = self.context.get('render_label', 'order')
        serializer = product_summary_serializer_class(order_item.product, context=self.context,
                                                      read_only=True, label=label)
        return serializer.data
Exemple #13
0
class BaseItemSerializer(ItemModelSerializer):
    url = serializers.HyperlinkedIdentityField(lookup_field='pk', view_name='shop:cart-detail')
    unit_price = MoneyField()
    line_total = MoneyField()
    summary = serializers.SerializerMethodField(
        help_text="Sub-serializer for fields to be shown in the product's summary.")
    extra_rows = ExtraCartRowList(read_only=True)

    def validate_product(self, product):
        if not product.active:
            msg = "Product `{}` is inactive, and can not be added to the cart."
            raise serializers.ValidationError(msg.format(product))
        return product

    def get_summary(self, cart_item):
        serializer = product_summary_serializer_class(cart_item.product, context=self.context,
                                                      read_only=True, label=self.root.label)
        return serializer.data
class ExtraCartRow(serializers.Serializer):
    """
    This data structure holds extra information for each item, or for the whole cart, while
    processing the cart using their modifiers.
    """
    label = serializers.CharField(read_only=True,
        help_text="A short description of this row in a natural language.")
    amount = MoneyField(read_only=True,
        help_text="The price difference, if applied.")
Exemple #15
0
 def to_representation(self, instance):
     data = super(AddToCartSerializer, self).to_representation(instance)
     try:
         data['quantity'] = self._validated_data['quantity']
     except AttributeError:
         data['quantity'] = self.validate_quantity(data['quantity'])
     data['subtotal'] = MoneyField().to_representation(
         data['quantity'] * instance['unit_price'])
     return data
Exemple #16
0
class BaseCartSerializer(serializers.ModelSerializer):
    subtotal = MoneyField()
    total = MoneyField()
    extra_rows = ExtraCartRowList(read_only=True)

    class Meta:
        model = CartModel
        fields = ['subtotal', 'total', 'extra_rows']

    def to_representation(self, cart):
        cart.update(self.context['request'])
        representation = super(BaseCartSerializer, self).to_representation(cart)
        if self.with_items:
            items = self.represent_items(cart)
            representation.update(items=items)
        return representation

    def represent_items(self, cart):
        raise NotImplementedError("{} must implement method `represent_items()`.".format(self.__class__))
Exemple #17
0
class CartIconCaptionSerializer(serializers.ModelSerializer):
    """
    The default serializer used to render the information nearby the cart icon symbol, normally
    located on the top right of e-commerce sites.
    """
    num_items = serializers.IntegerField(read_only=True, default=0)
    total = MoneyField(default=Money())

    class Meta:
        model = CartModel
        fields = ['num_items', 'total']
class OrderDetailSerializer(OrderListSerializer):
    items = OrderItemSerializer(many=True, read_only=True)
    amount_paid = MoneyField(read_only=True)
    outstanding_amount = MoneyField(read_only=True)
    is_partially_paid = serializers.SerializerMethodField(method_name='get_partially_paid',
        help_text="Returns true, if order has been partially paid")
    annotation = serializers.CharField(write_only=True, required=False)
    reorder = serializers.BooleanField(write_only=True, default=False)

    def get_partially_paid(self, order):
        return order.amount_paid > 0

    def update(self, order, validated_data):
        order.extra.setdefault('addenum', [])
        if validated_data.get('annotation'):
            timestamp = timezone.now().isoformat()
            order.extra['addenum'].append((timestamp, validated_data['annotation']))
        if validated_data.get('reorder'):
            cart = CartModel.objects.get_from_request(self.context['request'])
            order.readd_to_cart(cart)
        order.save()
        return order
Exemple #19
0
class MoneyTestSerializer(serializers.Serializer):
    amount = MoneyField(read_only=True)
class ProductSerializer(BaseProductSerializer):
    """
    Base product serializer.
    """
    FIELDS = app_settings.PRODUCT_SERIALIZER_FIELDS

    url = serializers.SerializerMethodField(read_only=True)
    add_to_cart_url = serializers.SerializerMethodField(read_only=True)
    unit_price = MoneyField()
    tax = TaxSerializer()
    is_available = serializers.SerializerMethodField(read_only=True)
    category = CategorySerializer()
    brand = BrandSerializer()
    manufacturer = ManufacturerSerializer()
    modifiers = ModifierSerializer(source='get_modifiers', many=True)
    flags = FlagSerializer(source='get_flags', many=True)
    width = MeasureField(measure=Distance)
    height = MeasureField(measure=Distance)
    depth = MeasureField(measure=Distance)
    weight = MeasureField(measure=Mass)
    attributes = serializers.DictField(source='get_attributes', read_only=True)
    attribute_choices = serializers.DictField(source='get_attribute_choices',
                                              read_only=True)
    discount_amount = MoneyField(read_only=True)
    tax_amount = MoneyField(read_only=True)
    variants = serializers.SerializerMethodField()
    variations = serializers.ListField(source='get_variations', read_only=True)
    attachments = serializers.SerializerMethodField()
    relations = RelationSerializer(source='get_relations', many=True)
    reviews = serializers.SerializerMethodField()

    class Meta:
        model = Product
        fields = [
            'id',
            'name',
            'slug',
            'caption',
            'code',
            'kind',
            'url',
            'add_to_cart_url',
            'price',
            'is_available',
            'description',
            'unit_price',
            'discount',
            'tax',
            'availability',
            'category',
            'brand',
            'manufacturer',
            'discountable',
            'modifiers',
            'flags',
            'width',
            'height',
            'depth',
            'weight',
            'available_attributes',
            'group',
            'attributes',
            'attribute_choices',
            'published',
            'quantity',
            'order',
            'active',
            'created_at',
            'updated_at',
            'is_single',
            'is_group',
            'is_variant',
            'is_discounted',
            'is_taxed',
            'discount_percent',
            'tax_percent',
            'discount_amount',
            'tax_amount',
            'variants',
            'variations',
            'attachments',
            'relations',
            'reviews',
        ]

    def get_fields(self):
        fields = super(ProductSerializer, self).get_fields()
        overriden = self.context['request'].GET.get('fields', None)
        names = [x for x in overriden.split(',') if x in self.Meta.fields
                 ] if overriden is not None else self.FIELDS
        names = list(set(names + self.get_included_fields()))
        for excluded in [x for x in fields if x not in names]:
            del fields[excluded]
        return fields

    def get_included_fields(self):
        return [
            x
            for x in self.context['request'].GET.get('include', '').split(',')
            if x in self.Meta.fields
        ]

    def get_url(self, obj):
        url = obj.get_absolute_url()
        return self.context['request'].build_absolute_uri(url) if url else None

    def get_add_to_cart_url(self, obj):
        try:
            url = reverse(
                'shopit-add-to-cart',
                args=[obj.safe_translation_getter('slug', any_language=True)])
            return self.context['request'].build_absolute_uri(url)
        except NoReverseMatch:
            return None

    def get_is_available(self, obj):
        return obj.is_available(request=self.context['request'])

    def get_variants(self, obj):
        variants = obj.get_variants()
        if variants:
            return ProductDetailSerializer(variants,
                                           context=self.context,
                                           many=True).data

    def get_attachments(self, obj):
        request = self.context['request']
        attachments = obj.get_attachments()
        for kind, items in [x for x in attachments.items() if x[1]]:
            for item in items:
                for key, value in item.items():
                    if key.startswith('url'):
                        item[key] = request.build_absolute_uri(value)
        return attachments

    def get_reviews(self, obj):
        reviews = obj.get_reviews(
            language=self.context['request'].LANGUAGE_CODE)
        if reviews:
            return ReviewSerializer(reviews, context=self.context,
                                    many=True).data
class ModifierSerializer(serializers.ModelSerializer):
    amount = MoneyField()

    class Meta:
        model = Modifier
        fields = ['id', 'name', 'code', 'amount', 'percent', 'kind', 'order']