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.
This commit is contained in:
@@ -1,4 +1,249 @@
|
||||
from rest_framework import serializers
|
||||
from drf_spectacular.utils import extend_schema_field
|
||||
from .models import Category, Product, ProductImage, DiscountCode, Order, OrderItem, Carrier
|
||||
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"]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user