from rest_framework import serializers from adventrues.models import AdventureOffering from equipment.models import EquipmentItem from marketing.models import ListingClick from marketing.services import listing_click_valid_for_booking from .models import Booking, BookingEventLog from .services import create_booking_request, quote_booking class BookingEventLogSerializer(serializers.ModelSerializer): actor_email = serializers.EmailField(source="actor.email", read_only=True) class Meta: model = BookingEventLog fields = ("id", "from_status", "to_status", "note", "actor_email", "created_at") class BookingSerializer(serializers.ModelSerializer): customer_email = serializers.EmailField(source="customer.email", read_only=True) vendor_slug = serializers.CharField(source="vendor.slug", read_only=True) equipment_public_id = serializers.CharField(source="equipment_item.public_id", read_only=True) adventure_public_id = serializers.CharField(source="adventure_offering.public_id", read_only=True) events = BookingEventLogSerializer(many=True, read_only=True) class Meta: model = Booking fields = ( "id", "customer_email", "vendor_slug", "equipment_item", "equipment_public_id", "adventure_public_id", "starts_at", "ends_at", "status", "total_price", "customer_notes", "vendor_notes", "events", "listing_click", "created_at", "updated_at", ) read_only_fields = ("status", "total_price", "created_at", "updated_at", "events", "listing_click") class BookingCreateSerializer(serializers.Serializer): equipment_item_id = serializers.PrimaryKeyRelatedField( queryset=EquipmentItem.objects.filter(is_active=True), required=False, allow_null=True, ) adventure_offering_id = serializers.PrimaryKeyRelatedField( queryset=AdventureOffering.objects.filter(is_active=True), required=False, allow_null=True, ) starts_at = serializers.DateTimeField() ends_at = serializers.DateTimeField() customer_notes = serializers.CharField(required=False, allow_blank=True) participants_count = serializers.IntegerField(required=False, min_value=1, default=1) marketing_click_id = serializers.IntegerField(required=False, allow_null=True) def validate(self, attrs): equipment_item = attrs.get("equipment_item_id") adventure_offering = attrs.get("adventure_offering_id") if bool(equipment_item) == bool(adventure_offering): raise serializers.ValidationError("Provide exactly one target: equipment_item_id or adventure_offering_id.") click_id = attrs.get("marketing_click_id") listing_click = None if click_id is not None: listing_click = ListingClick.objects.filter(pk=click_id).first() if not listing_click: raise serializers.ValidationError({"marketing_click_id": "Invalid marketing_click_id."}) if not listing_click_valid_for_booking( click=listing_click, equipment_item=equipment_item, adventure_offering=adventure_offering, ): raise serializers.ValidationError( {"marketing_click_id": "Click does not match this listing or is outside the attribution window."} ) attrs["_listing_click"] = listing_click try: quote_booking( equipment_item=equipment_item, adventure_offering=adventure_offering, starts_at=attrs["starts_at"], ends_at=attrs["ends_at"], participants_count=attrs.get("participants_count", 1), ) except Exception as exc: raise serializers.ValidationError(str(exc)) from exc return attrs def create(self, validated_data): try: booking = create_booking_request( customer=self.context["request"].user, starts_at=validated_data["starts_at"], ends_at=validated_data["ends_at"], equipment_item=validated_data.get("equipment_item_id"), adventure_offering=validated_data.get("adventure_offering_id"), participants_count=validated_data.get("participants_count", 1), customer_notes=validated_data.get("customer_notes", ""), listing_click=validated_data.get("_listing_click"), ) except Exception as exc: raise serializers.ValidationError({"non_field_errors": [str(exc)]}) from exc return booking