fix error in gitignore

This commit is contained in:
2026-09-24 12:32:59 +03:30
commit 86c117ee2a
165 changed files with 5963 additions and 0 deletions
+91
View File
@@ -0,0 +1,91 @@
from django.db import transaction
from django.utils import timezone
from apps.core.errors import ApiError
from apps.reservations.models import Reservation, Slot
class ReservationService:
"""Booking with backend-enforced availability, double-booking prevention,
and admin approval (which triggers a customer SMS)."""
@transaction.atomic
def book(self, customer, slot, title: str = "", notes: str = "") -> Reservation:
slot = Slot.objects.select_for_update().get(pk=slot.pk)
if slot.blocked:
raise ApiError("SLOT_BLOCKED", "This slot is blocked.")
if slot.date < timezone.localdate():
raise ApiError("SLOT_PAST", "Cannot book a slot in the past.")
if slot.booked_count >= slot.capacity:
raise ApiError("SLOT_FULL", "This slot is fully booked.")
slot.booked_count += 1
slot.save(update_fields=["booked_count"])
return Reservation.objects.create(
customer=customer,
slot=slot,
title=title,
status=Reservation.Status.PENDING,
notes=notes,
)
@transaction.atomic
def approve(self, reservation, user) -> Reservation:
reservation = Reservation.objects.select_for_update().select_related("slot", "customer").get(pk=reservation.pk)
if reservation.status != Reservation.Status.PENDING:
raise ApiError("INVALID_STATUS", "Only pending reservations can be approved.")
reservation.status = Reservation.Status.CONFIRMED
reservation.approved_by = user
reservation.approved_at = timezone.now()
reservation.save(update_fields=["status", "approved_by", "approved_at"])
from apps.notifications.services import NotificationService
customer = reservation.customer
NotificationService().send_event(
"reservation.approved",
{
"customer_name": customer.company_name or customer.user.get_full_name() or customer.user.username,
"reservation_title": reservation.title,
"slot_date": str(reservation.slot.date),
"slot_time": str(reservation.slot.start_time),
},
recipient=customer.user.phone or customer.user.username,
)
return reservation
@transaction.atomic
def reject(self, reservation, user) -> Reservation:
reservation = Reservation.objects.select_for_update().select_related("slot").get(pk=reservation.pk)
if reservation.status not in (Reservation.Status.PENDING, Reservation.Status.CONFIRMED):
raise ApiError("INVALID_STATUS", "This reservation cannot be rejected.")
reservation.status = Reservation.Status.REJECTED
reservation.approved_by = user
reservation.approved_at = timezone.now()
reservation.save(update_fields=["status", "approved_by", "approved_at"])
slot = reservation.slot
slot.booked_count = max(0, slot.booked_count - 1)
slot.save(update_fields=["booked_count"])
return reservation
@transaction.atomic
def cancel(self, reservation) -> Reservation:
reservation = (
Reservation.objects.select_for_update()
.select_related("slot")
.get(pk=reservation.pk)
)
if reservation.status == Reservation.Status.CANCELLED:
return reservation
if reservation.status == Reservation.Status.WEIGHED:
raise ApiError("RESERVATION_LOCKED", "Cannot cancel a reservation that has been weighed.")
reservation.status = Reservation.Status.CANCELLED
reservation.save(update_fields=["status"])
slot = reservation.slot
slot.booked_count = max(0, slot.booked_count - 1)
slot.save(update_fields=["booked_count"])
return reservation
@transaction.atomic
def reschedule(self, reservation, new_slot) -> Reservation:
self.cancel(reservation)
return self.book(reservation.customer, new_slot, title=reservation.title)