141 lines
5.9 KiB
Python
141 lines
5.9 KiB
Python
from drf_spectacular.utils import extend_schema_field
|
|
from rest_framework import serializers
|
|
|
|
from apps.catalog.models import Product
|
|
from apps.catalog.serializers import ProductListSerializer
|
|
from apps.locations.models import Address
|
|
from apps.stores.serializers import StoreListSerializer
|
|
|
|
from .models import CartItem, Notification, Order, OrderGroup, OrderItem, OrderStatusLog
|
|
|
|
|
|
class CartItemSerializer(serializers.ModelSerializer):
|
|
product = ProductListSerializer(read_only=True)
|
|
line_total = serializers.IntegerField(read_only=True)
|
|
|
|
class Meta:
|
|
model = CartItem
|
|
fields = ('uuid', 'product', 'quantity', 'line_total')
|
|
|
|
|
|
class CartItemWriteSerializer(serializers.Serializer):
|
|
product_uuid = serializers.PrimaryKeyRelatedField(
|
|
source='product', queryset=Product.objects.filter(is_active=True),
|
|
)
|
|
quantity = serializers.IntegerField(min_value=1, default=1)
|
|
|
|
|
|
class StoreCartGroupSerializer(serializers.Serializer):
|
|
"""One store's slice of a multi-store cart (see the C07 mock)."""
|
|
|
|
store = StoreListSerializer(read_only=True)
|
|
items = CartItemSerializer(many=True, read_only=True)
|
|
items_subtotal = serializers.IntegerField(read_only=True)
|
|
delivery_fee = serializers.IntegerField(read_only=True)
|
|
total = serializers.IntegerField(read_only=True)
|
|
meets_minimum_order = serializers.BooleanField(read_only=True)
|
|
|
|
|
|
class CartSerializer(serializers.Serializer):
|
|
"""Groups the authenticated user's cart items by store for the multi-store-cart UI."""
|
|
|
|
groups = serializers.SerializerMethodField()
|
|
grand_total = serializers.SerializerMethodField()
|
|
|
|
def _build_groups(self, cart):
|
|
if not hasattr(self, '_groups_cache'):
|
|
from .services import build_cart_groups
|
|
self._groups_cache = build_cart_groups(cart)
|
|
return self._groups_cache
|
|
|
|
@extend_schema_field(StoreCartGroupSerializer(many=True))
|
|
def get_groups(self, cart):
|
|
return StoreCartGroupSerializer(self._build_groups(cart), many=True, context=self.context).data
|
|
|
|
@extend_schema_field(serializers.IntegerField())
|
|
def get_grand_total(self, cart):
|
|
return sum(group['total'] for group in self._build_groups(cart))
|
|
|
|
|
|
class OrderItemSerializer(serializers.ModelSerializer):
|
|
line_total = serializers.IntegerField(read_only=True)
|
|
|
|
class Meta:
|
|
model = OrderItem
|
|
fields = ('uuid', 'product', 'product_name_snapshot', 'unit_price_snapshot', 'quantity', 'line_total')
|
|
|
|
|
|
class OrderStatusLogSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = OrderStatusLog
|
|
fields = ('from_status', 'to_status', 'note', 'created_at')
|
|
|
|
|
|
class OrderSerializer(serializers.ModelSerializer):
|
|
store = StoreListSerializer(read_only=True)
|
|
items = OrderItemSerializer(many=True, read_only=True)
|
|
status_logs = OrderStatusLogSerializer(many=True, read_only=True)
|
|
total_amount = serializers.IntegerField(read_only=True)
|
|
is_cancellable = serializers.SerializerMethodField()
|
|
|
|
class Meta:
|
|
model = Order
|
|
fields = (
|
|
'uuid', 'store', 'status', 'items', 'items_subtotal', 'delivery_fee',
|
|
'commission_amount', 'seller_payout_amount', 'total_amount', 'cancel_reason',
|
|
'courier_name', 'courier_phone', 'placed_at', 'delivered_at', 'status_logs',
|
|
'is_cancellable',
|
|
)
|
|
|
|
def get_is_cancellable(self, order):
|
|
return order.status in Order.CANCELLABLE_STATUSES
|
|
|
|
|
|
class OrderGroupSerializer(serializers.ModelSerializer):
|
|
orders = OrderSerializer(many=True, read_only=True)
|
|
|
|
class Meta:
|
|
model = OrderGroup
|
|
fields = (
|
|
'uuid', 'recipient_name', 'recipient_phone', 'full_address',
|
|
'delivery_type', 'scheduled_at', 'payment_method', 'payment_status',
|
|
'notes', 'total_amount', 'orders', 'created_at',
|
|
)
|
|
|
|
|
|
class CheckoutSerializer(serializers.Serializer):
|
|
address_uuid = serializers.PrimaryKeyRelatedField(source='address', queryset=Address.objects.all())
|
|
delivery_type = serializers.ChoiceField(choices=OrderGroup.DeliveryType.choices, default=OrderGroup.DeliveryType.EXPRESS)
|
|
scheduled_at = serializers.DateTimeField(required=False, allow_null=True, default=None)
|
|
payment_method = serializers.ChoiceField(choices=OrderGroup.PaymentMethod.choices)
|
|
notes = serializers.CharField(required=False, allow_blank=True, default='')
|
|
store_uuids = serializers.ListField(
|
|
child=serializers.UUIDField(), required=False, allow_empty=False, default=None,
|
|
help_text='بررسی و پرداخت فقط برای فروشگاههای انتخابشده از سبد خرید. در صورت خالی بودن، کل سبد خرید نهایی میشود.',
|
|
)
|
|
|
|
def validate(self, attrs):
|
|
if attrs.get('delivery_type') == OrderGroup.DeliveryType.SCHEDULED and not attrs.get('scheduled_at'):
|
|
raise serializers.ValidationError({'scheduled_at': 'برای ارسال زمانبندی شده، زمان را مشخص کنید.'})
|
|
address = attrs['address']
|
|
request = self.context['request']
|
|
if address.user_id != request.user.pk:
|
|
raise serializers.ValidationError({'address_uuid': 'این آدرس متعلق به شما نیست.'})
|
|
return attrs
|
|
|
|
|
|
class OrderCancelSerializer(serializers.Serializer):
|
|
reason = serializers.CharField(required=False, allow_blank=True, default='')
|
|
|
|
|
|
class OrderStatusActionSerializer(serializers.Serializer):
|
|
note = serializers.CharField(required=False, allow_blank=True, default='')
|
|
courier_name = serializers.CharField(required=False, allow_blank=True, default='')
|
|
courier_phone = serializers.CharField(required=False, allow_blank=True, default='')
|
|
|
|
|
|
class NotificationSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = Notification
|
|
fields = ('uuid', 'type', 'title', 'body', 'related_order', 'is_read', 'created_at')
|
|
read_only_fields = fields
|