from django.contrib.gis.geos import Point from drf_spectacular.utils import extend_schema_field from rest_framework import serializers from apps.core.media import presigned_media_url from apps.locations.models import City, Neighborhood from apps.locations.serializers import CitySerializer, NeighborhoodSerializer from .models import Store, StoreBankAccount, StoreCategory, StoreWorkingHours class StoreCategorySerializer(serializers.ModelSerializer): icon_url = serializers.SerializerMethodField() class Meta: model = StoreCategory fields = ('uuid', 'name', 'icon', 'icon_url', 'order') @extend_schema_field(serializers.URLField(allow_null=True)) def get_icon_url(self, obj): return presigned_media_url(obj.icon) class StoreWorkingHoursSerializer(serializers.ModelSerializer): class Meta: model = StoreWorkingHours fields = ('weekday', 'opens_at', 'closes_at', 'is_closed') class StoreListSerializer(serializers.ModelSerializer): category = StoreCategorySerializer(read_only=True) logo_url = serializers.SerializerMethodField() cover_image_url = serializers.SerializerMethodField() class Meta: model = Store fields = ( 'uuid', 'name', 'category', 'logo', 'logo_url', 'cover_image', 'cover_image_url', 'rating_avg', 'rating_count', 'min_order_amount', 'delivery_fee', 'free_delivery_threshold', 'is_open', ) @extend_schema_field(serializers.URLField(allow_null=True)) def get_logo_url(self, obj): return presigned_media_url(obj.logo) @extend_schema_field(serializers.URLField(allow_null=True)) def get_cover_image_url(self, obj): return presigned_media_url(obj.cover_image) class StoreDetailSerializer(StoreListSerializer): working_hours = StoreWorkingHoursSerializer(many=True, read_only=True) class Meta(StoreListSerializer.Meta): fields = StoreListSerializer.Meta.fields + ( 'description', 'address', 'phone_number', 'accepts_wallet', 'accepts_online', 'accepts_cash_on_delivery', 'working_hours', ) class TopProductSerializer(serializers.Serializer): name = serializers.CharField() units_sold = serializers.IntegerField() revenue = serializers.IntegerField() class SalesChartPointSerializer(serializers.Serializer): bucket = serializers.DateTimeField() sales = serializers.IntegerField() class SellerAnalyticsSerializer(serializers.Serializer): period = serializers.CharField() order_count = serializers.IntegerField() total_sales = serializers.IntegerField() cancellation_rate = serializers.FloatField() average_order_value = serializers.IntegerField() chart = SalesChartPointSerializer(many=True) top_products = TopProductSerializer(many=True) class StoreBankAccountSerializer(serializers.ModelSerializer): class Meta: model = StoreBankAccount fields = ('iban', 'account_holder_name') class SellerStoreSerializer(serializers.ModelSerializer): """Read/write serializer for the authenticated seller's own store.""" category = StoreCategorySerializer(read_only=True) category_uuid = serializers.PrimaryKeyRelatedField( source='category', queryset=StoreCategory.objects.all(), write_only=True, ) city = CitySerializer(read_only=True) city_uuid = serializers.PrimaryKeyRelatedField( source='city', queryset=City.objects.filter(is_active=True), write_only=True, ) service_neighborhoods = NeighborhoodSerializer(many=True, read_only=True) service_neighborhood_uuids = serializers.PrimaryKeyRelatedField( source='service_neighborhoods', queryset=Neighborhood.objects.filter(is_active=True), write_only=True, many=True, required=False, ) latitude = serializers.FloatField(write_only=True, required=False) longitude = serializers.FloatField(write_only=True, required=False) bank_account = StoreBankAccountSerializer(read_only=True) logo_url = serializers.SerializerMethodField() cover_image_url = serializers.SerializerMethodField() class Meta: model = Store fields = ( 'uuid', 'name', 'category', 'category_uuid', 'description', 'logo', 'logo_url', 'cover_image', 'cover_image_url', 'phone_number', 'city', 'city_uuid', 'address', 'latitude', 'longitude', 'service_neighborhoods', 'service_neighborhood_uuids', 'delivery_radius_km', 'min_order_amount', 'delivery_fee', 'free_delivery_threshold', 'accepts_wallet', 'accepts_online', 'accepts_cash_on_delivery', 'rating_avg', 'rating_count', 'status', 'is_open', 'bank_account', 'created_at', ) read_only_fields = ('rating_avg', 'rating_count', 'status') @extend_schema_field(serializers.URLField(allow_null=True)) def get_logo_url(self, obj): return presigned_media_url(obj.logo) @extend_schema_field(serializers.URLField(allow_null=True)) def get_cover_image_url(self, obj): return presigned_media_url(obj.cover_image) def _pop_location(self, validated_data): lat = validated_data.pop('latitude', None) lng = validated_data.pop('longitude', None) if lat is not None and lng is not None: validated_data['location'] = Point(lng, lat, srid=4326) def create(self, validated_data): self._pop_location(validated_data) validated_data['owner'] = self.context['request'].user return super().create(validated_data) def update(self, instance, validated_data): self._pop_location(validated_data) return super().update(instance, validated_data)