105 lines
4.1 KiB
Python
105 lines
4.1 KiB
Python
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}"
|