winofy-backend/apps/stores/serializers.py
Ali Asadi 52adc732fa Initial Winofy backend: marketplace core (no payment integration)
Django 6 + DRF resource server against the Gooyal accounts OAuth2 service,
matching the Winsoo ecosystem's conventions. Covers locations, stores,
catalog, cart, checkout/orders (with the multi-store-cart split and the
status stepper), reviews, and notifications, plus a demo-data seed command.

Payment integration (wallet debits, online gateway, seller payouts) is
intentionally left out here — see feature/payment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-10 14:03:15 +03:30

116 lines
4.4 KiB
Python

from django.contrib.gis.geos import Point
from rest_framework import serializers
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):
class Meta:
model = StoreCategory
fields = ('uuid', 'name', 'icon', 'order')
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)
class Meta:
model = Store
fields = (
'uuid', 'name', 'category', 'logo', 'cover_image',
'rating_avg', 'rating_count', 'min_order_amount', 'delivery_fee',
'free_delivery_threshold', 'is_open',
)
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)
class Meta:
model = Store
fields = (
'uuid', 'name', 'category', 'category_uuid', 'description', 'logo', 'cover_image',
'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')
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)