Files
vontor-cz/backend/commerce/serializers.py
Brunobrno b8a1a594b2 Major refactor of commerce and Stripe integration
Refactored commerce models to support refunds, invoices, and improved carrier/payment logic. Added new serializers and viewsets for products, categories, images, discount codes, and refunds. Introduced Stripe client integration and removed legacy Stripe admin/model code. Updated Dockerfile for PDF generation dependencies. Removed obsolete migration files and updated configuration app initialization. Added invoice template and tasks for order cleanup.
2025-11-18 01:00:03 +01:00

250 lines
5.9 KiB
Python

from rest_framework import serializers
from drf_spectacular.utils import extend_schema_field
from decimal import Decimal
from django.contrib.auth import get_user_model
from .models import (
Category,
Product,
ProductImage,
DiscountCode,
Refund,
Order,
OrderItem,
Carrier,
Payment,
)
User = get_user_model()
class UserBriefSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ["id", "first_name", "last_name", "email"]
class ProductBriefSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ["id", "name", "price"]
class OrderItemReadSerializer(serializers.ModelSerializer):
product = ProductBriefSerializer(read_only=True)
total_price = serializers.SerializerMethodField()
def get_total_price(self, obj):
return obj.get_total_price(list(obj.order.discount.all()) if getattr(obj, "order", None) else None)
class Meta:
model = OrderItem
fields = ["id", "product", "quantity", "total_price"]
read_only_fields = ["id", "total_price"]
class CarrierReadSerializer(serializers.ModelSerializer):
class Meta:
model = Carrier
fields = ["id", "shipping_method", "weight", "returning"]
read_only_fields = ["id"]
class PaymentReadSerializer(serializers.Serializer):
payment_method = serializers.CharField(read_only=True)
stripe_id = serializers.IntegerField(source="stripe.id", read_only=True, allow_null=True)
class OrderReadSerializer(serializers.ModelSerializer):
items = OrderItemReadSerializer(many=True, read_only=True)
carrier = CarrierReadSerializer(read_only=True)
payment = PaymentReadSerializer(source="payment", read_only=True)
user = serializers.SerializerMethodField()
def get_user(self, obj):
request = self.context.get("request") if hasattr(self, "context") else None
if request and getattr(request, "user", None) and request.user.is_authenticated and obj.user:
return UserBriefSerializer(obj.user).data
return None
class Meta:
model = Order
fields = [
"id",
"status",
"total_price",
"currency",
"first_name",
"last_name",
"email",
"phone",
"address",
"city",
"postal_code",
"country",
"note",
"created_at",
"updated_at",
"carrier",
"payment",
"user",
"items",
]
read_only_fields = [
"id",
"status",
"total_price",
"currency",
"created_at",
"updated_at",
]
class OrderMiniSerializer(serializers.ModelSerializer):
amount = serializers.DecimalField(max_digits=10, decimal_places=2, source="total_price", read_only=True)
shipping_method = serializers.CharField(source="carrier.shipping_method", read_only=True)
class Meta:
model = Order
fields = ["id", "amount", "status", "email", "shipping_method"]
read_only_fields = fields
# ----------------- CREATE PAYLOAD SERIALIZERS (PUBLIC) -----------------
class OrderItemCreateSerializer(serializers.Serializer):
product_id = serializers.IntegerField(label="Product ID")
quantity = serializers.IntegerField(min_value=1, label="Quantity")
class CarrierCreateSerializer(serializers.Serializer):
shipping_method = serializers.ChoiceField(
choices=Carrier.SHIPPING.choices,
label="Shipping Method",
help_text="Choose 'store' (pickup) or 'packeta'",
)
weight = serializers.DecimalField(
required=False,
max_digits=10,
decimal_places=2,
label="Weight (kg)",
)
class PaymentCreateSerializer(serializers.Serializer):
payment_method = serializers.ChoiceField(
choices=Payment.PAYMENT.choices,
label="Payment Method",
help_text="Choose 'shop', 'stripe' or 'cash_on_delivery'",
)
class OrderCreateSerializer(serializers.Serializer):
# Customer/billing
first_name = serializers.CharField(max_length=100, label="First Name")
last_name = serializers.CharField(max_length=100, label="Last Name")
email = serializers.EmailField(label="Email")
phone = serializers.CharField(max_length=20, required=False, allow_blank=True, label="Phone")
address = serializers.CharField(max_length=255, label="Address")
city = serializers.CharField(max_length=100, label="City")
postal_code = serializers.CharField(max_length=20, label="Postal Code")
country = serializers.CharField(max_length=100, required=False, default="Czech Republic", label="Country")
note = serializers.CharField(required=False, allow_blank=True, label="Note")
# Nested
items = OrderItemCreateSerializer(many=True)
carrier = CarrierCreateSerializer()
payment = PaymentCreateSerializer()
discount_codes = serializers.ListField(
child=serializers.CharField(), required=False, allow_empty=True, label="Discount Codes"
)
def validate(self, attrs):
if not attrs.get("items"):
raise serializers.ValidationError({"items": "At least one item is required."})
return attrs
# ----------------- ADMIN/READ MODELS -----------------
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = [
"id",
"name",
"url",
"parent",
"description",
"image",
]
class ProductImageSerializer(serializers.ModelSerializer):
class Meta:
model = ProductImage
fields = [
"id",
"product",
"image",
"alt_text",
"is_main",
]
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = [
"id",
"name",
"description",
"code",
"category",
"price",
"url",
"stock",
"is_active",
"limited_to",
"default_carrier",
"created_at",
"updated_at",
]
read_only_fields = ["created_at", "updated_at"]
class DiscountCodeSerializer(serializers.ModelSerializer):
class Meta:
model = DiscountCode
fields = [
"id",
"code",
"description",
"percent",
"amount",
"valid_from",
"valid_to",
"active",
"usage_limit",
"used_count",
"specific_products",
"specific_categories",
]
read_only_fields = ["used_count"]
class RefundSerializer(serializers.ModelSerializer):
class Meta:
model = Refund
fields = [
"id",
"order",
"reason_choice",
"reason_text",
"verified",
"created_at",
]
read_only_fields = ["id", "verified", "created_at"]