Files
2025-11-09 00:26:00 -06:00

254 lines
7.7 KiB
Python

from decimal import Decimal
from django.db import transaction
from rest_framework import serializers
from . import models
class ProductCategorySerializer(serializers.ModelSerializer):
class Meta:
model = models.ProductCategory
fields = (
"id",
"name",
"slug",
"description",
"display_order",
"is_active",
)
class ProductSerializer(serializers.ModelSerializer):
category = ProductCategorySerializer(read_only=True)
category_id = serializers.PrimaryKeyRelatedField(
source="category",
queryset=models.ProductCategory.objects.all(),
required=False,
allow_null=True,
write_only=True,
)
class Meta:
model = models.Product
fields = (
"id",
"name",
"slug",
"short_description",
"description",
"ingredients",
"allergens",
"price",
"inventory_quantity",
"max_per_order",
"is_active",
"is_featured",
"hero_image",
"category",
"category_id",
)
class DeliveryZoneSerializer(serializers.ModelSerializer):
is_active = serializers.BooleanField(required=False)
class Meta:
model = models.DeliveryZone
fields = ("id", "label", "postal_code", "is_active")
class DeliveryOverrideCodeSerializer(serializers.ModelSerializer):
can_use = serializers.SerializerMethodField()
class Meta:
model = models.DeliveryOverrideCode
fields = (
"id",
"code",
"label",
"message_to_customer",
"max_uses",
"usage_count",
"is_active",
"can_use",
)
read_only_fields = ("usage_count", "can_use")
def get_can_use(self, obj: models.DeliveryOverrideCode) -> bool:
return obj.can_use()
class OrderItemSerializer(serializers.ModelSerializer):
line_subtotal = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
class Meta:
model = models.OrderItem
fields = (
"id",
"product",
"product_name",
"product_slug",
"unit_price",
"quantity",
"line_subtotal",
)
read_only_fields = ("product_name", "product_slug", "unit_price", "line_subtotal")
class OrderSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True)
subtotal_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
tax_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
delivery_fee_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
discount_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
total_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
class Meta:
model = models.Order
fields = (
"id",
"customer_name",
"customer_email",
"customer_phone",
"fulfillment_method",
"status",
"scheduled_date",
"scheduled_time_slot",
"delivery_address_line1",
"delivery_address_line2",
"delivery_city",
"delivery_state",
"delivery_postal_code",
"delivery_override_code",
"subtotal_amount",
"tax_amount",
"delivery_fee_amount",
"discount_amount",
"total_amount",
"notes",
"items",
"created_at",
"updated_at",
)
read_only_fields = ("status", "created_at", "updated_at")
def create(self, validated_data):
items_data = validated_data.pop("items", [])
delivery_fee_amount = Decimal(validated_data.pop("delivery_fee_amount", Decimal("0")))
discount_amount = Decimal(validated_data.pop("discount_amount", Decimal("0")))
tax_amount = Decimal(validated_data.pop("tax_amount", Decimal("0")))
if not items_data:
raise serializers.ValidationError("Order requires at least one item.")
with transaction.atomic():
subtotal = Decimal("0")
order_items = []
for item_data in items_data:
product = item_data.get("product")
if product is None:
raise serializers.ValidationError("Each order item must reference a product.")
quantity = item_data.get("quantity", 1)
if quantity > product.inventory_quantity:
raise serializers.ValidationError(
f"Insufficient inventory for {product.name}. Requested {quantity}, "
f"available {product.inventory_quantity}."
)
line_subtotal = product.price * quantity
subtotal += line_subtotal
order_items.append(
{
"product": product,
"product_name": product.name,
"product_slug": product.slug,
"unit_price": product.price,
"quantity": quantity,
"line_subtotal": line_subtotal,
}
)
product.inventory_quantity -= quantity
product.save(update_fields=["inventory_quantity"])
total_amount = subtotal + tax_amount + delivery_fee_amount - discount_amount
order = models.Order.objects.create(
subtotal_amount=subtotal,
tax_amount=tax_amount,
delivery_fee_amount=delivery_fee_amount,
discount_amount=discount_amount,
total_amount=total_amount,
**validated_data,
)
models.OrderItem.objects.bulk_create(
[models.OrderItem(order=order, **item) for item in order_items]
)
return order
class OrderAdminSerializer(serializers.ModelSerializer):
items = OrderItemSerializer(many=True, read_only=True)
subtotal_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
tax_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
delivery_fee_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
discount_amount = serializers.DecimalField(
max_digits=8, decimal_places=2, read_only=True
)
total_amount = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
class Meta:
model = models.Order
fields = (
"id",
"customer_name",
"customer_email",
"customer_phone",
"fulfillment_method",
"status",
"scheduled_date",
"scheduled_time_slot",
"delivery_address_line1",
"delivery_address_line2",
"delivery_city",
"delivery_state",
"delivery_postal_code",
"delivery_override_code",
"subtotal_amount",
"tax_amount",
"delivery_fee_amount",
"discount_amount",
"total_amount",
"notes",
"items",
"created_at",
"updated_at",
)
read_only_fields = (
"subtotal_amount",
"tax_amount",
"delivery_fee_amount",
"discount_amount",
"total_amount",
"created_at",
"updated_at",
)