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 def get_groups(self, cart): return StoreCartGroupSerializer(self._build_groups(cart), many=True, context=self.context).data 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