from django.conf import settings from django.db import models from django.db.models import Q class AvailabilitySlot(models.Model): equipment_item = models.ForeignKey( "equipment.EquipmentItem", on_delete=models.CASCADE, null=True, blank=True, related_name="availability_slots", ) adventure_offering = models.ForeignKey( "adventrues.AdventureOffering", on_delete=models.CASCADE, null=True, blank=True, related_name="availability_slots", ) starts_at = models.DateTimeField() ends_at = models.DateTimeField() is_available = models.BooleanField(default=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: constraints = [ models.CheckConstraint( condition=Q(equipment_item__isnull=False, adventure_offering__isnull=True) | Q(equipment_item__isnull=True, adventure_offering__isnull=False), name="availability_slot_exactly_one_target", ), models.CheckConstraint(condition=Q(ends_at__gt=models.F("starts_at")), name="availability_slot_valid_range"), ] ordering = ("starts_at",) def __str__(self) -> str: target = self.equipment_item or self.adventure_offering return f"{target} | {self.starts_at} - {self.ends_at}" class Booking(models.Model): class Status(models.TextChoices): REQUESTED = "requested", "Requested" APPROVED = "approved", "Approved" DECLINED = "declined", "Declined" CANCELLED = "cancelled", "Cancelled" CONFIRMED = "confirmed", "Confirmed" customer = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="bookings", limit_choices_to={"is_customer": True} ) vendor = models.ForeignKey("accounts.VendorProfile", on_delete=models.CASCADE, related_name="bookings") equipment_item = models.ForeignKey( "equipment.EquipmentItem", on_delete=models.SET_NULL, null=True, blank=True, related_name="bookings" ) adventure_offering = models.ForeignKey( "adventrues.AdventureOffering", on_delete=models.SET_NULL, null=True, blank=True, related_name="bookings" ) starts_at = models.DateTimeField() ends_at = models.DateTimeField() status = models.CharField(max_length=16, choices=Status.choices, default=Status.REQUESTED) total_price = models.DecimalField(max_digits=10, decimal_places=2, default=0) customer_notes = models.TextField(blank=True) vendor_notes = models.TextField(blank=True) listing_click = models.ForeignKey( "marketing.ListingClick", on_delete=models.SET_NULL, null=True, blank=True, related_name="bookings", ) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: constraints = [ models.CheckConstraint( condition=Q(equipment_item__isnull=False, adventure_offering__isnull=True) | Q(equipment_item__isnull=True, adventure_offering__isnull=False), name="booking_exactly_one_target", ), models.CheckConstraint(condition=Q(ends_at__gt=models.F("starts_at")), name="booking_valid_range"), ] ordering = ("-created_at",) def __str__(self) -> str: return f"Booking #{self.id} ({self.status})" class BookingEventLog(models.Model): booking = models.ForeignKey(Booking, on_delete=models.CASCADE, related_name="events") from_status = models.CharField(max_length=16, choices=Booking.Status.choices, blank=True) to_status = models.CharField(max_length=16, choices=Booking.Status.choices) note = models.TextField(blank=True) actor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: ordering = ("-created_at",) def __str__(self) -> str: return f"Booking {self.booking_id}: {self.from_status} -> {self.to_status}"