Refactor order creation and add configuration endpoints

Refactored order creation logic to use new serializers and transaction handling, improving validation and modularity. Introduced admin and public endpoints for shop configuration with sensitive fields protected. Enhanced Zásilkovna (Packeta) integration, including packet widget template, new API fields, and improved error handling. Added django-silk for profiling, updated requirements and settings, and improved frontend Orval config for API client generation.
This commit is contained in:
David Bruno Vontor
2025-12-08 18:19:20 +01:00
parent 5b066e2770
commit 946f86db7e
18 changed files with 606 additions and 309 deletions

View File

@@ -42,7 +42,7 @@ from .serializers import (
RefundSerializer,
)
#FIXME: uravit view na nový order serializer
@extend_schema_view(
list=extend_schema(tags=["Orders"], summary="List Orders (public)"),
retrieve=extend_schema(tags=["Orders"], summary="Retrieve Order (public)"),
@@ -63,7 +63,7 @@ class OrderViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.Ge
return OrderReadSerializer
@extend_schema(
tags=["Orders"],
tags=["Order"],
summary="Create Order (public)",
request=OrderCreateSerializer,
responses={201: OrderReadSerializer},
@@ -92,87 +92,10 @@ class OrderViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.Ge
],
)
def create(self, request, *args, **kwargs):
serializer = OrderCreateSerializer(data=request.data)
serializer = OrderCreateSerializer(data=request.data, context={"request": request})
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
user = request.user
#VELMI DŮLEŽITELÉ: vše vytvořit v transakci, aby se nepřidávaly neúplné objednávky
with transaction.atomic():
# Create base order (customer details only for now)
order = Order.objects.create(
user=user if getattr(user, "is_authenticated", False) else None,
first_name=data["first_name"],
last_name=data["last_name"],
email=data["email"],
phone=data.get("phone", ""),
address=data["address"],
city=data["city"],
postal_code=data["postal_code"],
country=data.get("country", "Czech Republic"),
note=data.get("note", ""),
)
# Carrier
carrier_payload = data["carrier"]
carrier = Carrier.objects.create(
shipping_method=carrier_payload["shipping_method"],
weight=carrier_payload.get("weight"),
)
order.carrier = carrier
order.save(update_fields=["carrier", "updated_at"]) # recalc later after items
# Items
items_payload = data["items"]
order_items = []
for item in items_payload:
product = Product.objects.get(pk=item["product_id"]) # raises 404 if missing
qty = int(item["quantity"])
order_items.append(OrderItem(order=order, product=product, quantity=qty))
OrderItem.objects.bulk_create(order_items)
# Discount codes (optional)
codes = data.get("discount_codes") or []
if codes:
discounts = list(DiscountCode.objects.filter(code__in=codes))
order.discount.add(*discounts)
# Recalculate now that items/discounts/carrier are linked
order.save()
# Payment and validation
pay_payload = data["payment"]
payment_method = pay_payload["payment_method"]
# Validate combos (mirror of Payment.save but here we have order)
if payment_method == Payment.PAYMENT.SHOP and order.carrier.shipping_method != Carrier.SHIPPING.STORE:
return Response(
{"payment": "Platba v obchodě je možná pouze pro osobní odběr."},
status=status.HTTP_400_BAD_REQUEST,
)
if payment_method == Payment.PAYMENT.CASH_ON_DELIVERY and order.carrier.shipping_method == Carrier.SHIPPING.STORE:
return Response(
{"payment": "Dobírka není možná pro osobní odběr."},
status=status.HTTP_400_BAD_REQUEST,
)
# Create payment WITHOUT triggering Payment.save (which expects reverse link first)
payment = Payment(payment_method=payment_method)
# Bypass custom save by bulk_create
Payment.objects.bulk_create([payment])
order.payment = payment
order.save(update_fields=["payment", "updated_at"])
# If Stripe, create StripePayment now and attach
if payment_method == Payment.PAYMENT.STRIPE:
from thirdparty.stripe.models import StripePayment
stripe_obj = StripePayment.objects.create(amount=order.total_price)
payment.stripe = stripe_obj
payment.save(update_fields=["stripe"])
out = self.get_serializer(order)
order = serializer.save()
out = OrderReadSerializer(order)
return Response(out.data, status=status.HTTP_201_CREATED)
# -- List mini orders -- (public) --