Repository: cisagov/manage.get.gov

Issue:
Domain managers should not be able to DELETE retrieved domain invitations
### Issue description

We have seen users who are newly added to a domain choosing to press "Cancel" in the "Domain Invitations" table on the "Domain Managers" for the invitations that they just retrieved, which were presumably marked as status "retrieved". This takes them out of that table entirely which is not something we want (the "Cancel" button actually deletes a row from the DomainInvitation table).

To prevent losing that information, we probably want to remove the "Cancel" link from domain invitations that are retrieved so that they stay in that table forever. Domains who do lots of inviting and uninviting people might get confused with that table as it grows, but not deleting seems better at this point than confusing people.

### Acceptance criteria

- [ ] In the Domain Invitations table, there should not be a "Cancel" link for entries that are in the "retrieved" status
- [ ] We should check in the `DomainInvitationDeleteView` that the invitation is not retrieved so that a user can't use a POST request to delete a retrieved invitation in the absence of a "Cancel" link.

### Additional context

@h-m-f-t and @amcelya observed this with a new domain manager.

### Links to other issues

_No response_


Discussion:
Suggested timeframe: sprint 41/42

Resolving patch:
diff --git a/src/registrar/views/domain.py b/src/registrar/views/domain.py
--- a/src/registrar/views/domain.py
+++ b/src/registrar/views/domain.py
@@ -822,6 +822,18 @@ def form_valid(self, form):
 class DomainInvitationDeleteView(SuccessMessageMixin, DomainInvitationPermissionDeleteView):
     object: DomainInvitation  # workaround for type mismatch in DeleteView
 
+    def post(self, request, *args, **kwargs):
+        """Override post method in order to error in the case when the
+        domain invitation status is RETRIEVED"""
+        self.object = self.get_object()
+        form = self.get_form()
+        if form.is_valid() and self.object.status == self.object.DomainInvitationStatus.INVITED:
+            return self.form_valid(form)
+        else:
+            # Produce an error message if the domain invatation status is RETRIEVED
+            messages.error(request, f"Invitation to {self.object.email} has already been retrieved.")
+            return HttpResponseRedirect(self.get_success_url())
+
     def get_success_url(self):
         return reverse("domain-users", kwargs={"pk": self.object.domain.id})
 


Test patch:
diff --git a/src/registrar/tests/test_views_domain.py b/src/registrar/tests/test_views_domain.py
--- a/src/registrar/tests/test_views_domain.py
+++ b/src/registrar/tests/test_views_domain.py
@@ -665,6 +665,22 @@ def test_domain_invitation_cancel(self):
         with self.assertRaises(DomainInvitation.DoesNotExist):
             DomainInvitation.objects.get(id=invitation.id)
 
+    def test_domain_invitation_cancel_retrieved_invitation(self):
+        """Posting to the delete view when invitation retrieved returns an error message"""
+        email_address = "mayor@igorville.gov"
+        invitation, _ = DomainInvitation.objects.get_or_create(
+            domain=self.domain, email=email_address, status=DomainInvitation.DomainInvitationStatus.RETRIEVED
+        )
+        with less_console_noise():
+            response = self.client.post(reverse("invitation-delete", kwargs={"pk": invitation.id}), follow=True)
+            # Assert that an error message is displayed to the user
+            self.assertContains(response, f"Invitation to {email_address} has already been retrieved.")
+            # Assert that the Cancel link is not displayed
+            self.assertNotContains(response, "Cancel")
+        # Assert that the DomainInvitation is not deleted
+        self.assertTrue(DomainInvitation.objects.filter(id=invitation.id).exists())
+        DomainInvitation.objects.filter(email=email_address).delete()
+
     def test_domain_invitation_cancel_no_permissions(self):
         """Posting to the delete view as a different user should fail."""
         email_address = "mayor@igorville.gov"