diff --git a/src/registrar/admin.py b/src/registrar/admin.py index fe59c7d1f..8a0a458f8 100644 --- a/src/registrar/admin.py +++ b/src/registrar/admin.py @@ -5,6 +5,7 @@ from django.db.models import Value, CharField, Q from django.db.models.functions import Concat, Coalesce from django.http import HttpResponseRedirect +from registrar.models.federal_agency import FederalAgency from registrar.utility.admin_helpers import ( get_action_needed_reason_default_email, get_rejection_reason_default_email, @@ -3243,6 +3244,14 @@ def get_readonly_fields(self, request, obj=None): # straightforward and the readonly_fields list can control their behavior readonly_fields.extend([field.name for field in self.model._meta.fields]) + # Make senior_official readonly for federal organizations + if obj and obj.organization_type == obj.OrganizationChoices.FEDERAL: + if "senior_official" not in readonly_fields: + readonly_fields.append("senior_official") + elif "senior_official" in readonly_fields: + # Remove senior_official from readonly_fields if org is non-federal + readonly_fields.remove("senior_official") + if request.user.has_perm("registrar.full_access_permission"): return readonly_fields @@ -3265,12 +3274,11 @@ def change_view(self, request, object_id, form_url="", extra_context=None): extra_context["domain_requests"] = obj.get_domain_requests(order_by=["requested_domain__name"]) return super().change_view(request, object_id, form_url, extra_context) - def save_model(self, request, obj, form, change): - + def save_model(self, request, obj: Portfolio, form, change): if hasattr(obj, "creator") is False: # ---- update creator ---- # Set the creator field to the current admin user - obj.creator = request.user if request.user.is_authenticated else None + obj.creator = request.user if request.user.is_authenticated else None # type: ignore # ---- update organization name ---- # org name will be the same as federal agency, if it is federal, # otherwise it will be the actual org name. If nothing is entered for @@ -3280,12 +3288,19 @@ def save_model(self, request, obj, form, change): if is_federal and obj.organization_name is None: obj.organization_name = obj.federal_agency.agency - # Remove this line when senior_official is no longer readonly in /admin. - if obj.federal_agency: - if obj.federal_agency.so_federal_agency.exists(): - obj.senior_official = obj.federal_agency.so_federal_agency.first() - else: - obj.senior_official = None + # Set the senior official field to the senior official on the federal agency + # when federal - otherwise, clear the field. + if obj.organization_type == obj.OrganizationChoices.FEDERAL: + if obj.federal_agency: + if obj.federal_agency.so_federal_agency.exists(): + obj.senior_official = obj.federal_agency.so_federal_agency.first() + else: + obj.senior_official = None + else: + if obj.federal_agency and obj.federal_agency.agency != "Non-Federal Agency": + if obj.federal_agency.so_federal_agency.first() == obj.senior_official: + obj.senior_official = None + obj.federal_agency = FederalAgency.objects.filter(agency="Non-Federal Agency").first() # type: ignore super().save_model(request, obj, form, change) diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js index ed597d55f..a5c55acb1 100644 --- a/src/registrar/assets/js/get-gov-admin.js +++ b/src/registrar/assets/js/get-gov-admin.js @@ -966,6 +966,7 @@ document.addEventListener('DOMContentLoaded', function() { // This is the additional information that exists beneath the SO element. var contactList = document.querySelector(".field-senior_official .dja-address-contact-list"); + const federalAgencyContainer = document.querySelector(".field-federal_agency"); document.addEventListener('DOMContentLoaded', function() { let isPortfolioPage = document.getElementById("portfolio_form"); @@ -1014,11 +1015,13 @@ document.addEventListener('DOMContentLoaded', function() { let selectedValue = organizationType.value; if (selectedValue === "federal") { hideElement(organizationNameContainer); + showElement(federalAgencyContainer); if (federalType) { showElement(federalType); } } else { showElement(organizationNameContainer); + hideElement(federalAgencyContainer); if (federalType) { hideElement(federalType); } diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py index 9c5e3b582..2677462df 100644 --- a/src/registrar/tests/test_admin.py +++ b/src/registrar/tests/test_admin.py @@ -2103,6 +2103,66 @@ def test_portfolio_members_display(self): display_members = self.admin.display_members(self.portfolio) self.assertIn(f'2 members', display_members) + @less_console_noise_decorator + def test_senior_official_readonly_for_federal_org(self): + """Test that senior_official field is readonly for federal organizations""" + request = self.factory.get("/") + request.user = self.superuser + + # Create a federal portfolio + portfolio = Portfolio.objects.create( + organization_name="Test Federal Org", + organization_type=DomainRequest.OrganizationChoices.FEDERAL, + creator=self.superuser, + ) + + readonly_fields = self.admin.get_readonly_fields(request, portfolio) + self.assertIn("senior_official", readonly_fields) + + # Change to non-federal org + portfolio.organization_type = DomainRequest.OrganizationChoices.CITY + readonly_fields = self.admin.get_readonly_fields(request, portfolio) + self.assertNotIn("senior_official", readonly_fields) + + @less_console_noise_decorator + def test_senior_official_auto_assignment(self): + """Test automatic senior official assignment based on organization type and federal agency""" + request = self.factory.get("/") + request.user = self.superuser + + # Create a federal agency with a senior official + federal_agency = FederalAgency.objects.create(agency="Test Agency") + senior_official = SeniorOfficial.objects.create( + first_name="Test", + last_name="Official", + title="Some guy", + email="test@example.gov", + federal_agency=federal_agency, + ) + + # Create a federal portfolio + portfolio = Portfolio.objects.create( + organization_name="Test Federal Org", + organization_type=DomainRequest.OrganizationChoices.FEDERAL, + creator=self.superuser, + ) + + # Test that the federal org gets senior official from agency when federal + portfolio.federal_agency = federal_agency + self.admin.save_model(request, portfolio, form=None, change=False) + self.assertEqual(portfolio.senior_official, senior_official) + + # Test non-federal org clears senior official when not city + portfolio.organization_type = DomainRequest.OrganizationChoices.CITY + self.admin.save_model(request, portfolio, form=None, change=True) + self.assertIsNone(portfolio.senior_official) + self.assertEqual(portfolio.federal_agency.agency, "Non-Federal Agency") + + # Cleanup + senior_official.delete() + federal_agency.delete() + portfolio.delete() + class TestTransferUser(WebTest): """User transfer custom admin page"""