diff --git a/src/registrar/admin.py b/src/registrar/admin.py index 2fab7a735..f0f8a3673 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -2318,15 +2318,6 @@ class DomainInformationInline(admin.StackedInline): analyst_readonly_fields = copy.deepcopy(DomainInformationAdmin.analyst_readonly_fields) autocomplete_fields = copy.deepcopy(DomainInformationAdmin.autocomplete_fields) - # While the organization feature is under development, we can gate some fields - # from analysts for now. Remove this array and the get_fieldset overrides once this is done. - # Not my code initially, credit to Nicolle. This was once removed and like a phoenix it has been reborn. - superuser_only_fields = [ - "requested_suborganization", - "suborganization_city", - "suborganization_state_territory", - ] - def get_domain_managers(self, obj): user_domain_roles = UserDomainRole.objects.filter(domain=obj.domain) user_ids = user_domain_roles.values_list("user_id", flat=True) @@ -2427,14 +2418,6 @@ class DomainInformationInline(admin.StackedInline): # for permission-based field visibility. modified_fieldsets = copy.deepcopy(DomainInformationAdmin.get_fieldsets(self, request, obj=None)) - # Create a modified version of fieldsets to exclude certain fields - if not request.user.has_perm("registrar.full_access_permission"): - for name, data in modified_fieldsets: - fields = data.get("fields", []) - fields = tuple(field for field in fields if field not in self.superuser_only_fields) - modified_fieldsets.append((name, {**data, "fields": fields})) - return modified_fieldsets - # Modify fieldset sections in place for index, (title, options) in enumerate(modified_fieldsets): if title is None: diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js index cf6cbfb3a..64d12e400 100644 --- a/src/registrar/assets/js/get-gov.js +++ b/src/registrar/assets/js/get-gov.js @@ -2410,6 +2410,10 @@ document.addEventListener('DOMContentLoaded', function() { } })(); +/** An IIFE that intializes the requesting entity page. + * This page has a radio button that dynamically toggles some fields + * Within that, the dropdown also toggles some additional form elements. +*/ (function handleRequestingEntityFieldset() { // Check if the requesting-entity-fieldset exists. // This determines if we are on the requesting entity page or not. @@ -2423,15 +2427,13 @@ document.addEventListener('DOMContentLoaded', function() { const subOrgSelect = document.querySelector(`#id_${formPrefix}-sub_organization`); // The suborganization section is its own div + // Within the suborganization section, we also have a div that contains orgname, city, and stateterritory. const suborganizationFieldset = document.querySelector("#requesting-entity-fieldset__suborganization"); - - // Within the suborganization section, we also have a div that contains orgname, city, and stateterritory const suborganizationDetailsFieldset = document.querySelector("#requesting-entity-fieldset__suborganization__details"); + // This variable determines if the user is trying to request a new suborganization or not var isCustomSuborganization = document.querySelector("#id_portfolio_requesting_entity-is_custom_suborganization") - // Use a variable to determine which option has been selected on the yes/no form. - // Don't do anything if we are missing crucial page elements if (!isSuborgRadios || !subOrgSelect || !suborganizationFieldset || !suborganizationDetailsFieldset) return; @@ -2486,5 +2488,4 @@ document.addEventListener('DOMContentLoaded', function() { subOrgSelect.addEventListener("change", () => { toggleSuborganizationDetails(); }); - -})(); \ No newline at end of file +})(); diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py index 4aea46efe..62b9056bf 100644 --- a/src/registrar/forms/domain_request_wizard.py +++ b/src/registrar/forms/domain_request_wizard.py @@ -22,6 +22,10 @@ logger = logging.getLogger(__name__) class RequestingEntityForm(RegistrarForm): + """The requesting entity form contains a dropdown for suborganizations, + and some (hidden by default) input fields that allow the user to request for a suborganization. + All of these fields are not required by default, but as we use javascript to conditionally show + and hide some of these, they then become required in certain circumstances.""" sub_organization = forms.ModelChoiceField( label="Suborganization name", # not required because this field won't be filled out unless @@ -65,6 +69,7 @@ class RequestingEntityForm(RegistrarForm): is_custom_suborganization = forms.BooleanField(required=False, widget=forms.HiddenInput()) def __init__(self, *args, **kwargs): + """Override of init to add the suborganization queryset""" super().__init__(*args, **kwargs) if self.domain_request.portfolio: @@ -73,6 +78,8 @@ class RequestingEntityForm(RegistrarForm): ) def clean_sub_organization(self): + """On suborganization clean, set the suborganization value to None if the user is requesting + a custom suborganization (as it doesn't exist yet)""" sub_organization = self.cleaned_data.get("sub_organization") is_custom = self.cleaned_data.get("is_custom_suborganization") if is_custom: @@ -81,6 +88,8 @@ class RequestingEntityForm(RegistrarForm): return sub_organization def full_clean(self): + """Validation logic to remove the custom suborganization value before clean is triggered. + Without this override, the form will throw an 'invalid option' error.""" # Remove the custom other field before cleaning data = self.data.copy() if self.data else None suborganization = self.data.get("portfolio_requesting_entity-sub_organization") diff --git a/src/registrar/models/suborganization.py b/src/registrar/models/suborganization.py index 0b1c6e0ac..6ad80fdc0 100644 --- a/src/registrar/models/suborganization.py +++ b/src/registrar/models/suborganization.py @@ -1,5 +1,4 @@ from django.db import models -from registrar.models import DomainRequest from .utility.time_stamped_model import TimeStampedModel diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py index 13173565c..9d7122451 100644 --- a/src/registrar/tests/test_views_portfolio.py +++ b/src/registrar/tests/test_views_portfolio.py @@ -1387,3 +1387,91 @@ class TestPortfolio(WebTest): # Check that the domain request still exists self.assertTrue(DomainRequest.objects.filter(pk=domain_request.pk).exists()) domain_request.delete() + + +class TestRequestingEntity(WebTest): + """The requesting entity page is a domain request form that only exists + within the context of a portfolio.""" + def setUp(self): + super().setUp() + self.client = Client() + self.user = create_test_user() + self.domain, _ = Domain.objects.get_or_create(name="igorville.gov") + self.portfolio, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel California") + self.role, _ = UserDomainRole.objects.get_or_create( + user=self.user, domain=self.domain, role=UserDomainRole.Roles.MANAGER + ) + # Login the current user + self.app.set_user(self.user.username) + + def tearDown(self): + UserDomainRole.objects.all().delete() + DomainRequest.objects.all().delete() + DomainInformation.objects.all().delete() + Domain.objects.all().delete() + UserPortfolioPermission.objects.all().delete() + Portfolio.objects.all().delete() + User.objects.all().delete() + super().tearDown() + + # need a test that starts a new domain request + @override_flag("organization_feature", active=True) + @override_flag("organization_requests", active=True) + def test_requesting_entity_page(self): + """Tests that the requesting entity page loads correctly""" + pass + + @override_flag("organization_feature", active=True) + @override_flag("organization_requests", active=True) + def test_requesting_entity_page_submission(self): + """Tests that you can submit a form on this page""" + pass + + @override_flag("organization_feature", active=True) + @override_flag("organization_requests", active=True) + def test_requesting_entity_page_errors(self): + """Tests that we get the expected form errors on requesting entity""" + domain_request = completed_domain_request(user=self.user, portfolio=self.portfolio) + UserPortfolioPermission.objects.create(portfolio=self.portfolio, user=self.user, roles=[ + UserPortfolioRoleChoices.ORGANIZATION_ADMIN + ]) + response = self.app.get(reverse("edit-domain-request", kwargs={"id": domain_request.pk})).follow() + form = response.forms[0] + session_id = self.app.cookies[settings.SESSION_COOKIE_NAME] + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + + # Test missing suborganization selection + form['portfolio_requesting_entity-is_suborganization'] = True + form['portfolio_requesting_entity-sub_organization'] = "" + + response = form.submit() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + self.assertContains(response, "Select a suborganization.", status_code=200) + + # Test missing custom suborganization details + form['portfolio_requesting_entity-is_custom_suborganization'] = True + response = form.submit() + self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id) + self.assertContains(response, "Enter details for your organization name.", status_code=200) + self.assertContains(response, "Enter details for your city.", status_code=200) + self.assertContains(response, "Enter details for your state or territory.", status_code=200) + + domain_request.delete() + + @override_flag("organization_feature", active=True) + @override_flag("organization_requests", active=True) + def test_requesting_entity_submission_email_sent(self, mock_send_email): + """Tests that an email is sent out on form submission""" + pass + + @override_flag("organization_feature", active=True) + @override_flag("organization_requests", active=True) + def test_requesting_entity_viewonly(self): + """Tests the review steps page on under our viewonly context""" + pass + + @override_flag("organization_feature", active=True) + @override_flag("organization_requests", active=True) + def test_requesting_entity_manage(self): + """Tests the review steps page on under our manage context""" + pass diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py index 5a0d90240..0a4728d81 100644 --- a/src/registrar/views/domain_request.py +++ b/src/registrar/views/domain_request.py @@ -592,6 +592,9 @@ class RequestingEntity(DomainRequestWizard): forms = [forms.RequestingEntityYesNoForm, forms.RequestingEntityForm] def save(self, forms: list): + """Override of save to clear or associate certain suborganization data + depending on what the user wishes to do. For instance, we want to add a suborganization + if the user selects one.""" requesting_entity_form = forms[1] cleaned_data = requesting_entity_form.cleaned_data is_suborganization = cleaned_data.get("is_suborganization") @@ -859,13 +862,15 @@ class DomainRequestStatus(DomainRequestPermissionView): return True def get_context_data(self, **kwargs): + """Context override to add a step list to the context""" context = super().get_context_data(**kwargs) # Create a temp wizard object to grab the step list - wizard = PortfolioDomainRequestWizard() - wizard.request = self.request - context["Step"] = PortfolioDomainRequestStep.__members__ - context["steps"] = request_step_list(wizard, PortfolioDomainRequestStep) - context["form_titles"] = wizard.titles + if self.request.user.is_org_user(self.request): + wizard = PortfolioDomainRequestWizard() + wizard.request = self.request + context["Step"] = PortfolioDomainRequestStep.__members__ + context["steps"] = request_step_list(wizard, PortfolioDomainRequestStep) + context["form_titles"] = wizard.titles return context