init
This commit is contained in:
178
backend/commerce/serializers.py
Normal file
178
backend/commerce/serializers.py
Normal file
@@ -0,0 +1,178 @@
|
||||
from rest_framework import serializers
|
||||
from django.utils import timezone
|
||||
|
||||
from trznice.utils import RoundedDateTimeField
|
||||
from account.serializers import CustomUserSerializer
|
||||
from booking.serializers import ReservationSerializer
|
||||
from account.models import CustomUser
|
||||
from booking.models import Event, MarketSlot, Reservation
|
||||
from .models import Order
|
||||
|
||||
from decimal import Decimal
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
#počítaní ceny!!! (taky validní)
|
||||
class SlotPriceInputSerializer(serializers.Serializer):
|
||||
slot_id = serializers.PrimaryKeyRelatedField(queryset=MarketSlot.objects.all())
|
||||
used_extension = serializers.FloatField(min_value=0)
|
||||
|
||||
#počítaní ceny!!! (počítá správně!!)
|
||||
class PriceCalculationSerializer(serializers.Serializer):
|
||||
slot = serializers.PrimaryKeyRelatedField(queryset=MarketSlot.objects.all())
|
||||
reserved_from = RoundedDateTimeField()
|
||||
reserved_to = RoundedDateTimeField()
|
||||
used_extension = serializers.FloatField(min_value=0, required=False)
|
||||
|
||||
final_price = serializers.DecimalField(max_digits=8, decimal_places=2, read_only=True)
|
||||
|
||||
def validate(self, data):
|
||||
from django.utils.timezone import make_aware, is_naive
|
||||
|
||||
reserved_from = data["reserved_from"]
|
||||
reserved_to = data["reserved_to"]
|
||||
|
||||
if is_naive(reserved_from):
|
||||
reserved_from = make_aware(reserved_from)
|
||||
if is_naive(reserved_to):
|
||||
reserved_to = make_aware(reserved_to)
|
||||
|
||||
duration = reserved_to - reserved_from
|
||||
days = duration.days + 1 # zahrnujeme první den
|
||||
|
||||
data["reserved_from"] = reserved_from
|
||||
data["reserved_to"] = reserved_to
|
||||
data["duration"] = days
|
||||
|
||||
market_slot = data["slot"]
|
||||
event = market_slot.event if hasattr(market_slot, "event") else None
|
||||
|
||||
if not event or not event.square:
|
||||
raise serializers.ValidationError("Slot musí být přiřazen k akci, která má náměstí.")
|
||||
|
||||
# Get width and height from market_slot
|
||||
area = market_slot.width * market_slot.height
|
||||
|
||||
price_per_m2 = market_slot.price_per_m2 if market_slot.price_per_m2 and market_slot.price_per_m2 > 0 else event.price_per_m2
|
||||
|
||||
if not price_per_m2 or price_per_m2 < 0:
|
||||
raise serializers.ValidationError("Cena za m² není dostupná nebo je záporná.")
|
||||
|
||||
# Calculate final price using slot area and reserved days
|
||||
final_price = Decimal(area) * Decimal(price_per_m2) * Decimal(days)
|
||||
final_price = final_price.quantize(Decimal("0.01"))
|
||||
|
||||
data["final_price"] = final_price
|
||||
return data
|
||||
|
||||
|
||||
|
||||
class OrderSerializer(serializers.ModelSerializer):
|
||||
created_at = RoundedDateTimeField(read_only=True, required=False)
|
||||
payed_at = RoundedDateTimeField(read_only=True, required=False)
|
||||
|
||||
user = CustomUserSerializer(read_only=True)
|
||||
reservation = ReservationSerializer(read_only=True)
|
||||
|
||||
user_id = serializers.PrimaryKeyRelatedField(
|
||||
queryset=CustomUser.objects.all(), source="user", write_only=True, required=False, allow_null=True
|
||||
)
|
||||
reservation_id = serializers.PrimaryKeyRelatedField(
|
||||
queryset=Reservation.objects.all(), source="reservation", write_only=True
|
||||
)
|
||||
|
||||
price_to_pay = serializers.DecimalField(
|
||||
max_digits=10, decimal_places=2, required=False, allow_null=True
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = Order
|
||||
fields = [
|
||||
"id",
|
||||
"user", # nested read-only
|
||||
"user_id", # required in POST/PUT
|
||||
"reservation", # nested read-only
|
||||
"reservation_id", # required in POST/PUT
|
||||
"created_at",
|
||||
"status",
|
||||
"note",
|
||||
"price_to_pay",
|
||||
"payed_at",
|
||||
]
|
||||
read_only_fields = ["id", "created_at", "price_to_pay", "payed_at"]
|
||||
|
||||
extra_kwargs = {
|
||||
"user_id": {"help_text": "ID uživatele, který objednávku vytvořil", "required": False},
|
||||
"reservation_id": {"help_text": "ID rezervace, ke které se objednávka vztahuje", "required": True},
|
||||
"status": {"help_text": "Stav objednávky (např. new / paid / cancelled)", "required": False},
|
||||
"note": {"help_text": "Poznámka k objednávce (volitelné)", "required": False},
|
||||
"price_to_pay": {
|
||||
"help_text": "Celková cena, kterou má uživatel zaplatit. Pokud není zadána, převezme se z rezervace.",
|
||||
"required": False,
|
||||
"allow_null": True,
|
||||
},
|
||||
"payed_at": {"help_text": "Datum a čas, kdy byla objednávka zaplacena", "required": False},
|
||||
}
|
||||
|
||||
def validate(self, data):
|
||||
if "status" in data and data["status"] not in dict(Order.STATUS_CHOICES):
|
||||
raise serializers.ValidationError({"status": "Neplatný stav objednávky."})
|
||||
|
||||
# status = data.get("status", getattr(self.instance, "status", "pending"))
|
||||
# payed_at = data.get("payed_at", getattr(self.instance, "payed_at", None))
|
||||
reservation = data.get("reservation", getattr(self.instance, "reservation", None))
|
||||
price = data.get("price_to_pay", getattr(self.instance, "price_to_pay", 0))
|
||||
|
||||
errors = {}
|
||||
|
||||
# if status == "payed" and not payed_at:
|
||||
# errors["payed_at"] = "Musíte zadat datum a čas zaplacení, pokud je objednávka zaplacena."
|
||||
|
||||
# if status != "payed" and payed_at:
|
||||
# errors["payed_at"] = "Datum zaplacení může být uvedeno pouze u zaplacených objednávek."
|
||||
|
||||
if price is not None and price < 0:
|
||||
errors["price_to_pay"] = "Cena musí být větší nebo rovna 0."
|
||||
|
||||
if reservation:
|
||||
if self.instance is None and hasattr(reservation, "order"):
|
||||
errors["reservation"] = "Tato rezervace již má přiřazenou objednávku."
|
||||
|
||||
|
||||
user = data.get("user")
|
||||
request_user = self.context["request"].user if "request" in self.context else None
|
||||
|
||||
# If user is not specified, use the logged-in user
|
||||
if user is None and request_user is not None:
|
||||
user = request_user
|
||||
data["user"] = user
|
||||
|
||||
# If user is specified and differs from logged-in user, check permissions
|
||||
if user is not None and request_user is not None and user != request_user:
|
||||
if request_user.role not in ["admin", "cityClerk", "squareManager"]:
|
||||
errors["user"] = "Pouze administrátor, úředník nebo správce tržiště může vytvářet rezervace pro jiné uživatele."
|
||||
|
||||
if errors:
|
||||
raise serializers.ValidationError(errors)
|
||||
|
||||
return data
|
||||
|
||||
def create(self, validated_data):
|
||||
if validated_data.get("reservation"):
|
||||
validated_data["price_to_pay"] = validated_data["reservation"].final_price
|
||||
|
||||
validated_data["user"] = validated_data.pop("user_id", validated_data.get("user"))
|
||||
validated_data["reservation"] = validated_data.pop("reservation_id", validated_data.get("reservation"))
|
||||
|
||||
return super().create(validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
old_status = instance.status
|
||||
new_status = validated_data.get("status", old_status)
|
||||
|
||||
logger.debug(f"\n\nUpdating order {instance.id} from status {old_status} to {new_status}\n\n")
|
||||
|
||||
if old_status != "payed" and new_status == "payed":
|
||||
validated_data["payed_at"] = timezone.now()
|
||||
return super().update(instance, validated_data)
|
||||
Reference in New Issue
Block a user