diff --git a/care/emr/api/viewsets/scheduling/booking.py b/care/emr/api/viewsets/scheduling/booking.py index 010962be21..7486603c01 100644 --- a/care/emr/api/viewsets/scheduling/booking.py +++ b/care/emr/api/viewsets/scheduling/booking.py @@ -3,7 +3,7 @@ from django.db import transaction from django_filters import CharFilter, DateFromToRangeFilter, FilterSet, UUIDFilter from django_filters.rest_framework import DjangoFilterBackend -from pydantic import BaseModel +from pydantic import UUID4, BaseModel from rest_framework.decorators import action from rest_framework.exceptions import PermissionDenied from rest_framework.generics import get_object_or_404 @@ -15,6 +15,8 @@ EMRRetrieveMixin, EMRUpdateMixin, ) +from care.emr.api.viewsets.scheduling import lock_create_appointment +from care.emr.models import TokenSlot from care.emr.models.scheduling import SchedulableUserResource, TokenBooking from care.emr.resources.scheduling.slot.spec import ( CANCELLED_STATUS_CHOICES, @@ -29,10 +31,16 @@ class CancelBookingSpec(BaseModel): reason: Literal[ - BookingStatusChoices.cancelled, BookingStatusChoices.entered_in_error + BookingStatusChoices.cancelled, + BookingStatusChoices.entered_in_error, + BookingStatusChoices.rescheduled, ] +class RescheduleBookingSpec(BaseModel): + new_slot: UUID4 + + class TokenBookingFilters(FilterSet): status = CharFilter(field_name="status") date = DateFromToRangeFilter(field_name="token_slot__start_datetime__date") @@ -117,6 +125,37 @@ def cancel(self, request, *args, **kwargs): self.authorize_update({}, instance) return self.cancel_appointment_handler(instance, request.data, request.user) + @action(detail=True, methods=["POST"]) + def reschedule(self, request, *args, **kwargs): + request_data = RescheduleBookingSpec(**request.data) + existing_booking = self.get_object() + facility = self.get_facility_obj() + self.authorize_update({}, existing_booking) + if not AuthorizationController.call( + "can_create_appointment", self.request.user, facility + ): + raise PermissionDenied("You do not have permission to create appointments") + new_slot = get_object_or_404( + TokenSlot, + external_id=request_data.new_slot, + resource=existing_booking.token_slot.resource, + ) + with transaction.atomic(): + self.cancel_appointment_handler( + existing_booking, + {"reason": BookingStatusChoices.rescheduled}, + request.user, + ) + appointment = lock_create_appointment( + new_slot, + existing_booking.patient, + request.user, + existing_booking.reason_for_visit, + ) + return Response( + TokenBookingReadSpec.serialize(appointment).model_dump(exclude=["meta"]) + ) + @action(detail=False, methods=["GET"]) def available_users(self, request, *args, **kwargs): facility = Facility.objects.get(external_id=self.kwargs["facility_external_id"]) diff --git a/care/emr/resources/scheduling/slot/spec.py b/care/emr/resources/scheduling/slot/spec.py index 25df6b5f90..cb40bb3461 100644 --- a/care/emr/resources/scheduling/slot/spec.py +++ b/care/emr/resources/scheduling/slot/spec.py @@ -8,8 +8,10 @@ from care.emr.models.scheduling.booking import TokenSlot from care.emr.models.scheduling.schedule import Availability from care.emr.resources.base import EMRResource +from care.emr.resources.facility.spec import FacilityBareMinimumSpec from care.emr.resources.patient.otp_based_flow import PatientOTPReadSpec from care.emr.resources.user.spec import UserSpec +from care.facility.models import Facility from care.users.models import User @@ -52,11 +54,13 @@ class BookingStatusChoices(str, Enum): checked_in = "checked_in" waitlist = "waitlist" in_consultation = "in_consultation" + rescheduled = "rescheduled" CANCELLED_STATUS_CHOICES = [ BookingStatusChoices.entered_in_error.value, BookingStatusChoices.cancelled.value, + BookingStatusChoices.rescheduled.value, ] @@ -83,6 +87,7 @@ class TokenBookingReadSpec(TokenBookingBaseSpec): status: str reason_for_visit: str user: dict = {} + facility: dict = {} @classmethod def perform_extra_serialization(cls, mapping, obj): @@ -96,3 +101,6 @@ def perform_extra_serialization(cls, mapping, obj): mapping["user"] = UserSpec.serialize( User.objects.get(id=obj.token_slot.resource.user_id) ).model_dump(exclude=["meta"]) + mapping["facility"] = FacilityBareMinimumSpec.serialize( + Facility.objects.get(id=obj.token_slot.resource.facility_id) + ).model_dump(exclude=["meta"])