diff --git a/care/emr/api/viewsets/scheduling/token.py b/care/emr/api/viewsets/scheduling/token.py index c24af4e3dd..3f264ae036 100644 --- a/care/emr/api/viewsets/scheduling/token.py +++ b/care/emr/api/viewsets/scheduling/token.py @@ -81,17 +81,21 @@ def perform_create(self, instance): super().perform_create(instance) def validate_data(self, instance, model_obj=None): - if ( - model_obj - and instance.sub_queue - and model_obj.sub_queue - and instance.sub_queue != model_obj.sub_queue.external_id - ): - existing_current = TokenSubQueue.objects.filter( - current_token=model_obj - ).exists() - if existing_current: - raise ValidationError("Sub Queue already has a current token") + if model_obj and instance.sub_queue: + if ( + model_obj.sub_queue + and instance.sub_queue != model_obj.sub_queue.external_id + ): + existing_current = TokenSubQueue.objects.filter( + current_token=model_obj + ).exists() + if existing_current: + raise ValidationError("Sub Queue already has a current token") + if ( + instance.sub_queue + and instance.status == TokenStatusOptions.IN_PROGRESS.value + ): + raise ValidationError("Use set_next endpoint to assign a token.") return super().validate_data(instance, model_obj) @@ -100,18 +104,26 @@ def perform_update(self, instance): raise ValidationError("Sub Queue and Queue are not in the same facility") with transaction.atomic(): obj = self.get_object() - if obj.sub_queue and obj.sub_queue != instance.sub_queue: - if ( - instance.sub_queue - and obj.sub_queue.resource != instance.sub_queue.resource - ): - raise ValidationError( - "Sub Queue and Queue are not in the same resource" - ) - # Clear current token if the sub queue is changed - if obj.sub_queue.current_token == obj: - obj.sub_queue.current_token = None - obj.sub_queue.save(update_fields=["current_token", "modified_date"]) + if ( + obj.sub_queue + and obj.sub_queue != instance.sub_queue + and instance.sub_queue + and obj.sub_queue.resource != instance.sub_queue.resource + ): + raise ValidationError( + "Sub Queue and Queue are not in the same resource" + ) + # Clear current token if the sub queue is changed + if ( + obj.sub_queue + and obj.sub_queue.current_token == obj + and ( + obj.sub_queue != instance.sub_queue + or instance.status != TokenStatusOptions.IN_PROGRESS.value + ) + ): + obj.sub_queue.current_token = None + obj.sub_queue.save(update_fields=["current_token", "modified_date"]) super().perform_update(instance) def perform_destroy(self, instance): @@ -181,17 +193,22 @@ def get_queryset(self): @action(detail=True, methods=["POST"]) def set_next(self, request, *args, **kwargs): obj = self.get_object() + if obj.status != TokenStatusOptions.CREATED.value: + raise ValidationError("Token in serving state cannot be set next") request_obj = SetCurrentTokenRequest(**request.data) queue = obj.queue self.authorize_update(None, obj) - with transaction.atomic(): - sub_queue = get_object_or_404( - TokenSubQueue, - external_id=request_obj.sub_queue, - resource=queue.resource, - ) + sub_queue = get_object_or_404( + TokenSubQueue, + external_id=request_obj.sub_queue, + resource=queue.resource, + ) + with Lock(f"token:set_next:{sub_queue.id}"), transaction.atomic(): + if sub_queue.current_token and sub_queue.current_token != obj: + raise ValidationError("Sub Queue already has a current token") sub_queue.current_token = obj sub_queue.save() obj.status = TokenStatusOptions.IN_PROGRESS.value + obj.sub_queue = sub_queue obj.save() return Response(self.get_retrieve_pydantic_model().serialize(obj).to_json()) diff --git a/care/emr/api/viewsets/scheduling/token_queue.py b/care/emr/api/viewsets/scheduling/token_queue.py index 97b673ed31..740bebebbb 100644 --- a/care/emr/api/viewsets/scheduling/token_queue.py +++ b/care/emr/api/viewsets/scheduling/token_queue.py @@ -232,9 +232,10 @@ def set_next_token_to_subqueue(self, request, *args, **kwargs): ).order_by("created_date") if category: tokens_qs = tokens_qs.filter(category=category) - if tokens_qs.exists(): - next_token = tokens_qs.first() - else: + tokens_in_waiting_qs = tokens_qs.filter(sub_queue__isnull=True) + tokens_in_calling_qs = tokens_qs.filter(sub_queue=sub_queue) + next_token = tokens_in_calling_qs.first() or tokens_in_waiting_qs.first() + if next_token is None: raise ValidationError("No tokens found") sub_queue.current_token = next_token sub_queue.save() diff --git a/care/emr/tests/test_token_api.py b/care/emr/tests/test_token_api.py index 6209823699..f32a4c1c30 100644 --- a/care/emr/tests/test_token_api.py +++ b/care/emr/tests/test_token_api.py @@ -275,7 +275,7 @@ def test_update_token_as_superuser(self): name="Sub Queue 1", ) token_data = { - "status": TokenStatusOptions.IN_PROGRESS, + "status": TokenStatusOptions.CREATED, "note": "Token is in progress", "sub_queue": subqueue.external_id, } @@ -289,7 +289,7 @@ def test_update_token_as_superuser(self): format="json", ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data["status"], TokenStatusOptions.IN_PROGRESS.value) + self.assertEqual(response.data["status"], TokenStatusOptions.CREATED.value) def test_update_token_as_user_with_permission(self): """Test updating a token as a user with permission.""" @@ -311,7 +311,7 @@ def test_update_token_as_user_with_permission(self): name="Sub Queue 1", ) token_data = { - "status": TokenStatusOptions.IN_PROGRESS, + "status": TokenStatusOptions.CREATED, "note": "Token is in progress", "sub_queue": subqueue.external_id, } @@ -325,7 +325,7 @@ def test_update_token_as_user_with_permission(self): format="json", ) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data["status"], TokenStatusOptions.IN_PROGRESS.value) + self.assertEqual(response.data["status"], TokenStatusOptions.CREATED.value) def test_update_token_as_user_without_permission(self): """Test updating a token as a user without permission.""" @@ -342,7 +342,7 @@ def test_update_token_as_user_without_permission(self): name="Sub Queue 1", ) token_data = { - "status": TokenStatusOptions.IN_PROGRESS, + "status": TokenStatusOptions.CREATED, "note": "Token is in progress", "sub_queue": subqueue.external_id, }