kwork-poizonstore/store/serializers.py
phzhik e5c104bc11 + BonusProgramConfig
* Moved GlobalSettings to core app
* Moved bonus program logic from User to BonusProgram class
* Worked on error handling a bit
2024-05-24 02:19:00 +04:00

272 lines
9.9 KiB
Python

from django.db.transaction import atomic
from drf_extra_fields.fields import Base64ImageField
from rest_framework import serializers
from account.models.bonus import BonusProgram
from account.serializers import UserSerializer
from utils.exceptions import CRMException
from store.models import Checklist, Category, PaymentMethod, Promocode, Image, Gift
from core.models import GlobalSettings
from store.utils import get_primary_key_related_model
from poizonstore.utils import PriceField
class ImageSerializer(serializers.ModelSerializer):
image = Base64ImageField()
class Meta:
model = Image
fields = ('image',)
class ImageListSerializer(serializers.ListSerializer):
child = ImageSerializer()
def to_internal_value(self, data):
if isinstance(data, list):
data = [{"image": x} for x in data]
return super().to_internal_value(data)
def to_representation(self, data):
images = super().to_representation(data)
return [image['image'] for image in images]
class CategoryChecklistSerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('id', 'name')
class CategorySerializer(serializers.ModelSerializer):
chinarush = PriceField(source='delivery_price_CN_RU')
class Meta:
model = Category
fields = ('id', 'name', 'chinarush', 'commission')
class CategoryFullSerializer(CategorySerializer):
children = serializers.SerializerMethodField()
def get_children(self, obj):
return CategoryFullSerializer(obj.get_children(), many=True).data
class Meta:
model = CategorySerializer.Meta.model
fields = CategorySerializer.Meta.fields + ('children',)
class GiftSerializer(serializers.ModelSerializer):
image = Base64ImageField(required=False, allow_null=True)
min_price = PriceField()
class Meta:
model = Gift
fields = ('id', 'name', 'image', 'min_price', 'available_count')
class ChecklistSerializer(serializers.ModelSerializer):
id = serializers.CharField(read_only=True)
manager_id = serializers.PrimaryKeyRelatedField(read_only=True, allow_null=True)
link = serializers.URLField(source='product_link', required=False)
category = get_primary_key_related_model(CategoryChecklistSerializer, required=False, allow_null=True)
size = serializers.CharField(required=False, allow_null=True)
image = ImageListSerializer(source='main_images', required=False)
preview_image_url = serializers.ImageField(read_only=True)
promocode = serializers.SlugRelatedField(slug_field='name', queryset=Promocode.objects.active(),
required=False, allow_null=True)
use_bonuses = serializers.BooleanField(write_only=True, default=False)
gift = get_primary_key_related_model(GiftSerializer, required=False, allow_null=True)
yuan_rate = PriceField(read_only=True)
price_yuan = PriceField(required=False)
price_rub = serializers.IntegerField(read_only=True)
delivery_price_CN = PriceField(read_only=True)
delivery_price_CN_RU = PriceField(read_only=True)
full_price = PriceField(read_only=True)
real_price = PriceField(required=False, allow_null=True)
commission_rub = PriceField(read_only=True)
customer = get_primary_key_related_model(UserSerializer, required=False, allow_null=True)
receiver_name = serializers.CharField(required=False, allow_null=True)
receiver_phone = serializers.CharField(required=False, allow_null=True)
is_split_payment = serializers.BooleanField(required=False)
payment_method = serializers.SlugRelatedField(slug_field='slug',
queryset=PaymentMethod.objects.all(),
required=False, allow_null=True)
payment_proof = Base64ImageField(required=False, allow_null=True)
split_payment_proof = Base64ImageField(required=False, allow_null=True)
receipt = Base64ImageField(required=False, allow_null=True)
poizon_tracking = serializers.CharField(required=False, allow_null=True)
cdek_tracking = serializers.CharField(required=False, allow_null=True)
delivery = serializers.ChoiceField(choices=Checklist.DeliveryType.CHOICES, required=False, allow_null=True)
delivery_display = serializers.CharField(source='get_delivery_display', read_only=True)
created_at = serializers.DateTimeField(read_only=True)
status_updated_at = serializers.DateTimeField(read_only=True)
def _collect_images_by_fields(self, validated_data):
images = {}
for k in ('main_images',):
if k in validated_data:
images[k] = validated_data.pop(k)
return images
def _create_main_images(self, instance, images):
if images is None:
return
img_objs = [Image(image=img['image'], type=Image.DEFAULT) for img in images]
img_objs = Image.objects.bulk_create(img_objs)
instance.images.set(img_objs)
instance.generate_preview(next(iter(img_objs), None))
@atomic
def create(self, validated_data):
images = self._collect_images_by_fields(validated_data)
# Managers can create orders with arbitrary customers
# Client orders are created with client's account
user = self.context['request'].user
if not user.is_manager or validated_data.get('customer') is None:
validated_data['customer'] = user
use_bonuses = validated_data.pop('use_bonuses')
instance = super().create(validated_data)
self._create_main_images(instance, images.get('main_images'))
if use_bonuses:
BonusProgram.spend_bonuses(order=instance)
return instance
def update(self, instance, validated_data):
images = self._collect_images_by_fields(validated_data)
# Perform update
instance: Checklist = super().update(instance, validated_data)
self._create_main_images(instance, images.get('main_images'))
# Invalidate prefetched images
delattr(instance, '_images')
if instance.status == Checklist.Status.NEW:
has_payment_info = instance.payment_proof is not None and instance.payment_method is not None
if has_payment_info:
instance.status = Checklist.Status.PAYMENT
instance.save()
return instance
@staticmethod
def get_image(obj: Checklist):
return obj.images.all()
class Meta:
model = Checklist
fields = ('id', 'status', 'manager_id', 'link',
'category',
'brand', 'model', 'size',
'image',
'preview_image_url',
'yuan_rate', 'price_yuan', 'price_rub', 'delivery_price_CN', 'delivery_price_CN_RU', 'commission_rub',
'promocode', 'gift', 'use_bonuses', 'bonus_used',
'comment',
'full_price', 'real_price',
'customer',
'receiver_name', 'receiver_phone',
'is_split_payment', 'payment_method', 'payment_proof', 'split_payment_proof', 'split_accepted', 'receipt',
'poizon_tracking', 'cdek_tracking', 'cdek_barcode_pdf', 'delivery', 'delivery_display',
'created_at', 'status_updated_at', 'buy_time_remaining'
)
class ClientChecklistSerializerMixin:
def validate(self, attrs):
gift = attrs.get('gift')
if gift is not None:
if self.instance.price_yuan < gift.min_price:
raise CRMException("Can't add gift: price of order < min_price of gift")
if gift.available_count == 0:
raise CRMException("Gift is not available")
return attrs
class ClientCreateChecklistSerializer(ClientChecklistSerializerMixin, ChecklistSerializer):
status = serializers.SerializerMethodField()
class Meta:
model = ChecklistSerializer.Meta.model
fields = ChecklistSerializer.Meta.fields
writable_fields = {
'link',
'brand', 'model', 'size', 'category',
'price_yuan', 'use_bonuses',
'comment',
}
read_only_fields = tuple(set(ChecklistSerializer.Meta.fields) - writable_fields)
def get_status(self, obj):
return Checklist.Status.DRAFT
class ClientUpdateChecklistSerializer(ClientChecklistSerializerMixin, ChecklistSerializer):
class Meta:
model = ChecklistSerializer.Meta.model
fields = ChecklistSerializer.Meta.fields
writable_fields = {
'comment',
'payment_proof', 'payment_method',
'delivery',
'receiver_name', 'receiver_phone',
'gift', 'cdek_barcode_pdf'
}
read_only_fields = tuple(set(ChecklistSerializer.Meta.fields) - writable_fields)
class GlobalSettingsSerializer(serializers.ModelSerializer):
yuan_rate = PriceField(source='full_yuan_rate', read_only=True)
yuan_rate_commission = PriceField()
chinadelivery = PriceField(source='delivery_price_CN')
commission = PriceField(source='commission_rub')
pickup = serializers.CharField(source='pickup_address')
class Meta:
model = GlobalSettings
fields = ('yuan_rate', 'yuan_rate_last_updated', 'yuan_rate_commission', 'commission', 'chinadelivery', 'pickup', 'time_to_buy')
read_only_fields = ('yuan_rate_last_updated',)
class AnonymousGlobalSettingsSerializer(GlobalSettingsSerializer):
class Meta:
model = GlobalSettingsSerializer.Meta.model
fields = tuple(set(GlobalSettingsSerializer.Meta.fields) - {'yuan_rate_commission', 'yuan_rate_last_updated'})
class PaymentMethodSerializer(serializers.ModelSerializer):
class Meta:
model = PaymentMethod
fields = ('slug', 'name', 'requisites', 'cardnumber')
class PromocodeSerializer(serializers.ModelSerializer):
class Meta:
model = Promocode
fields = ('id', 'name', 'discount', 'free_delivery', 'no_comission', 'is_active')