diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index 88c064379..fba675bf7 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -2736,8 +2736,30 @@ class DomainRequestAdmin(ListHeaderAdmin, ImportExportModelAdmin):
return response
def change_view(self, request, object_id, form_url="", extra_context=None):
- """Display restricted warning,
- Setup the auditlog trail and pass it in extra context."""
+ """Display restricted warning, setup the auditlog trail and pass it in extra context,
+ display warning that status cannot be changed from 'Approved' if domain is in Ready state"""
+
+ # Fetch the domain request instance
+ domain_request: models.DomainRequest = models.DomainRequest.objects.get(pk=object_id)
+ if domain_request.approved_domain and domain_request.approved_domain.state == models.Domain.State.READY:
+ domain = domain_request.approved_domain
+ # get change url for domain
+ app_label = domain_request.approved_domain._meta.app_label
+ model_name = domain._meta.model_name
+ obj_id = domain.id
+ change_url = reverse("admin:%s_%s_change" % (app_label, model_name), args=[obj_id])
+
+ message = format_html(
+ "The status of this domain request cannot be changed because it has been joined to a domain in Ready status: " # noqa: E501
+ "{}",
+ mark_safe(change_url), # nosec
+ escape(str(domain)),
+ )
+ messages.warning(
+ request,
+ message,
+ )
+
obj = self.get_object(request, object_id)
self.display_restricted_warning(request, obj)
diff --git a/src/registrar/tests/test_admin_request.py b/src/registrar/tests/test_admin_request.py
index efb1331df..da789a1b5 100644
--- a/src/registrar/tests/test_admin_request.py
+++ b/src/registrar/tests/test_admin_request.py
@@ -28,6 +28,8 @@ from registrar.models import (
AllowedEmail,
Suborganization,
)
+from registrar.models.host import Host
+from registrar.models.public_contact import PublicContact
from .common import (
MockSESClient,
completed_domain_request,
@@ -39,7 +41,7 @@ from .common import (
MockEppLib,
GenericTestHelper,
)
-from unittest.mock import patch
+from unittest.mock import ANY, patch
from django.conf import settings
import boto3_mocking # type: ignore
@@ -79,6 +81,8 @@ class TestDomainRequestAdmin(MockEppLib):
def tearDown(self):
super().tearDown()
+ Host.objects.all().delete()
+ PublicContact.objects.all().delete()
Domain.objects.all().delete()
DomainInformation.objects.all().delete()
DomainRequest.objects.all().delete()
@@ -1889,6 +1893,37 @@ class TestDomainRequestAdmin(MockEppLib):
"Cannot edit a domain request with a restricted creator.",
)
+ @less_console_noise_decorator
+ def test_approved_domain_request_with_ready_domain_has_warning_message(self):
+ """Tests if the domain request has a warning message when the approved domain is in Ready state"""
+ # Create an instance of the model
+ domain_request = completed_domain_request(status=DomainRequest.DomainRequestStatus.IN_REVIEW)
+ # Approve the domain request
+ domain_request.approve()
+ domain_request.save()
+
+ # Add nameservers to get to Ready state
+ domain_request.approved_domain.nameservers = [
+ ("ns1.city.gov", ["1.1.1.1"]),
+ ("ns2.city.gov", ["1.1.1.2"]),
+ ]
+ domain_request.approved_domain.save()
+
+ with boto3_mocking.clients.handler_for("sesv2", self.mock_client):
+ with patch("django.contrib.messages.warning") as mock_warning:
+ # Create a request object
+ self.client.force_login(self.superuser)
+ self.client.get(
+ "/admin/registrar/domainrequest/{}/change/".format(domain_request.pk),
+ follow=True,
+ )
+
+ # Assert that the error message was called with the correct argument
+ mock_warning.assert_called_once_with(
+ ANY, # don't care about the request argument
+ f"The status of this domain request cannot be changed because it has been joined to a domain in Ready status: {domain_request.approved_domain.name}", # noqa
+ )
+
def trigger_saving_approved_to_another_state(self, domain_is_active, another_state, rejection_reason=None):
"""Helper method that triggers domain request state changes from approved to another state,
with an associated domain that can be either active (READY) or not.