diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index f0baa9828..f6f9b9373 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -602,15 +602,6 @@ def save(self, forms: list):
if is_suborganization and (sub_organization or requested_suborganization):
# Cleanup the organization name field, as this isn't for suborganizations.
self.domain_request.organization_name = None
-
- # Create or get the Suborganization.
- # Then update the domain_request with the new or existing suborganization
- if not sub_organization:
- sub_organization, created = Suborganization.objects.get_or_create(
- name=cleaned_data.get("requested_suborganization"),
- portfolio=self.domain_request.portfolio,
- )
-
self.domain_request.sub_organization = sub_organization
else:
# If the user doesn't intend to create a suborg, simply don't make one and do some data cleanup
From 514cd8af3125613b56d2c2d81a5fb1a894ad85aa Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 12:52:42 -0600
Subject: [PATCH 06/47] fix unlocking steps
---
src/registrar/forms/domain_request_wizard.py | 1 -
src/registrar/forms/utility/wizard_form_helper.py | 1 -
src/registrar/models/domain_request.py | 10 +++++-----
3 files changed, 5 insertions(+), 7 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index d401360b8..e0aa04483 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -83,7 +83,6 @@ def full_clean(self):
# 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')
- is_suborganization = self.data.get("portfolio_requesting_entity-is_suborganization")
if suborganization:
if "other" in data['portfolio_requesting_entity-sub_organization']:
# Remove the 'other' value
diff --git a/src/registrar/forms/utility/wizard_form_helper.py b/src/registrar/forms/utility/wizard_form_helper.py
index 0a1019aae..eedf5839b 100644
--- a/src/registrar/forms/utility/wizard_form_helper.py
+++ b/src/registrar/forms/utility/wizard_form_helper.py
@@ -261,7 +261,6 @@ def get_typed_choice_field(self):
"required": self.required_error_message,
},
)
- print(f"here are the form choices: {self.form_choices}, here is the initial: {self.get_initial_value()}")
return choice_field
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index e9ba3d7bf..9c990ebd4 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1208,6 +1208,7 @@ def is_suborganization(self) -> bool:
if self.has_information_required_to_make_suborganization():
return True
+
return False
def is_custom_suborganization(self) -> bool:
@@ -1219,11 +1220,10 @@ def is_custom_suborganization(self) -> bool:
def has_information_required_to_make_suborganization(self):
"""Checks if we have all the information we need to create a new suborganization object.
Checks for a the existence of requested_suborganization, suborganization_city, suborganization_state_territory"""
- return (
- self.requested_domain and
- self.suborganization_city and
- self.suborganization_state_territory
- )
+ if self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory:
+ return True
+ else:
+ return False
def to_dict(self):
"""This is to process to_dict for Domain Information, making it friendly
From 263ec02bc32ba1d3e35014c8414c682692317375 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 13:14:03 -0600
Subject: [PATCH 07/47] Add requesting entity to review page
---
.../templates/domain_request_requesting_entity.html | 9 ++++++++-
.../includes/portfolio_request_review_steps.html | 7 ++++---
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/src/registrar/templates/domain_request_requesting_entity.html b/src/registrar/templates/domain_request_requesting_entity.html
index 70db337de..04ceb4a1a 100644
--- a/src/registrar/templates/domain_request_requesting_entity.html
+++ b/src/registrar/templates/domain_request_requesting_entity.html
@@ -22,7 +22,14 @@
{% input_with_errors forms.0.is_suborganization %}
{% endwith %}
{% endwith %}
- {% input_with_errors forms.1.is_custom_suborganization %}
+
+ {% comment %} Add an invisible form element to track whether the custom value "other"
+ was selected or not. This allows for persistence across page reloads without using session variables.
+ {% endcomment %}
+ {% with add_group_class="display-none" %}
+ {% input_with_errors forms.1.is_custom_suborganization %}
+ {% endwith %}
+
Add suborganization information
diff --git a/src/registrar/templates/includes/portfolio_request_review_steps.html b/src/registrar/templates/includes/portfolio_request_review_steps.html
index 9d3c5bdeb..dd0099ec7 100644
--- a/src/registrar/templates/includes/portfolio_request_review_steps.html
+++ b/src/registrar/templates/includes/portfolio_request_review_steps.html
@@ -8,9 +8,10 @@
{% endif %}
{% if step == Step.REQUESTING_ENTITY %}
- {% if domain_request.organization_name %}
- {% with title=form_titles|get_item:step value=domain_request %}
- {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url address='true' %}
+ {% if domain_request.requested_suborganization and domain_request.suborganization_city and domain_request.suborganization_state_territory %}
+ {% with title=form_titles|get_item:step value=domain_request.requested_suborganization %}
+ {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
+
{{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
{% endwith %}
{% else %}
{% with title=form_titles|get_item:step value="
Incomplete "|safe %}
From 086d1977b8f24ed77f9dc127305aaf0e98ac54b4 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 13:15:14 -0600
Subject: [PATCH 08/47] migration
---
.../0134_domainrequest_requested_suborganization_and_more.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py b/src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py
index bc7235a41..6070b4a82 100644
--- a/src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py
+++ b/src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.10 on 2024-10-23 15:01
+# Generated by Django 4.2.10 on 2024-10-23 19:15
from django.db import migrations, models
From 5b629bd7edd7d4d9afabfb45a6b2cc8430acdfcc Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 13:15:41 -0600
Subject: [PATCH 09/47] Delete
0134_domainrequest_requested_suborganization_and_more.py
---
...uest_requested_suborganization_and_more.py | 95 -------------------
1 file changed, 95 deletions(-)
delete mode 100644 src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py
diff --git a/src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py b/src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py
deleted file mode 100644
index 6070b4a82..000000000
--- a/src/registrar/migrations/0134_domainrequest_requested_suborganization_and_more.py
+++ /dev/null
@@ -1,95 +0,0 @@
-# Generated by Django 4.2.10 on 2024-10-23 19:15
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("registrar", "0133_domainrequest_rejection_reason_email_and_more"),
- ]
-
- operations = [
- migrations.AddField(
- model_name="domainrequest",
- name="requested_suborganization",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domainrequest",
- name="suborganization_city",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domainrequest",
- name="suborganization_state_territory",
- field=models.CharField(
- blank=True,
- choices=[
- ("AL", "Alabama (AL)"),
- ("AK", "Alaska (AK)"),
- ("AS", "American Samoa (AS)"),
- ("AZ", "Arizona (AZ)"),
- ("AR", "Arkansas (AR)"),
- ("CA", "California (CA)"),
- ("CO", "Colorado (CO)"),
- ("CT", "Connecticut (CT)"),
- ("DE", "Delaware (DE)"),
- ("DC", "District of Columbia (DC)"),
- ("FL", "Florida (FL)"),
- ("GA", "Georgia (GA)"),
- ("GU", "Guam (GU)"),
- ("HI", "Hawaii (HI)"),
- ("ID", "Idaho (ID)"),
- ("IL", "Illinois (IL)"),
- ("IN", "Indiana (IN)"),
- ("IA", "Iowa (IA)"),
- ("KS", "Kansas (KS)"),
- ("KY", "Kentucky (KY)"),
- ("LA", "Louisiana (LA)"),
- ("ME", "Maine (ME)"),
- ("MD", "Maryland (MD)"),
- ("MA", "Massachusetts (MA)"),
- ("MI", "Michigan (MI)"),
- ("MN", "Minnesota (MN)"),
- ("MS", "Mississippi (MS)"),
- ("MO", "Missouri (MO)"),
- ("MT", "Montana (MT)"),
- ("NE", "Nebraska (NE)"),
- ("NV", "Nevada (NV)"),
- ("NH", "New Hampshire (NH)"),
- ("NJ", "New Jersey (NJ)"),
- ("NM", "New Mexico (NM)"),
- ("NY", "New York (NY)"),
- ("NC", "North Carolina (NC)"),
- ("ND", "North Dakota (ND)"),
- ("MP", "Northern Mariana Islands (MP)"),
- ("OH", "Ohio (OH)"),
- ("OK", "Oklahoma (OK)"),
- ("OR", "Oregon (OR)"),
- ("PA", "Pennsylvania (PA)"),
- ("PR", "Puerto Rico (PR)"),
- ("RI", "Rhode Island (RI)"),
- ("SC", "South Carolina (SC)"),
- ("SD", "South Dakota (SD)"),
- ("TN", "Tennessee (TN)"),
- ("TX", "Texas (TX)"),
- ("UM", "United States Minor Outlying Islands (UM)"),
- ("UT", "Utah (UT)"),
- ("VT", "Vermont (VT)"),
- ("VI", "Virgin Islands (VI)"),
- ("VA", "Virginia (VA)"),
- ("WA", "Washington (WA)"),
- ("WV", "West Virginia (WV)"),
- ("WI", "Wisconsin (WI)"),
- ("WY", "Wyoming (WY)"),
- ("AA", "Armed Forces Americas (AA)"),
- ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
- ("AP", "Armed Forces Pacific (AP)"),
- ],
- max_length=2,
- null=True,
- verbose_name="state, territory, or military post",
- ),
- ),
- ]
From 0374efb778eb1e01b47c9cd4e19e2200c1c791a5 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 13:16:05 -0600
Subject: [PATCH 10/47] Fix merge conflict
---
...uest_requested_suborganization_and_more.py | 98 +++++++++++++++++++
1 file changed, 98 insertions(+)
create mode 100644 src/registrar/migrations/0135_domainrequest_requested_suborganization_and_more.py
diff --git a/src/registrar/migrations/0135_domainrequest_requested_suborganization_and_more.py b/src/registrar/migrations/0135_domainrequest_requested_suborganization_and_more.py
new file mode 100644
index 000000000..92ca32697
--- /dev/null
+++ b/src/registrar/migrations/0135_domainrequest_requested_suborganization_and_more.py
@@ -0,0 +1,98 @@
+# Generated by Django 4.2.10 on 2024-10-23 19:15
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ (
+ "registrar",
+ "0134_rename_portfolio_additional_permissions_portfolioinvitation_additional_permissions_and_more",
+ ),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="domainrequest",
+ name="requested_suborganization",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domainrequest",
+ name="suborganization_city",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domainrequest",
+ name="suborganization_state_territory",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ ("AL", "Alabama (AL)"),
+ ("AK", "Alaska (AK)"),
+ ("AS", "American Samoa (AS)"),
+ ("AZ", "Arizona (AZ)"),
+ ("AR", "Arkansas (AR)"),
+ ("CA", "California (CA)"),
+ ("CO", "Colorado (CO)"),
+ ("CT", "Connecticut (CT)"),
+ ("DE", "Delaware (DE)"),
+ ("DC", "District of Columbia (DC)"),
+ ("FL", "Florida (FL)"),
+ ("GA", "Georgia (GA)"),
+ ("GU", "Guam (GU)"),
+ ("HI", "Hawaii (HI)"),
+ ("ID", "Idaho (ID)"),
+ ("IL", "Illinois (IL)"),
+ ("IN", "Indiana (IN)"),
+ ("IA", "Iowa (IA)"),
+ ("KS", "Kansas (KS)"),
+ ("KY", "Kentucky (KY)"),
+ ("LA", "Louisiana (LA)"),
+ ("ME", "Maine (ME)"),
+ ("MD", "Maryland (MD)"),
+ ("MA", "Massachusetts (MA)"),
+ ("MI", "Michigan (MI)"),
+ ("MN", "Minnesota (MN)"),
+ ("MS", "Mississippi (MS)"),
+ ("MO", "Missouri (MO)"),
+ ("MT", "Montana (MT)"),
+ ("NE", "Nebraska (NE)"),
+ ("NV", "Nevada (NV)"),
+ ("NH", "New Hampshire (NH)"),
+ ("NJ", "New Jersey (NJ)"),
+ ("NM", "New Mexico (NM)"),
+ ("NY", "New York (NY)"),
+ ("NC", "North Carolina (NC)"),
+ ("ND", "North Dakota (ND)"),
+ ("MP", "Northern Mariana Islands (MP)"),
+ ("OH", "Ohio (OH)"),
+ ("OK", "Oklahoma (OK)"),
+ ("OR", "Oregon (OR)"),
+ ("PA", "Pennsylvania (PA)"),
+ ("PR", "Puerto Rico (PR)"),
+ ("RI", "Rhode Island (RI)"),
+ ("SC", "South Carolina (SC)"),
+ ("SD", "South Dakota (SD)"),
+ ("TN", "Tennessee (TN)"),
+ ("TX", "Texas (TX)"),
+ ("UM", "United States Minor Outlying Islands (UM)"),
+ ("UT", "Utah (UT)"),
+ ("VT", "Vermont (VT)"),
+ ("VI", "Virgin Islands (VI)"),
+ ("VA", "Virginia (VA)"),
+ ("WA", "Washington (WA)"),
+ ("WV", "West Virginia (WV)"),
+ ("WI", "Wisconsin (WI)"),
+ ("WY", "Wyoming (WY)"),
+ ("AA", "Armed Forces Americas (AA)"),
+ ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
+ ("AP", "Armed Forces Pacific (AP)"),
+ ],
+ max_length=2,
+ null=True,
+ verbose_name="state, territory, or military post",
+ ),
+ ),
+ ]
From 939b11ecee431d376ad5b31cd67835d9e8c027a9 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 13:54:53 -0600
Subject: [PATCH 11/47] Fix edge cases for review page and add fields to admin
---
src/registrar/admin.py | 3 +++
.../includes/portfolio_request_review_steps.html | 14 +++++++++++---
2 files changed, 14 insertions(+), 3 deletions(-)
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index 0b96b4c48..a4117e232 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -1748,6 +1748,9 @@ def status_history(self, obj):
"fields": [
"portfolio",
"sub_organization",
+ "requested_suborganization",
+ "suborganization_city",
+ "suborganization_state_territory",
"status_history",
"status",
"rejection_reason",
diff --git a/src/registrar/templates/includes/portfolio_request_review_steps.html b/src/registrar/templates/includes/portfolio_request_review_steps.html
index dd0099ec7..3f00f911c 100644
--- a/src/registrar/templates/includes/portfolio_request_review_steps.html
+++ b/src/registrar/templates/includes/portfolio_request_review_steps.html
@@ -8,16 +8,24 @@
{% endif %}
{% if step == Step.REQUESTING_ENTITY %}
- {% if domain_request.requested_suborganization and domain_request.suborganization_city and domain_request.suborganization_state_territory %}
+ {% if domain_request.portfolio and domain_request.organization_name == domain_request.portfolio.organization_name %}
+ {% with title=form_titles|get_item:step value=domain_request.portfolio.organization_name %}
+ {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
+ {% endwith %}
+ {% elif domain_request.sub_organization %}
+ {% with title=form_titles|get_item:step value=domain_request.sub_organization %}
+ {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
+ {% endwith %}
+ {% elif domain_request.requested_suborganization and domain_request.suborganization_city and domain_request.suborganization_state_territory %}
{% with title=form_titles|get_item:step value=domain_request.requested_suborganization %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
-
{{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
{% endwith %}
+
{{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
{% else %}
{% with title=form_titles|get_item:step value="
Incomplete "|safe %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
{% endwith %}
- {% endif%}
+ {% endif %}
{% endif %}
{% if step == Step.CURRENT_SITES %}
From 150a59cdfc7a44ebd4ce68a92015aed6be55ede9 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 14:02:31 -0600
Subject: [PATCH 12/47] Update portfolio_request_review_steps.html
---
.../templates/includes/portfolio_request_review_steps.html | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/registrar/templates/includes/portfolio_request_review_steps.html b/src/registrar/templates/includes/portfolio_request_review_steps.html
index 3f00f911c..c2bdb18cc 100644
--- a/src/registrar/templates/includes/portfolio_request_review_steps.html
+++ b/src/registrar/templates/includes/portfolio_request_review_steps.html
@@ -12,10 +12,14 @@
{% with title=form_titles|get_item:step value=domain_request.portfolio.organization_name %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
{% endwith %}
+ {% if domain_request.portfolio.city and domain_request.portfolio.state_territory %}
+
{{domain_request.portfolio.city}}, {{domain_request.portfolio.state_territory}}
+ {% endif %}
{% elif domain_request.sub_organization %}
{% with title=form_titles|get_item:step value=domain_request.sub_organization %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
{% endwith %}
+ {% comment %} We don't have city or state_territory for suborganizations yet, so no data should display {% endcomment %}
{% elif domain_request.requested_suborganization and domain_request.suborganization_city and domain_request.suborganization_state_territory %}
{% with title=form_titles|get_item:step value=domain_request.requested_suborganization %}
{% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
From 5cb3298afd89fd5493a4ed05246cd01128ecd87a Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 14:29:26 -0600
Subject: [PATCH 13/47] Update email content, update manage button content
---
.../templates/domain_request_status.html | 6 ++-
.../portfolio_domain_request_summary.txt | 41 +++++++++++++++++++
.../emails/submission_confirmation.txt | 7 +++-
.../includes/portfolio_status_manage.html | 7 ++++
src/registrar/views/domain_request.py | 10 +++++
5 files changed, 68 insertions(+), 3 deletions(-)
create mode 100644 src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
create mode 100644 src/registrar/templates/includes/portfolio_status_manage.html
diff --git a/src/registrar/templates/domain_request_status.html b/src/registrar/templates/domain_request_status.html
index d332ce54e..d98220532 100644
--- a/src/registrar/templates/domain_request_status.html
+++ b/src/registrar/templates/domain_request_status.html
@@ -6,5 +6,9 @@
{% block content %}
- {% include "includes/request_status_manage.html" %}
+ {% if not portfolio %}
+ {% include "includes/request_status_manage.html" %}
+ {% else %}
+ {% include "includes/portfolio_status_manage.html" %}
+ {% endif %}
{% endblock %}
diff --git a/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
new file mode 100644
index 000000000..e0b62f93e
--- /dev/null
+++ b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
@@ -0,0 +1,41 @@
+SUMMARY OF YOUR DOMAIN REQUEST
+
+Requesting entity:
+{% if domain_request.portfolio and domain_request.organization_name == domain_request.portfolio.organization_name %}
+ {{domain_request.portfolio.organization_name}}
+ {{domain_request.portfolio.city}}, {{domain_request.portfolio.state_territory}}
+{% elif domain_request.sub_organization %}
+ {{domain_request.sub_organization}}
+ {% comment %} We don't have city or state_territory for suborganizations yet, so no data should display {% endcomment %}
+{% elif domain_request.requested_suborganization and domain_request.suborganization_city and domain_request.suborganization_state_territory %}
+ {{domain_request.requested_suborganization}}
+ {{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
+{% endif %}
+Alternative domains:
+{% for site in domain_request.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
+{% endfor %}{% endif %}
+Current websites: {% for site in domain_request.current_websites.all %}
+{% spaceless %}{{ site.website }}{% endspaceless %}
+{% endfor %}{% endif %}
+.gov domain:
+{{ domain_request.requested_domain.name }}
+{% if domain_request.alternative_domains.all %}
+Alternative domains:
+{% for site in domain_request.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
+{% endfor %}{% endif %}
+Purpose of your domain:
+{{ domain_request.purpose }}
+Additional details:
+{{ domain_request.additional_details }}
+
+Your contact information:
+{% spaceless %}{% include "emails/includes/contact.txt" with contact=recipient %}{% endspaceless %}
+
+Other employees from your organization:{% for other in domain_request.other_contacts.all %}
+{% spaceless %}{% include "emails/includes/contact.txt" with contact=other %}{% endspaceless %}
+{% empty %}
+{{ domain_request.no_other_contacts_rationale }}
+{% endfor %}{% if domain_request.anything_else %}
+Anything else?
+{{ domain_request.anything_else }}
+{% endif %}
diff --git a/src/registrar/templates/emails/submission_confirmation.txt b/src/registrar/templates/emails/submission_confirmation.txt
index c8ff4c7eb..2b51127f2 100644
--- a/src/registrar/templates/emails/submission_confirmation.txt
+++ b/src/registrar/templates/emails/submission_confirmation.txt
@@ -30,8 +30,11 @@ THANK YOU
.Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain.
----------------------------------------------------------------
-
-{% include 'emails/includes/domain_request_summary.txt' %}
+{% if portfolio %}
+ {% include 'emails/includes/portfolio_domain_request_summary.txt' %}
+{% else %}
+ {% include 'emails/includes/domain_request_summary.txt' %}
+{% endif %}
----------------------------------------------------------------
The .gov team
diff --git a/src/registrar/templates/includes/portfolio_status_manage.html b/src/registrar/templates/includes/portfolio_status_manage.html
new file mode 100644
index 000000000..2f2bda8b3
--- /dev/null
+++ b/src/registrar/templates/includes/portfolio_status_manage.html
@@ -0,0 +1,7 @@
+{% extends "includes/request_status_manage.html" %}
+{% load custom_filters %}
+{% load static url_helpers %}
+
+{% block request_summary %}
+ {% include "includes/portfolio_request_review_steps.html" with is_editable=False domain_request=DomainRequest %}
+{% endblock request_summary %}
\ No newline at end of file
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index cf8c56303..e88eb4c75 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -857,6 +857,16 @@ def has_permission(self):
return True
+ def get_context_data(self, **kwargs):
+ 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
+ return context
+
class DomainRequestWithdrawConfirmation(DomainRequestPermissionWithdrawView):
"""This page will ask user to confirm if they want to withdraw
From bde6c5e835e75f670975187eb243a398a406722d Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 23 Oct 2024 15:38:25 -0600
Subject: [PATCH 14/47] check for org on email
---
src/registrar/models/domain_request.py | 6 ++++++
src/registrar/templates/emails/submission_confirmation.txt | 2 +-
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index faa02a3c7..33a9f2dea 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -6,6 +6,7 @@
from django.db import models
from django_fsm import FSMField, transition # type: ignore
from django.utils import timezone
+from waffle import flag_is_active
from registrar.models.domain import Domain
from registrar.models.federal_agency import FederalAgency
from registrar.models.utility.generic_helper import CreateOrUpdateOrganizationTypeHelper
@@ -13,6 +14,8 @@
from registrar.utility.constants import BranchChoices
from auditlog.models import LogEntry
+from registrar.utility.waffle import flag_is_active_for_user
+
from .utility.time_stamped_model import TimeStampedModel
from ..utility.email import send_templated_email, EmailSendingError
from itertools import chain
@@ -841,10 +844,13 @@ def _send_status_update_email(
try:
if not context:
+ has_organization_feature_flag = flag_is_active_for_user(recipient, "organization_feature")
+ is_org_user = has_organization_feature_flag and recipient.has_base_portfolio_permission(self.portfolio)
context = {
"domain_request": self,
# This is the user that we refer to in the email
"recipient": recipient,
+ "is_org_user": is_org_user,
}
if custom_email_content:
diff --git a/src/registrar/templates/emails/submission_confirmation.txt b/src/registrar/templates/emails/submission_confirmation.txt
index 2b51127f2..f4acae7e9 100644
--- a/src/registrar/templates/emails/submission_confirmation.txt
+++ b/src/registrar/templates/emails/submission_confirmation.txt
@@ -30,7 +30,7 @@ THANK YOU
.Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain.
----------------------------------------------------------------
-{% if portfolio %}
+{% if is_org_user %}
{% include 'emails/includes/portfolio_domain_request_summary.txt' %}
{% else %}
{% include 'emails/includes/domain_request_summary.txt' %}
From 5e23ebead50281aaa2928cbfe3a710e300fbc390 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 24 Oct 2024 08:38:58 -0600
Subject: [PATCH 15/47] Fix some tests
---
src/.pa11yci | 1 +
src/registrar/templates/emails/submission_confirmation.txt | 1 +
src/registrar/templates/includes/portfolio_status_manage.html | 2 +-
src/registrar/tests/test_admin_request.py | 3 +++
4 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/.pa11yci b/src/.pa11yci
index c18704c07..bff786de5 100644
--- a/src/.pa11yci
+++ b/src/.pa11yci
@@ -20,6 +20,7 @@
"http://localhost:8080/request/anything_else/",
"http://localhost:8080/request/requirements/",
"http://localhost:8080/request/finished/",
+ "http://localhost:8080/request/requesting_entity/",
"http://localhost:8080/user-profile/"
]
}
diff --git a/src/registrar/templates/emails/submission_confirmation.txt b/src/registrar/templates/emails/submission_confirmation.txt
index f4acae7e9..911269bf1 100644
--- a/src/registrar/templates/emails/submission_confirmation.txt
+++ b/src/registrar/templates/emails/submission_confirmation.txt
@@ -30,6 +30,7 @@ THANK YOU
.Gov helps the public identify official, trusted information. Thank you for requesting a .gov domain.
----------------------------------------------------------------
+
{% if is_org_user %}
{% include 'emails/includes/portfolio_domain_request_summary.txt' %}
{% else %}
diff --git a/src/registrar/templates/includes/portfolio_status_manage.html b/src/registrar/templates/includes/portfolio_status_manage.html
index 2f2bda8b3..120770156 100644
--- a/src/registrar/templates/includes/portfolio_status_manage.html
+++ b/src/registrar/templates/includes/portfolio_status_manage.html
@@ -4,4 +4,4 @@
{% block request_summary %}
{% include "includes/portfolio_request_review_steps.html" with is_editable=False domain_request=DomainRequest %}
-{% endblock request_summary %}
\ No newline at end of file
+{% endblock request_summary %}
diff --git a/src/registrar/tests/test_admin_request.py b/src/registrar/tests/test_admin_request.py
index 217756359..4b65c401d 100644
--- a/src/registrar/tests/test_admin_request.py
+++ b/src/registrar/tests/test_admin_request.py
@@ -1642,6 +1642,9 @@ def test_readonly_when_restricted_creator(self):
"federal_agency",
"portfolio",
"sub_organization",
+ "requested_suborganization",
+ "suborganization_city",
+ "suborganization_state_territory",
"creator",
"investigator",
"generic_org_type",
From d9ec108f5800215a811e5162280ba90e0c3759aa Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 24 Oct 2024 08:49:01 -0600
Subject: [PATCH 16/47] fix emails
---
.../emails/includes/portfolio_domain_request_summary.txt | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
index e0b62f93e..e1dff27ed 100644
--- a/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
+++ b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
@@ -11,9 +11,7 @@ Requesting entity:
{{domain_request.requested_suborganization}}
{{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
{% endif %}
-Alternative domains:
-{% for site in domain_request.alternative_domains.all %}{% spaceless %}{{ site.website }}{% endspaceless %}
-{% endfor %}{% endif %}
+{% if domain_request.current_websites.exists %}
Current websites: {% for site in domain_request.current_websites.all %}
{% spaceless %}{{ site.website }}{% endspaceless %}
{% endfor %}{% endif %}
@@ -25,8 +23,10 @@ Alternative domains:
{% endfor %}{% endif %}
Purpose of your domain:
{{ domain_request.purpose }}
+{% if domain_request.additional_details%}
Additional details:
{{ domain_request.additional_details }}
+{% endif %}
Your contact information:
{% spaceless %}{% include "emails/includes/contact.txt" with contact=recipient %}{% endspaceless %}
From 87d51c10028fa1525090d1f05c01a0a41c2d841d Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 24 Oct 2024 10:26:59 -0600
Subject: [PATCH 17/47] Fix bugs with email
---
src/registrar/models/domain_request.py | 11 ++++--
src/registrar/models/portfolio.py | 13 +++++++
.../portfolio_domain_request_summary.txt | 34 ++++++-------------
src/registrar/templatetags/custom_filters.py | 24 +++++++++++++
4 files changed, 57 insertions(+), 25 deletions(-)
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index 33a9f2dea..68db5fb67 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1205,7 +1205,8 @@ def is_federal(self) -> Union[bool, None]:
return False
def is_suborganization(self) -> bool:
- """Determines if this record is a suborganization or not"""
+ """Determines if this record is a suborganization or not by checking if a suborganization exists,
+ and if it doesn't, determining if properties like requested_suborganization exist."""
if self.portfolio:
if self.sub_organization:
return True
@@ -1216,12 +1217,18 @@ def is_suborganization(self) -> bool:
return False
def is_custom_suborganization(self) -> bool:
+ """Used on the requesting entity form to determine if a user is trying to request
+ a new suborganization using the domain request form.
+
+ This only occurs when no suborganization is selected, but they've filled out
+ the requested_suborganization, suborganization_city, and suborganization_state_territory fields.
+ """
if self.is_suborganization():
return not self.sub_organization and self.has_information_required_to_make_suborganization()
else:
return False
- def has_information_required_to_make_suborganization(self):
+ def has_information_required_to_make_suborganization(self) -> bool:
"""Checks if we have all the information we need to create a new suborganization object.
Checks for a the existence of requested_suborganization, suborganization_city, suborganization_state_territory"""
if self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory:
diff --git a/src/registrar/models/portfolio.py b/src/registrar/models/portfolio.py
index 8d820e105..82afcd4d6 100644
--- a/src/registrar/models/portfolio.py
+++ b/src/registrar/models/portfolio.py
@@ -2,6 +2,8 @@
from registrar.models.domain_request import DomainRequest
from registrar.models.federal_agency import FederalAgency
+from registrar.models.user import User
+from registrar.models.utility.portfolio_helper import UserPortfolioRoleChoices
from .utility.time_stamped_model import TimeStampedModel
@@ -131,6 +133,17 @@ def federal_type(self):
def get_federal_type(cls, federal_agency):
return federal_agency.federal_type if federal_agency else None
+ @property
+ def portfolio_admin_users(self):
+ """Gets all users with the role organization_admin for this particular portfolio.
+ Returns a queryset of User."""
+ admin_ids = self.portfolio_users.filter(
+ roles__overlap=[
+ UserPortfolioRoleChoices.ORGANIZATION_ADMIN,
+ ],
+ ).values_list("user__id", flat=True)
+ return User.objects.filter(id__in=admin_ids)
+
# == Getters for domains == #
def get_domains(self, order_by=None):
"""Returns all DomainInformations associated with this portfolio"""
diff --git a/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
index e1dff27ed..f2486dbe1 100644
--- a/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
+++ b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
@@ -1,16 +1,7 @@
-SUMMARY OF YOUR DOMAIN REQUEST
+{% load custom_filters %}SUMMARY OF YOUR DOMAIN REQUEST
-Requesting entity:
-{% if domain_request.portfolio and domain_request.organization_name == domain_request.portfolio.organization_name %}
- {{domain_request.portfolio.organization_name}}
- {{domain_request.portfolio.city}}, {{domain_request.portfolio.state_territory}}
-{% elif domain_request.sub_organization %}
- {{domain_request.sub_organization}}
- {% comment %} We don't have city or state_territory for suborganizations yet, so no data should display {% endcomment %}
-{% elif domain_request.requested_suborganization and domain_request.suborganization_city and domain_request.suborganization_state_territory %}
- {{domain_request.requested_suborganization}}
- {{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
-{% endif %}
+Requesting entity: {# if blockmakes a newline #}
+{{ domain_request|display_requesting_entity }}
{% if domain_request.current_websites.exists %}
Current websites: {% for site in domain_request.current_websites.all %}
{% spaceless %}{{ site.website }}{% endspaceless %}
@@ -23,19 +14,16 @@ Alternative domains:
{% endfor %}{% endif %}
Purpose of your domain:
{{ domain_request.purpose }}
-{% if domain_request.additional_details%}
+{% if domain_request.anything_else %}
Additional details:
-{{ domain_request.additional_details }}
+{{ domain_request.anything_else }}
{% endif %}
-
+{% if recipient %}
Your contact information:
{% spaceless %}{% include "emails/includes/contact.txt" with contact=recipient %}{% endspaceless %}
-
-Other employees from your organization:{% for other in domain_request.other_contacts.all %}
-{% spaceless %}{% include "emails/includes/contact.txt" with contact=other %}{% endspaceless %}
-{% empty %}
-{{ domain_request.no_other_contacts_rationale }}
-{% endfor %}{% if domain_request.anything_else %}
-Anything else?
-{{ domain_request.anything_else }}
{% endif %}
+
+Administrators from your organization:{% for admin in domain_request.portfolio.portfolio_admin_users %}
+{% spaceless %}{% if admin != recipient %}{% include "emails/includes/contact.txt" with contact=admin %}{% endif %}{% endspaceless %}
+{% endfor %}
+
diff --git a/src/registrar/templatetags/custom_filters.py b/src/registrar/templatetags/custom_filters.py
index b29dccb08..9e7298ac6 100644
--- a/src/registrar/templatetags/custom_filters.py
+++ b/src/registrar/templatetags/custom_filters.py
@@ -257,3 +257,27 @@ def portfolio_role_summary(user, portfolio):
return user.portfolio_role_summary(portfolio)
else:
return []
+
+@register.filter(name="display_requesting_entity")
+def display_requesting_entity(domain_request):
+ """Workaround for a newline issue in .txt files (our emails) as if statements
+ count as a newline to the file.
+ Will output something that looks like:
+ MyOrganizationName
+ Boise, ID
+ """
+ display = ""
+ if domain_request.portfolio and domain_request.organization_name == domain_request.portfolio.organization_name:
+ display = (
+ f"{domain_request.portfolio.organization_name}\n"
+ f"{domain_request.portfolio.city}, {domain_request.portfolio.state_territory}"
+ )
+ elif domain_request.sub_organization:
+ display = domain_request.sub_organization
+ elif domain_request.has_information_required_to_make_suborganization():
+ display = (
+ f"{domain_request.requested_suborganization}\n"
+ f"{domain_request.suborganization_city}, {domain_request.suborganization_state_territory}"
+ )
+
+ return display
From 178d12711a13e25faf6b286a13e1eb5cea3dc2be Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 24 Oct 2024 10:32:15 -0600
Subject: [PATCH 18/47] linting + add fields to django admin
---
src/registrar/admin.py | 20 ++++-
src/registrar/forms/domain_request_wizard.py | 25 ++++--
...ion_requested_suborganization_and_more.py} | 84 ++++++++++++++++++-
src/registrar/models/domain_information.py | 18 ++++
src/registrar/models/domain_request.py | 11 +--
src/registrar/templatetags/custom_filters.py | 1 +
src/registrar/views/domain_request.py | 1 +
7 files changed, 143 insertions(+), 17 deletions(-)
rename src/registrar/migrations/{0135_domainrequest_requested_suborganization_and_more.py => 0135_domaininformation_requested_suborganization_and_more.py} (52%)
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index a4117e232..913820a14 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -1478,7 +1478,21 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
search_help_text = "Search by domain."
fieldsets = [
- (None, {"fields": ["portfolio", "sub_organization", "creator", "domain_request", "notes"]}),
+ (
+ None,
+ {
+ "fields": [
+ "portfolio",
+ "sub_organization",
+ "requested_suborganization",
+ "suborganization_city",
+ "suborganization_state_territory",
+ "creator",
+ "domain_request",
+ "notes",
+ ]
+ },
+ ),
(".gov domain", {"fields": ["domain"]}),
("Contacts", {"fields": ["senior_official", "other_contacts", "no_other_contacts_rationale"]}),
("Background info", {"fields": ["anything_else"]}),
@@ -1748,8 +1762,8 @@ def status_history(self, obj):
"fields": [
"portfolio",
"sub_organization",
- "requested_suborganization",
- "suborganization_city",
+ "requested_suborganization",
+ "suborganization_city",
"suborganization_state_territory",
"status_history",
"status",
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index a8fa19146..c7b962f60 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -68,7 +68,9 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.domain_request.portfolio:
- self.fields["sub_organization"].queryset = Suborganization.objects.filter(portfolio=self.domain_request.portfolio)
+ self.fields["sub_organization"].queryset = Suborganization.objects.filter(
+ portfolio=self.domain_request.portfolio
+ )
def clean_sub_organization(self):
sub_organization = self.cleaned_data.get("sub_organization")
@@ -82,15 +84,15 @@ def clean_sub_organization(self):
def full_clean(self):
# 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')
+ suborganization = self.data.get("portfolio_requesting_entity-sub_organization")
if suborganization:
- if "other" in data['portfolio_requesting_entity-sub_organization']:
+ if "other" in data["portfolio_requesting_entity-sub_organization"]:
# Remove the 'other' value
- data['portfolio_requesting_entity-sub_organization'] = ""
-
+ data["portfolio_requesting_entity-sub_organization"] = ""
+
# Set the modified data back to the form
self.data = data
-
+
# Call the parent's full_clean method
super().full_clean()
@@ -129,7 +131,10 @@ def __init__(self, *args, **kwargs):
"""Extend the initialization of the form from RegistrarForm __init__"""
super().__init__(*args, **kwargs)
if self.domain_request.portfolio:
- self.form_choices = ((False, self.domain_request.portfolio), (True, "A suborganization. (choose from list)"))
+ self.form_choices = (
+ (False, self.domain_request.portfolio),
+ (True, "A suborganization. (choose from list)"),
+ )
self.fields[self.field_name] = self.get_typed_choice_field()
@property
@@ -138,13 +143,17 @@ def form_is_checked(self):
Determines the initial checked state of the form based on the domain_request's attributes.
"""
- if self.domain_request.portfolio and self.domain_request.organization_name == self.domain_request.portfolio.organization_name:
+ if (
+ self.domain_request.portfolio
+ and self.domain_request.organization_name == self.domain_request.portfolio.organization_name
+ ):
return False
elif self.domain_request.is_suborganization():
return True
else:
return None
+
class OrganizationTypeForm(RegistrarForm):
generic_org_type = forms.ChoiceField(
# use the long names in the domain request form
diff --git a/src/registrar/migrations/0135_domainrequest_requested_suborganization_and_more.py b/src/registrar/migrations/0135_domaininformation_requested_suborganization_and_more.py
similarity index 52%
rename from src/registrar/migrations/0135_domainrequest_requested_suborganization_and_more.py
rename to src/registrar/migrations/0135_domaininformation_requested_suborganization_and_more.py
index 92ca32697..b82619c78 100644
--- a/src/registrar/migrations/0135_domainrequest_requested_suborganization_and_more.py
+++ b/src/registrar/migrations/0135_domaininformation_requested_suborganization_and_more.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.10 on 2024-10-23 19:15
+# Generated by Django 4.2.10 on 2024-10-24 16:30
from django.db import migrations, models
@@ -13,6 +13,88 @@ class Migration(migrations.Migration):
]
operations = [
+ migrations.AddField(
+ model_name="domaininformation",
+ name="requested_suborganization",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domaininformation",
+ name="suborganization_city",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domaininformation",
+ name="suborganization_state_territory",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ ("AL", "Alabama (AL)"),
+ ("AK", "Alaska (AK)"),
+ ("AS", "American Samoa (AS)"),
+ ("AZ", "Arizona (AZ)"),
+ ("AR", "Arkansas (AR)"),
+ ("CA", "California (CA)"),
+ ("CO", "Colorado (CO)"),
+ ("CT", "Connecticut (CT)"),
+ ("DE", "Delaware (DE)"),
+ ("DC", "District of Columbia (DC)"),
+ ("FL", "Florida (FL)"),
+ ("GA", "Georgia (GA)"),
+ ("GU", "Guam (GU)"),
+ ("HI", "Hawaii (HI)"),
+ ("ID", "Idaho (ID)"),
+ ("IL", "Illinois (IL)"),
+ ("IN", "Indiana (IN)"),
+ ("IA", "Iowa (IA)"),
+ ("KS", "Kansas (KS)"),
+ ("KY", "Kentucky (KY)"),
+ ("LA", "Louisiana (LA)"),
+ ("ME", "Maine (ME)"),
+ ("MD", "Maryland (MD)"),
+ ("MA", "Massachusetts (MA)"),
+ ("MI", "Michigan (MI)"),
+ ("MN", "Minnesota (MN)"),
+ ("MS", "Mississippi (MS)"),
+ ("MO", "Missouri (MO)"),
+ ("MT", "Montana (MT)"),
+ ("NE", "Nebraska (NE)"),
+ ("NV", "Nevada (NV)"),
+ ("NH", "New Hampshire (NH)"),
+ ("NJ", "New Jersey (NJ)"),
+ ("NM", "New Mexico (NM)"),
+ ("NY", "New York (NY)"),
+ ("NC", "North Carolina (NC)"),
+ ("ND", "North Dakota (ND)"),
+ ("MP", "Northern Mariana Islands (MP)"),
+ ("OH", "Ohio (OH)"),
+ ("OK", "Oklahoma (OK)"),
+ ("OR", "Oregon (OR)"),
+ ("PA", "Pennsylvania (PA)"),
+ ("PR", "Puerto Rico (PR)"),
+ ("RI", "Rhode Island (RI)"),
+ ("SC", "South Carolina (SC)"),
+ ("SD", "South Dakota (SD)"),
+ ("TN", "Tennessee (TN)"),
+ ("TX", "Texas (TX)"),
+ ("UM", "United States Minor Outlying Islands (UM)"),
+ ("UT", "Utah (UT)"),
+ ("VT", "Vermont (VT)"),
+ ("VI", "Virgin Islands (VI)"),
+ ("VA", "Virginia (VA)"),
+ ("WA", "Washington (WA)"),
+ ("WV", "West Virginia (WV)"),
+ ("WI", "Wisconsin (WI)"),
+ ("WY", "Wyoming (WY)"),
+ ("AA", "Armed Forces Americas (AA)"),
+ ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
+ ("AP", "Armed Forces Pacific (AP)"),
+ ],
+ max_length=2,
+ null=True,
+ verbose_name="state, territory, or military post",
+ ),
+ ),
migrations.AddField(
model_name="domainrequest",
name="requested_suborganization",
diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py
index 5f98197bd..a00098d66 100644
--- a/src/registrar/models/domain_information.py
+++ b/src/registrar/models/domain_information.py
@@ -75,6 +75,24 @@ class Meta:
verbose_name="Suborganization",
)
+ requested_suborganization = models.CharField(
+ null=True,
+ blank=True,
+ )
+
+ suborganization_city = models.CharField(
+ null=True,
+ blank=True,
+ )
+
+ suborganization_state_territory = models.CharField(
+ max_length=2,
+ choices=StateTerritoryChoices.choices,
+ null=True,
+ blank=True,
+ verbose_name="state, territory, or military post",
+ )
+
domain_request = models.OneToOneField(
"registrar.DomainRequest",
on_delete=models.PROTECT,
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index 68db5fb67..d7c3a950b 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1129,7 +1129,7 @@ def reject_with_prejudice(self):
# Form unlocking steps
# These methods control the conditions in which we should unlock certain domain wizard steps.
def unlock_requesting_entity(self) -> bool:
- """Unlocks the requesting entity step """
+ """Unlocks the requesting entity step"""
if self.portfolio and self.organization_name == self.portfolio.organization_name:
return True
else:
@@ -1215,11 +1215,11 @@ def is_suborganization(self) -> bool:
return True
return False
-
+
def is_custom_suborganization(self) -> bool:
"""Used on the requesting entity form to determine if a user is trying to request
a new suborganization using the domain request form.
-
+
This only occurs when no suborganization is selected, but they've filled out
the requested_suborganization, suborganization_city, and suborganization_state_territory fields.
"""
@@ -1227,10 +1227,11 @@ def is_custom_suborganization(self) -> bool:
return not self.sub_organization and self.has_information_required_to_make_suborganization()
else:
return False
-
+
def has_information_required_to_make_suborganization(self) -> bool:
"""Checks if we have all the information we need to create a new suborganization object.
- Checks for a the existence of requested_suborganization, suborganization_city, suborganization_state_territory"""
+ Checks for a the existence of requested_suborganization, suborganization_city, suborganization_state_territory
+ """
if self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory:
return True
else:
diff --git a/src/registrar/templatetags/custom_filters.py b/src/registrar/templatetags/custom_filters.py
index 9e7298ac6..50ff6c842 100644
--- a/src/registrar/templatetags/custom_filters.py
+++ b/src/registrar/templatetags/custom_filters.py
@@ -258,6 +258,7 @@ def portfolio_role_summary(user, portfolio):
else:
return []
+
@register.filter(name="display_requesting_entity")
def display_requesting_entity(domain_request):
"""Workaround for a newline issue in .txt files (our emails) as if statements
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index e88eb4c75..5a0d90240 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -614,6 +614,7 @@ def save(self, forms: list):
super().save(forms)
+
class PortfolioAdditionalDetails(DomainRequestWizard):
template_name = "portfolio_domain_request_additional_details.html"
From 14bfeb7d9bfd34185714f8c897b20f49f2318ea0 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 24 Oct 2024 11:58:36 -0600
Subject: [PATCH 19/47] admin show/hide logic
---
src/registrar/admin.py | 38 ++++++++++++
src/registrar/assets/js/get-gov-admin.js | 64 ++++++++++++++++++++
src/registrar/forms/domain_request_wizard.py | 1 -
3 files changed, 102 insertions(+), 1 deletion(-)
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index 913820a14..2fab7a735 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -1885,6 +1885,27 @@ def status_history(self, obj):
change_form_template = "django/admin/domain_request_change_form.html"
+ # 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_fieldsets(self, request, obj=None):
+ fieldsets = super().get_fieldsets(request, obj)
+
+ # Create a modified version of fieldsets to exclude certain fields
+ if not request.user.has_perm("registrar.full_access_permission"):
+ modified_fieldsets = []
+ for name, data in 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
+ return fieldsets
+
# Trigger action when a fieldset is changed
def save_model(self, request, obj, form, change):
"""Custom save_model definition that handles edge cases"""
@@ -2297,6 +2318,15 @@ 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)
@@ -2397,6 +2427,14 @@ def get_fieldsets(self, request, obj=None):
# 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-admin.js b/src/registrar/assets/js/get-gov-admin.js
index fd50fbb0c..9c770f40a 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -47,10 +47,49 @@ function addOrRemoveSessionBoolean(name, add){
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
// Event handlers.
+/** Helper function that handles business logic for the suborganization field.
+ * Can be used anywhere the suborganization dropdown exists
+*/
+function handleSuborganizationFields(
+ portfolioDropdownSelector="#id_portfolio",
+ suborgDropdownSelector="#id_sub_organization",
+ requestedSuborgFieldSelector=".field-requested_suborganization",
+ suborgCitySelector=".field-suborganization_city",
+ suborgStateTerritorySelector=".field-suborganization_state_territory"
+) {
+ // These dropdown arecselect2 fields so they must be interacted with via jquery
+ const portfolioDropdown = django.jQuery(portfolioDropdownSelector)
+ const suborganizationDropdown = django.jQuery(suborgDropdownSelector)
+ const requestedSuborgField = document.querySelector(requestedSuborgFieldSelector);
+ const suborgCity = document.querySelector(suborgCitySelector);
+ const suborgStateTerritory = document.querySelector(suborgStateTerritorySelector);
+ if (!suborganizationDropdown || !requestedSuborgField || !suborgCity || !suborgStateTerritory) {
+ console.error("Requested suborg fields not found.");
+ return;
+ }
+
+ function toggleSuborganizationFields() {
+ if (portfolioDropdown.val() && !suborganizationDropdown.val()) {
+ showElement(requestedSuborgField);
+ showElement(suborgCity);
+ showElement(suborgStateTerritory);
+ }else {
+ hideElement(requestedSuborgField);
+ hideElement(suborgCity);
+ hideElement(suborgStateTerritory);
+ }
+ }
+
+ // Run the function once on page startup, then attach an event listener
+ toggleSuborganizationFields();
+ suborganizationDropdown.on("change", toggleSuborganizationFields);
+ portfolioDropdown.on("change", toggleSuborganizationFields);
+}
// <<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>><<>>
// Initialization code.
+
/** An IIFE for pages in DjangoAdmin that use modals.
* Dja strips out form elements, and modals generate their content outside
* of the current form scope, so we need to "inject" these inputs.
@@ -1170,3 +1209,28 @@ document.addEventListener('DOMContentLoaded', function() {
};
}
})();
+
+/** An IIFE for dynamic DomainRequest fields
+*/
+(function dynamicDomainRequestFields(){
+ const domainRequestPage = document.getElementById("domainrequest_form");
+ if (domainRequestPage) {
+ handleSuborganizationFields();
+ }
+})();
+
+
+/** An IIFE for dynamic DomainInformation fields
+*/
+(function dynamicDomainInformationFields(){
+ const domainInformationPage = document.getElementById("domaininformation_form");
+ // DomainInformation is embedded inside domain so this should fire there too
+ const domainPage = document.getElementById("domain_form");
+ if (domainInformationPage) {
+ handleSuborganizationFields();
+ }
+
+ if (domainPage) {
+ handleSuborganizationFields(portfolioDropdownSelector="#id_domain_info-0-portfolio", suborgDropdownSelector="#id_domain_info-0-sub_organization");
+ }
+})();
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index c7b962f60..4aea46efe 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -75,7 +75,6 @@ def __init__(self, *args, **kwargs):
def clean_sub_organization(self):
sub_organization = self.cleaned_data.get("sub_organization")
is_custom = self.cleaned_data.get("is_custom_suborganization")
- print(f"in clean: {sub_organization}")
if is_custom:
# If it's a custom suborganization, return None (equivalent to selecting nothing)
return None
From 738ff0f95216db4574e6c0124de04db4c8342c85 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 24 Oct 2024 15:12:07 -0600
Subject: [PATCH 20/47] unit test stuff
---
src/registrar/admin.py | 17 ----
src/registrar/assets/js/get-gov.js | 13 +--
src/registrar/forms/domain_request_wizard.py | 9 ++
src/registrar/models/suborganization.py | 1 -
src/registrar/tests/test_views_portfolio.py | 88 ++++++++++++++++++++
src/registrar/views/domain_request.py | 15 ++--
6 files changed, 114 insertions(+), 29 deletions(-)
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 @@ def get_fieldsets(self, request, obj=None):
# 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 @@
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 @@ def __init__(self, *args, **kwargs):
)
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 @@ def clean_sub_organization(self):
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 @@ def test_org_user_cannot_delete_others_domain_requests(self):
# 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 @@ def has_permission(self):
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
From d44bcadafb79063b9c22d495ce2d069c91d3965c Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 25 Oct 2024 10:37:55 -0600
Subject: [PATCH 21/47] unit tests
---
src/registrar/admin.py | 1 +
src/registrar/forms/domain_request_wizard.py | 1 +
src/registrar/models/domain_request.py | 1 -
.../emails/submission_confirmation.txt | 6 +-
src/registrar/tests/test_views_portfolio.py | 252 ++++++++++++++++--
src/registrar/views/domain_request.py | 7 +-
6 files changed, 233 insertions(+), 35 deletions(-)
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index f0f8a3673..fdadde436 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -1893,6 +1893,7 @@ def status_history(self, obj):
"suborganization_city",
"suborganization_state_territory",
]
+
def get_fieldsets(self, request, obj=None):
fieldsets = super().get_fieldsets(request, obj)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 62b9056bf..72421e7c9 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -26,6 +26,7 @@ class RequestingEntityForm(RegistrarForm):
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
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index d7c3a950b..8ac12e085 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -6,7 +6,6 @@
from django.db import models
from django_fsm import FSMField, transition # type: ignore
from django.utils import timezone
-from waffle import flag_is_active
from registrar.models.domain import Domain
from registrar.models.federal_agency import FederalAgency
from registrar.models.utility.generic_helper import CreateOrUpdateOrganizationTypeHelper
diff --git a/src/registrar/templates/emails/submission_confirmation.txt b/src/registrar/templates/emails/submission_confirmation.txt
index 911269bf1..ef9736a9d 100644
--- a/src/registrar/templates/emails/submission_confirmation.txt
+++ b/src/registrar/templates/emails/submission_confirmation.txt
@@ -31,11 +31,7 @@ THANK YOU
----------------------------------------------------------------
-{% if is_org_user %}
- {% include 'emails/includes/portfolio_domain_request_summary.txt' %}
-{% else %}
- {% include 'emails/includes/domain_request_summary.txt' %}
-{% endif %}
+{% if is_org_user %}{% include 'emails/includes/portfolio_domain_request_summary.txt' %}{% else %}{% include 'emails/includes/domain_request_summary.txt' %}{% endif %}
----------------------------------------------------------------
The .gov team
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index 9d7122451..d770dd677 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -2,6 +2,7 @@
from api.tests.common import less_console_noise_decorator
from registrar.config import settings
from registrar.models import Portfolio, SeniorOfficial
+from unittest.mock import MagicMock
from django_webtest import WebTest # type: ignore
from registrar.models import (
DomainRequest,
@@ -9,12 +10,14 @@
DomainInformation,
UserDomainRole,
User,
+ Suborganization,
+ AllowedEmail,
)
from registrar.models.portfolio_invitation import PortfolioInvitation
from registrar.models.user_group import UserGroup
from registrar.models.user_portfolio_permission import UserPortfolioPermission
from registrar.models.utility.portfolio_helper import UserPortfolioPermissionChoices, UserPortfolioRoleChoices
-from .common import MockSESClient, completed_domain_request, create_test_user
+from .common import MockSESClient, completed_domain_request, create_test_user, create_user
from waffle.testutils import override_flag
from django.contrib.sessions.middleware import SessionMiddleware
import boto3_mocking # type: ignore
@@ -1392,86 +1395,283 @@ def test_org_user_cannot_delete_others_domain_requests(self):
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.user = create_user()
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
+ self.portfolio_2, _ = Portfolio.objects.get_or_create(creator=self.user, organization_name="Hotel Alaska")
+ self.suborganization, _ = Suborganization.objects.get_or_create(
+ name="Rocky road",
+ portfolio=self.portfolio,
+ )
+ self.suborganization_2, _ = Suborganization.objects.get_or_create(
+ name="Vanilla",
+ portfolio=self.portfolio,
+ )
+ self.unrelated_suborganization, _ = Suborganization.objects.get_or_create(
+ name="Cold",
+ portfolio=self.portfolio_2,
+ )
+ self.portfolio_role = UserPortfolioPermission.objects.create(
+ portfolio=self.portfolio, user=self.user, roles=[UserPortfolioRoleChoices.ORGANIZATION_ADMIN]
)
# Login the current user
self.app.set_user(self.user.username)
+ self.mock_client_class = MagicMock()
+ self.mock_client = self.mock_client_class.return_value
+
def tearDown(self):
UserDomainRole.objects.all().delete()
DomainRequest.objects.all().delete()
DomainInformation.objects.all().delete()
Domain.objects.all().delete()
UserPortfolioPermission.objects.all().delete()
+ Suborganization.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
+ @less_console_noise_decorator
+ def test_requesting_entity_page_new_request(self):
+ """Tests that the requesting entity page loads correctly when a new request is started"""
+
+ response = self.app.get(reverse("domain-request:"))
+
+ # Navigate past the intro page
+ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
+ self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+ intro_form = response.forms[0]
+ response = intro_form.submit().follow()
+
+ # Test the requesting entiy page
+ self.assertContains(response, "Who will use the domain you’re requesting?")
+ self.assertContains(response, "Add suborganization information")
+ # We expect to see the portfolio name in two places:
+ # the header, and as one of the radio button options.
+ self.assertContains(response, self.portfolio.organization_name, count=2)
+
+ # We expect the dropdown list to contain the suborganizations that currently exist on this portfolio
+ self.assertContains(response, self.suborganization.name, count=1)
+ self.assertContains(response, self.suborganization_2.name, count=1)
+
+ # However, we should only see suborgs that are on the actual portfolio
+ self.assertNotContains(response, self.unrelated_suborganization.name)
@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
+ @less_console_noise_decorator
+ def test_requesting_entity_page_existing_suborg_submission(self):
+ """Tests that you can submit a form on this page and set a suborg"""
+ response = self.app.get(reverse("domain-request:"))
+
+ # Navigate past the intro page
+ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
+ self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+ form = response.forms[0]
+ response = form.submit().follow()
+
+ # Check that we're on the right page
+ self.assertContains(response, "Who will use the domain you’re requesting?")
+ form = response.forms[0]
+
+ # Test selecting an existing suborg
+ form["portfolio_requesting_entity-is_suborganization"] = True
+ form["portfolio_requesting_entity-sub_organization"] = f"{self.suborganization.id}"
+ form["portfolio_requesting_entity-is_custom_suborganization"] = False
+
+ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
+ self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+ response = form.submit().follow()
+
+ # Ensure that the post occurred successfully by checking that we're on the following page.
+ self.assertContains(response, "Current websites")
+ created_domain_request_exists = DomainRequest.objects.filter(
+ organization_name__isnull=True, sub_organization=self.suborganization
+ ).exists()
+ self.assertTrue(created_domain_request_exists)
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
+ @less_console_noise_decorator
+ def test_requesting_entity_page_new_suborg_submission(self):
+ """Tests that you can submit a form on this page and set a new suborg"""
+ response = self.app.get(reverse("domain-request:"))
+
+ # Navigate past the intro page
+ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
+ self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+ form = response.forms[0]
+ response = form.submit().follow()
+
+ # Check that we're on the right page
+ self.assertContains(response, "Who will use the domain you’re requesting?")
+ form = response.forms[0]
+
+ # Test selecting an existing suborg
+ form["portfolio_requesting_entity-is_suborganization"] = True
+ form["portfolio_requesting_entity-is_custom_suborganization"] = True
+ form["portfolio_requesting_entity-sub_organization"] = ""
+
+ form["portfolio_requesting_entity-requested_suborganization"] = "moon"
+ form["portfolio_requesting_entity-suborganization_city"] = "kepler"
+ form["portfolio_requesting_entity-suborganization_state_territory"] = "AL"
+
+ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
+ self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+ response = form.submit().follow()
+
+ # Ensure that the post occurred successfully by checking that we're on the following page.
+ self.assertContains(response, "Current websites")
+ created_domain_request_exists = DomainRequest.objects.filter(
+ organization_name__isnull=True,
+ sub_organization__isnull=True,
+ requested_suborganization="moon",
+ suborganization_city="kepler",
+ suborganization_state_territory=DomainRequest.StateTerritoryChoices.ALABAMA,
+ ).exists()
+ self.assertTrue(created_domain_request_exists)
+
+ @override_flag("organization_feature", active=True)
+ @override_flag("organization_requests", active=True)
+ @less_console_noise_decorator
+ def test_requesting_entity_page_organization_submission(self):
+ """Tests submitting an organization on the requesting org form"""
+ response = self.app.get(reverse("domain-request:"))
+
+ # Navigate past the intro page
+ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
+ self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+ form = response.forms[0]
+ response = form.submit().follow()
+
+ # Check that we're on the right page
+ self.assertContains(response, "Who will use the domain you’re requesting?")
+ form = response.forms[0]
+
+ # Test selecting an existing suborg
+ form["portfolio_requesting_entity-is_suborganization"] = False
+
+ session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
+ self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
+ response = form.submit().follow()
+
+ # Ensure that the post occurred successfully by checking that we're on the following page.
+ self.assertContains(response, "Current websites")
+ created_domain_request_exists = DomainRequest.objects.filter(
+ organization_name=self.portfolio.organization_name,
+ ).exists()
+ self.assertTrue(created_domain_request_exists)
+
+ @override_flag("organization_feature", active=True)
+ @override_flag("organization_requests", active=True)
+ @less_console_noise_decorator
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
- ])
+ 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'] = ""
+ 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
+ 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
+ @boto3_mocking.patching
+ @less_console_noise_decorator
+ def test_requesting_entity_submission_email_sent(self):
+ """Tests that an email is sent out on successful form submission"""
+ AllowedEmail.objects.create(email=self.user.email)
+ domain_request = completed_domain_request(
+ user=self.user,
+ # This is the additional details field
+ has_anything_else=True,
+ )
+ domain_request.portfolio = self.portfolio
+ domain_request.requested_suborganization = "moon"
+ domain_request.suborganization_city = "kepler"
+ domain_request.suborganization_state_territory = DomainRequest.StateTerritoryChoices.ALABAMA
+ domain_request.save()
+ domain_request.refresh_from_db()
+
+ with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
+ domain_request.submit()
+ _, kwargs = self.mock_client.send_email.call_args
+ body = kwargs["Content"]["Simple"]["Body"]["Text"]["Data"]
+
+ self.assertNotIn("Anything else", body)
+ self.assertIn("kepler, AL", body)
+ self.assertIn("Requesting entity:", body)
+ self.assertIn("Administrators from your organization:", body)
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
+ @boto3_mocking.patching
+ @less_console_noise_decorator
def test_requesting_entity_viewonly(self):
"""Tests the review steps page on under our viewonly context"""
- pass
+ domain_request = completed_domain_request(
+ user=create_test_user(),
+ # This is the additional details field
+ has_anything_else=True,
+ )
+ domain_request.portfolio = self.portfolio
+ domain_request.requested_suborganization = "moon"
+ domain_request.suborganization_city = "kepler"
+ domain_request.suborganization_state_territory = DomainRequest.StateTerritoryChoices.ALABAMA
+ domain_request.save()
+ domain_request.refresh_from_db()
+
+ domain_request.submit()
+
+ response = self.app.get(reverse("domain-request-status-viewonly", kwargs={"pk": domain_request.pk}))
+ self.assertContains(response, "Requesting entity")
+ self.assertContains(response, "moon")
+ self.assertContains(response, "kepler, AL")
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)
+ @boto3_mocking.patching
+ @less_console_noise_decorator
def test_requesting_entity_manage(self):
"""Tests the review steps page on under our manage context"""
- pass
+ domain_request = completed_domain_request(
+ user=self.user,
+ # This is the additional details field
+ has_anything_else=True,
+ )
+ domain_request.portfolio = self.portfolio
+ domain_request.requested_suborganization = "moon"
+ domain_request.suborganization_city = "kepler"
+ domain_request.suborganization_state_territory = DomainRequest.StateTerritoryChoices.ALABAMA
+ domain_request.save()
+ domain_request.refresh_from_db()
+
+ domain_request.submit()
+
+ response = self.app.get(reverse("domain-request-status", kwargs={"pk": domain_request.pk}))
+ self.assertContains(response, "Requesting entity")
+ self.assertContains(response, "moon")
+ self.assertContains(response, "kepler, AL")
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index 0a4728d81..bdb496c9e 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -11,7 +11,6 @@
from registrar.forms.utility.wizard_form_helper import request_step_list
from registrar.models import DomainRequest
from registrar.models.contact import Contact
-from registrar.models.suborganization import Suborganization
from registrar.models.user import User
from registrar.views.utility import StepsHelper
from registrar.views.utility.permission_views import DomainRequestPermissionDeleteView
@@ -601,14 +600,16 @@ def save(self, forms: list):
sub_organization = cleaned_data.get("sub_organization")
requested_suborganization = cleaned_data.get("requested_suborganization")
- # If no suborganization presently exists but the user filled out org information then create a suborg automatically.
+ # If no suborganization presently exists but the user filled out
+ # org information then create a suborg automatically.
if is_suborganization and (sub_organization or requested_suborganization):
# Cleanup the organization name field, as this isn't for suborganizations.
self.domain_request.organization_name = None
self.domain_request.sub_organization = sub_organization
else:
# If the user doesn't intend to create a suborg, simply don't make one and do some data cleanup
- self.domain_request.organization_name = self.domain_request.portfolio.organization_name
+ if self.domain_request.portfolio:
+ self.domain_request.organization_name = self.domain_request.portfolio.organization_name
self.domain_request.sub_organization = None
self.domain_request.requested_suborganization = None
From 934d06dfb5a56ef93fdc03df4df9a41bf51de107 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 25 Oct 2024 10:52:22 -0600
Subject: [PATCH 22/47] lint and fix existing unit tests
---
src/registrar/tests/test_views_portfolio.py | 3 ---
src/registrar/tests/test_views_request.py | 2 --
src/registrar/views/domain_request.py | 2 +-
3 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index d770dd677..6bf6ad783 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -1573,9 +1573,6 @@ def test_requesting_entity_page_organization_submission(self):
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]
diff --git a/src/registrar/tests/test_views_request.py b/src/registrar/tests/test_views_request.py
index 17e6bcbe6..73e538df3 100644
--- a/src/registrar/tests/test_views_request.py
+++ b/src/registrar/tests/test_views_request.py
@@ -2887,8 +2887,6 @@ def test_domain_request_withdraw_portfolio_redirects_correctly(self):
detail_page = self.app.get(f"/domain-request/{domain_request.id}")
self.assertContains(detail_page, "city.gov")
self.assertContains(detail_page, "city1.gov")
- self.assertContains(detail_page, "Chief Tester")
- self.assertContains(detail_page, "testy@town.com")
self.assertContains(detail_page, "Status:")
# click the "Withdraw request" button
mock_client = MockSESClient()
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index bdb496c9e..d39833fab 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -600,7 +600,7 @@ def save(self, forms: list):
sub_organization = cleaned_data.get("sub_organization")
requested_suborganization = cleaned_data.get("requested_suborganization")
- # If no suborganization presently exists but the user filled out
+ # If no suborganization presently exists but the user filled out
# org information then create a suborg automatically.
if is_suborganization and (sub_organization or requested_suborganization):
# Cleanup the organization name field, as this isn't for suborganizations.
From 0933fe4035e9b72237b15a2fd57b50ae29ca8305 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 25 Oct 2024 12:22:49 -0600
Subject: [PATCH 23/47] fix weird spaces
---
.../emails/includes/portfolio_domain_request_summary.txt | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
index f2486dbe1..866fde50f 100644
--- a/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
+++ b/src/registrar/templates/emails/includes/portfolio_domain_request_summary.txt
@@ -25,5 +25,4 @@ Your contact information:
Administrators from your organization:{% for admin in domain_request.portfolio.portfolio_admin_users %}
{% spaceless %}{% if admin != recipient %}{% include "emails/includes/contact.txt" with contact=admin %}{% endif %}{% endspaceless %}
-{% endfor %}
-
+{% endfor %}
\ No newline at end of file
From 9531076f87934e5f7c972abe0bb847257ae9a2c4 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Mon, 28 Oct 2024 10:30:41 -0600
Subject: [PATCH 24/47] Update src/registrar/forms/domain_request_wizard.py
---
src/registrar/forms/domain_request_wizard.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 72421e7c9..0ec0c6dac 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -107,7 +107,7 @@ def full_clean(self):
def clean(self):
"""Custom clean implementation to handle our desired logic flow for suborganization.
- Given that these fields often corely on eachother, we need to do this in the parent function."""
+ Given that these fields often rely on eachother, we need to do this in the parent function."""
cleaned_data = super().clean()
suborganization = self.cleaned_data.get("sub_organization")
From d0aff60db0291278ef97e7d52917f287890f5b2c Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 08:05:06 -0600
Subject: [PATCH 25/47] Update src/registrar/assets/js/get-gov-admin.js
Co-authored-by: Rachid Mrad <107004823+rachidatecs@users.noreply.github.com>
---
src/registrar/assets/js/get-gov-admin.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index 9c770f40a..ed597d55f 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -57,7 +57,7 @@ function handleSuborganizationFields(
suborgCitySelector=".field-suborganization_city",
suborgStateTerritorySelector=".field-suborganization_state_territory"
) {
- // These dropdown arecselect2 fields so they must be interacted with via jquery
+ // These dropdown are select2 fields so they must be interacted with via jquery
const portfolioDropdown = django.jQuery(portfolioDropdownSelector)
const suborganizationDropdown = django.jQuery(suborgDropdownSelector)
const requestedSuborgField = document.querySelector(requestedSuborgFieldSelector);
From b09e0cad4b6f0d49ea407977ba5f29fb1d4afbf8 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 09:25:23 -0600
Subject: [PATCH 26/47] Fix bug with both org name and suborg
---
src/registrar/forms/domain_request_wizard.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 0ec0c6dac..255eff332 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -152,13 +152,13 @@ def form_is_checked(self):
Determines the initial checked state of the form based on the domain_request's attributes.
"""
- if (
+ if self.domain_request.is_suborganization():
+ return True
+ elif (
self.domain_request.portfolio
and self.domain_request.organization_name == self.domain_request.portfolio.organization_name
):
return False
- elif self.domain_request.is_suborganization():
- return True
else:
return None
From bc8789bc1b2e0bc8e616f6096d791269f64c071a Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 11:22:39 -0600
Subject: [PATCH 27/47] Simplify logic and use better names
---
src/registrar/assets/js/get-gov.js | 64 ++++----
src/registrar/forms/domain_request_wizard.py | 57 ++++---
src/registrar/models/domain_request.py | 74 ++++-----
.../domain_request_requesting_entity.html | 10 +-
.../templates/domain_request_status.html | 6 +-
.../portfolio_request_review_steps.html | 28 ++--
.../includes/portfolio_status_manage.html | 7 -
.../includes/request_status_manage.html | 144 +++++++++---------
src/registrar/templatetags/custom_filters.py | 14 +-
src/registrar/tests/test_views_portfolio.py | 14 +-
src/registrar/views/domain_request.py | 4 +-
11 files changed, 205 insertions(+), 217 deletions(-)
delete mode 100644 src/registrar/templates/includes/portfolio_status_manage.html
diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js
index 6302e57f7..6182bc828 100644
--- a/src/registrar/assets/js/get-gov.js
+++ b/src/registrar/assets/js/get-gov.js
@@ -2741,49 +2741,45 @@ document.addEventListener('DOMContentLoaded', function() {
* 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.
+(function handleRequestingEntityFieldset() {
+ // Sadly, these ugly ids are the auto generated with this prefix
+ const formPrefix = "portfolio_requesting_entity"
+
// This determines if we are on the requesting entity page or not.
- const fieldset = document.getElementById("requesting-entity-fieldset");
- if (!fieldset) return;
+ const isSubOrgFieldset = document.getElementById(`id_${formPrefix}-requesting_entity_is_suborganization__fieldset`);
+ if (!isSubOrgFieldset) return;
// Get the is_suborganization radio buttons
- // Sadly, these ugly ids are the auto generated
- const formPrefix = "portfolio_requesting_entity"
- const isSuborgRadios = document.querySelectorAll(`input[name="${formPrefix}-is_suborganization"]`);
- const subOrgSelect = document.querySelector(`#id_${formPrefix}-sub_organization`);
+ const isSuborgRadios = isSubOrgFieldset.querySelectorAll(`input[name="${formPrefix}-requesting_entity_is_suborganization"]`);
+ const subOrgSelect = document.getElementById(`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");
- const suborganizationDetailsFieldset = document.querySelector("#requesting-entity-fieldset__suborganization__details");
+ const suborganizationContainer = document.getElementById("suborganization-container");
+ const suborganizationDetailsContainer = document.getElementById("suborganization-container__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")
+ // This variable determines if the user is trying to *create* a new suborganization or not.
+ var isRequestingSuborganization = document.getElementById(`id_${formPrefix}-is_requesting_new_suborganization`)
// Don't do anything if we are missing crucial page elements
- if (!isSuborgRadios || !subOrgSelect || !suborganizationFieldset || !suborganizationDetailsFieldset) return;
+ if (!isSuborgRadios || !subOrgSelect || !suborganizationContainer || !suborganizationDetailsContainer) return;
// Function to toggle suborganization based on is_suborganization selection
function toggleSuborganization(radio) {
if (radio && radio.checked && radio.value === "True") {
- showElement(suborganizationFieldset);
- toggleSuborganizationDetails();
- } else {
- hideElement(suborganizationFieldset);
- hideElement(suborganizationDetailsFieldset);
- }
- };
+ showElement(suborganizationContainer);
- // Function to toggle organization details based on sub_organization selection
- function toggleSuborganizationDetails () {
- // We should hide the org name fields when we select the special other value
- if (subOrgSelect.value === "other") {
- showElement(suborganizationDetailsFieldset);
- isCustomSuborganization.value = "True";
+ // Handle custom suborganizations
+ if (subOrgSelect.value === "other") {
+ showElement(suborganizationDetailsContainer);
+ isRequestingSuborganization.value = "True";
+ } else {
+ hideElement(suborganizationDetailsContainer);
+ isRequestingSuborganization.value = "False";
+ }
} else {
- hideElement(suborganizationDetailsFieldset);
- isCustomSuborganization.value = "False";
+ hideElement(suborganizationContainer);
+ hideElement(suborganizationDetailsContainer);
}
};
@@ -2795,7 +2791,8 @@ document.addEventListener('DOMContentLoaded', function() {
subOrgSelect.add(fakeOption);
}
- if (isCustomSuborganization.value === "True") {
+ console.log(isRequestingSuborganization.value)
+ if (isRequestingSuborganization.value === "True") {
subOrgSelect.value = "other"
}
@@ -2813,6 +2810,13 @@ document.addEventListener('DOMContentLoaded', function() {
// Add event listener to the suborg dropdown to show/hide the suborg details section
subOrgSelect.addEventListener("change", () => {
- toggleSuborganizationDetails();
+ // Handle the custom suborganization field
+ if (subOrgSelect.value === "other") {
+ showElement(suborganizationDetailsContainer);
+ isRequestingSuborganization.value = "True";
+ } else {
+ hideElement(suborganizationDetailsContainer);
+ isRequestingSuborganization.value = "False";
+ }
});
})();
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 255eff332..e6188eb33 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -27,6 +27,9 @@ class RequestingEntityForm(RegistrarForm):
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."""
+ # Add a hidden field to store if the user is requesting a new suborganization
+ is_requesting_new_suborganization = forms.BooleanField(required=False, widget=forms.HiddenInput())
+
sub_organization = forms.ModelChoiceField(
label="Suborganization name",
# not required because this field won't be filled out unless
@@ -57,17 +60,6 @@ class RequestingEntityForm(RegistrarForm):
"required": ("Select the state, territory, or military post where your organization is located.")
},
)
- is_suborganization = forms.NullBooleanField(
- widget=forms.RadioSelect(
- choices=[
- (True, "Yes"),
- (False, "No"),
- ],
- )
- )
-
- # Add a hidden field to store that we are adding a custom suborg
- is_custom_suborganization = forms.BooleanField(required=False, widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
"""Override of init to add the suborganization queryset"""
@@ -81,23 +73,25 @@ def __init__(self, *args, **kwargs):
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:
- # If it's a custom suborganization, return None (equivalent to selecting nothing)
+
+ # If it's a new suborganization, return None (equivalent to selecting nothing)
+ if self.cleaned_data.get("is_requesting_new_suborganization"):
return None
- return sub_organization
+
+ # Otherwise just return the suborg as normal
+ return self.cleaned_data.get("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
+
+ # Remove the 'other' value from suborganization if it exists.
+ # This is a special value that tracks if the user is requesting a new suborg.
suborganization = self.data.get("portfolio_requesting_entity-sub_organization")
- if suborganization:
- if "other" in data["portfolio_requesting_entity-sub_organization"]:
- # Remove the 'other' value
- data["portfolio_requesting_entity-sub_organization"] = ""
+ if suborganization and "other" in suborganization:
+ data["portfolio_requesting_entity-sub_organization"] = ""
# Set the modified data back to the form
self.data = data
@@ -110,11 +104,16 @@ def clean(self):
Given that these fields often rely on eachother, we need to do this in the parent function."""
cleaned_data = super().clean()
+ # Do some custom error validation if the requesting entity is a suborg.
+ # Otherwise, just validate as normal.
suborganization = self.cleaned_data.get("sub_organization")
- is_suborganization = self.cleaned_data.get("is_suborganization")
- is_custom_suborganization = self.cleaned_data.get("is_custom_suborganization")
- if is_suborganization:
- if is_custom_suborganization:
+ is_requesting_new_suborganization = self.cleaned_data.get("is_requesting_new_suborganization")
+
+ # Get the value of the yes/no checkbox from RequestingEntityYesNoForm.
+ # Since self.data stores this as a string, we need to convert "True" => True.
+ requesting_entity_is_suborganization = self.data.get("portfolio_requesting_entity-requesting_entity_is_suborganization")
+ if requesting_entity_is_suborganization == "True":
+ if is_requesting_new_suborganization:
# Validate custom suborganization fields
if not cleaned_data.get("requested_suborganization"):
self.add_error("requested_suborganization", "Enter details for your organization name.")
@@ -125,7 +124,6 @@ def clean(self):
elif not suborganization:
self.add_error("sub_organization", "Select a suborganization.")
- cleaned_data = super().clean()
return cleaned_data
@@ -134,7 +132,7 @@ class RequestingEntityYesNoForm(BaseYesNoForm):
# This first option will change dynamically
form_choices = ((False, "Current Organization"), (True, "A suborganization. (choose from list)"))
- field_name = "is_suborganization"
+ field_name = "requesting_entity_is_suborganization"
def __init__(self, *args, **kwargs):
"""Extend the initialization of the form from RegistrarForm __init__"""
@@ -152,12 +150,9 @@ def form_is_checked(self):
Determines the initial checked state of the form based on the domain_request's attributes.
"""
- if self.domain_request.is_suborganization():
+ if self.domain_request.requesting_entity_is_suborganization():
return True
- elif (
- self.domain_request.portfolio
- and self.domain_request.organization_name == self.domain_request.portfolio.organization_name
- ):
+ elif self.domain_request.requesting_entity_is_portfolio():
return False
else:
return None
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index 8ac12e085..a04cea526 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1125,16 +1125,49 @@ def reject_with_prejudice(self):
self.creator.restrict_user()
- # Form unlocking steps
+ def requesting_entity_is_portfolio(self) -> bool:
+ """Determines if this record is requesting that a portfolio be their organization."""
+ return self.portfolio and self.organization_name == self.portfolio.organization_name
+
+ def requesting_entity_is_suborganization(self) -> bool:
+ """Used to determine if this domain request is also requesting that it be tied to a suborganization.
+ Checks if this record has a suborganization or not by checking if a suborganization exists,
+ and if it doesn't, determining if properties like requested_suborganization exist.
+ """
+
+ if self.portfolio:
+ if self.sub_organization:
+ return True
+ if self.is_requesting_new_suborganization():
+ return True
+ return False
+
+ def is_requesting_new_suborganization(self) -> bool:
+ """Used on the requesting entity form to determine if a user is trying to request
+ a new suborganization using the domain request form.
+
+ This only occurs when no suborganization is selected, but they've filled out
+ the requested_suborganization, suborganization_city, and suborganization_state_territory fields.
+ """
+ # If a suborganization already exists, it can't possibly be a new one
+ if self.sub_organization:
+ return False
+ return bool(self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory)
+
+ # ## Form unlocking steps ## #
+ #
# These methods control the conditions in which we should unlock certain domain wizard steps.
+
def unlock_requesting_entity(self) -> bool:
"""Unlocks the requesting entity step"""
- if self.portfolio and self.organization_name == self.portfolio.organization_name:
+ if self.requesting_entity_is_suborganization():
+ return True
+ elif self.requesting_entity_is_portfolio():
return True
else:
- return self.is_suborganization()
+ return False
- # ## Form policies ###
+ # ## Form policies ## #
#
# These methods control what questions need to be answered by applicants
# during the domain request flow. They are policies about the domain request so
@@ -1203,39 +1236,6 @@ def is_federal(self) -> Union[bool, None]:
return True
return False
- def is_suborganization(self) -> bool:
- """Determines if this record is a suborganization or not by checking if a suborganization exists,
- and if it doesn't, determining if properties like requested_suborganization exist."""
- if self.portfolio:
- if self.sub_organization:
- return True
-
- if self.has_information_required_to_make_suborganization():
- return True
-
- return False
-
- def is_custom_suborganization(self) -> bool:
- """Used on the requesting entity form to determine if a user is trying to request
- a new suborganization using the domain request form.
-
- This only occurs when no suborganization is selected, but they've filled out
- the requested_suborganization, suborganization_city, and suborganization_state_territory fields.
- """
- if self.is_suborganization():
- return not self.sub_organization and self.has_information_required_to_make_suborganization()
- else:
- return False
-
- def has_information_required_to_make_suborganization(self) -> bool:
- """Checks if we have all the information we need to create a new suborganization object.
- Checks for a the existence of requested_suborganization, suborganization_city, suborganization_state_territory
- """
- if self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory:
- return True
- else:
- return False
-
def to_dict(self):
"""This is to process to_dict for Domain Information, making it friendly
to "copy" it
diff --git a/src/registrar/templates/domain_request_requesting_entity.html b/src/registrar/templates/domain_request_requesting_entity.html
index 04ceb4a1a..d09e8ab89 100644
--- a/src/registrar/templates/domain_request_requesting_entity.html
+++ b/src/registrar/templates/domain_request_requesting_entity.html
@@ -7,7 +7,7 @@
{% endblock %}
{% block form_fields %}
-
+
Who will use the domain you’re requesting?
@@ -19,7 +19,7 @@ Who will use the domain you’re requesting?
{# forms.0 is a small yes/no form that toggles the visibility of "requesting entity" formset #}
{% with add_class="usa-radio__input--tile" add_legend_class="usa-sr-only" %}
{% with attr_required=True %}
- {% input_with_errors forms.0.is_suborganization %}
+ {% input_with_errors forms.0.requesting_entity_is_suborganization %}
{% endwith %}
{% endwith %}
@@ -27,10 +27,10 @@ Who will use the domain you’re requesting?
was selected or not. This allows for persistence across page reloads without using session variables.
{% endcomment %}
{% with add_group_class="display-none" %}
- {% input_with_errors forms.1.is_custom_suborganization %}
+ {% input_with_errors forms.1.is_requesting_new_suborganization %}
{% endwith %}
-
+
Add suborganization information
This information will be published in .gov’s public data . If you don’t see your suborganization in the list,
@@ -43,7 +43,7 @@
Add suborganization information
{% comment %} This will be toggled if a special value, "other", is selected.
Otherwise this field is invisible.
{% endcomment %}
-
+
{% with attr_required=True %}
{% input_with_errors forms.1.requested_suborganization %}
{% endwith %}
diff --git a/src/registrar/templates/domain_request_status.html b/src/registrar/templates/domain_request_status.html
index d98220532..d332ce54e 100644
--- a/src/registrar/templates/domain_request_status.html
+++ b/src/registrar/templates/domain_request_status.html
@@ -6,9 +6,5 @@
{% block content %}
- {% if not portfolio %}
- {% include "includes/request_status_manage.html" %}
- {% else %}
- {% include "includes/portfolio_status_manage.html" %}
- {% endif %}
+ {% include "includes/request_status_manage.html" %}
{% endblock %}
diff --git a/src/registrar/templates/includes/portfolio_request_review_steps.html b/src/registrar/templates/includes/portfolio_request_review_steps.html
index c2bdb18cc..ed727fee8 100644
--- a/src/registrar/templates/includes/portfolio_request_review_steps.html
+++ b/src/registrar/templates/includes/portfolio_request_review_steps.html
@@ -8,28 +8,24 @@
{% endif %}
{% if step == Step.REQUESTING_ENTITY %}
- {% if domain_request.portfolio and domain_request.organization_name == domain_request.portfolio.organization_name %}
- {% with title=form_titles|get_item:step value=domain_request.portfolio.organization_name %}
- {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
- {% endwith %}
+ {% with title=form_titles|get_item:step %}
+ {% if domain_request.sub_organization %}
+ {% include "includes/summary_item.html" with value=domain_request.sub_organization edit_link=domain_request_url %}
+ {% comment %} We don't have city or state_territory for suborganizations yet, so no data should display {% endcomment %}
+ {% elif domain_request.requesting_entity_is_suborganization %}
+ {% include "includes/summary_item.html" with value=domain_request.requested_suborganization edit_link=domain_request_url %}
+
{{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
+ {% elif domain_request.requesting_entity_is_portfolio %}
+ {% include "includes/summary_item.html" with value=domain_request.portfolio.organization_name edit_link=domain_request_url %}
{% if domain_request.portfolio.city and domain_request.portfolio.state_territory %}
{{domain_request.portfolio.city}}, {{domain_request.portfolio.state_territory}}
{% endif %}
- {% elif domain_request.sub_organization %}
- {% with title=form_titles|get_item:step value=domain_request.sub_organization %}
- {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
- {% endwith %}
- {% comment %} We don't have city or state_territory for suborganizations yet, so no data should display {% endcomment %}
- {% elif domain_request.requested_suborganization and domain_request.suborganization_city and domain_request.suborganization_state_territory %}
- {% with title=form_titles|get_item:step value=domain_request.requested_suborganization %}
- {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
- {% endwith %}
-
{{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
{% else %}
- {% with title=form_titles|get_item:step value="
Incomplete "|safe %}
- {% include "includes/summary_item.html" with title=title value=value heading_level=heading_level editable=is_editable edit_link=domain_request_url %}
+ {% with value="
Incomplete "|safe %}
+ {% include "includes/summary_item.html" with edit_link=domain_request_url %}
{% endwith %}
{% endif %}
+ {% endwith %}
{% endif %}
{% if step == Step.CURRENT_SITES %}
diff --git a/src/registrar/templates/includes/portfolio_status_manage.html b/src/registrar/templates/includes/portfolio_status_manage.html
deleted file mode 100644
index 120770156..000000000
--- a/src/registrar/templates/includes/portfolio_status_manage.html
+++ /dev/null
@@ -1,7 +0,0 @@
-{% extends "includes/request_status_manage.html" %}
-{% load custom_filters %}
-{% load static url_helpers %}
-
-{% block request_summary %}
- {% include "includes/portfolio_request_review_steps.html" with is_editable=False domain_request=DomainRequest %}
-{% endblock request_summary %}
diff --git a/src/registrar/templates/includes/request_status_manage.html b/src/registrar/templates/includes/request_status_manage.html
index 2a254df4b..fc2fd8f12 100644
--- a/src/registrar/templates/includes/request_status_manage.html
+++ b/src/registrar/templates/includes/request_status_manage.html
@@ -145,92 +145,96 @@
Summary of your domain request
{% endblock request_summary_header%}
{% block request_summary %}
- {% with heading_level='h3' %}
- {% with org_type=DomainRequest.get_generic_org_type_display %}
- {% include "includes/summary_item.html" with title='Type of organization' value=org_type heading_level=heading_level %}
- {% endwith %}
+ {% if portfolio %}
+ {% include "includes/portfolio_request_review_steps.html" with is_editable=False domain_request=DomainRequest %}
+ {% else %}
+ {% with heading_level='h3' %}
+ {% with org_type=DomainRequest.get_generic_org_type_display %}
+ {% include "includes/summary_item.html" with title='Type of organization' value=org_type heading_level=heading_level %}
+ {% endwith %}
- {% if DomainRequest.tribe_name %}
- {% include "includes/summary_item.html" with title='Tribal government' value=DomainRequest.tribe_name heading_level=heading_level %}
+ {% if DomainRequest.tribe_name %}
+ {% include "includes/summary_item.html" with title='Tribal government' value=DomainRequest.tribe_name heading_level=heading_level %}
- {% if DomainRequest.federally_recognized_tribe %}
-
Federally-recognized tribe
- {% endif %}
+ {% if DomainRequest.federally_recognized_tribe %}
+
Federally-recognized tribe
+ {% endif %}
- {% if DomainRequest.state_recognized_tribe %}
-
State-recognized tribe
- {% endif %}
+ {% if DomainRequest.state_recognized_tribe %}
+
State-recognized tribe
+ {% endif %}
- {% endif %}
+ {% endif %}
- {% if DomainRequest.get_federal_type_display %}
- {% include "includes/summary_item.html" with title='Federal government branch' value=DomainRequest.get_federal_type_display heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.get_federal_type_display %}
+ {% include "includes/summary_item.html" with title='Federal government branch' value=DomainRequest.get_federal_type_display heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.is_election_board %}
- {% with value=DomainRequest.is_election_board|yesno:"Yes,No,Incomplete" %}
- {% include "includes/summary_item.html" with title='Election office' value=value heading_level=heading_level %}
- {% endwith %}
- {% endif %}
+ {% if DomainRequest.is_election_board %}
+ {% with value=DomainRequest.is_election_board|yesno:"Yes,No,Incomplete" %}
+ {% include "includes/summary_item.html" with title='Election office' value=value heading_level=heading_level %}
+ {% endwith %}
+ {% endif %}
- {% if DomainRequest.organization_name %}
- {% include "includes/summary_item.html" with title='Organization' value=DomainRequest address='true' heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.organization_name %}
+ {% include "includes/summary_item.html" with title='Organization' value=DomainRequest address='true' heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.about_your_organization %}
- {% include "includes/summary_item.html" with title='About your organization' value=DomainRequest.about_your_organization heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.about_your_organization %}
+ {% include "includes/summary_item.html" with title='About your organization' value=DomainRequest.about_your_organization heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.senior_official %}
- {% include "includes/summary_item.html" with title='Senior official' value=DomainRequest.senior_official contact='true' heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.senior_official %}
+ {% include "includes/summary_item.html" with title='Senior official' value=DomainRequest.senior_official contact='true' heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.current_websites.all %}
- {% include "includes/summary_item.html" with title='Current websites' value=DomainRequest.current_websites.all list='true' heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.current_websites.all %}
+ {% include "includes/summary_item.html" with title='Current websites' value=DomainRequest.current_websites.all list='true' heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.requested_domain %}
- {% include "includes/summary_item.html" with title='.gov domain' value=DomainRequest.requested_domain heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.requested_domain %}
+ {% include "includes/summary_item.html" with title='.gov domain' value=DomainRequest.requested_domain heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.alternative_domains.all %}
- {% include "includes/summary_item.html" with title='Alternative domains' value=DomainRequest.alternative_domains.all list='true' heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.alternative_domains.all %}
+ {% include "includes/summary_item.html" with title='Alternative domains' value=DomainRequest.alternative_domains.all list='true' heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.purpose %}
- {% include "includes/summary_item.html" with title='Purpose of your domain' value=DomainRequest.purpose heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.purpose %}
+ {% include "includes/summary_item.html" with title='Purpose of your domain' value=DomainRequest.purpose heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.creator %}
- {% include "includes/summary_item.html" with title='Your contact information' value=DomainRequest.creator contact='true' heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.creator %}
+ {% include "includes/summary_item.html" with title='Your contact information' value=DomainRequest.creator contact='true' heading_level=heading_level %}
+ {% endif %}
- {% if DomainRequest.other_contacts.all %}
- {% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.other_contacts.all contact='true' list='true' heading_level=heading_level %}
- {% else %}
- {% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.no_other_contacts_rationale heading_level=heading_level %}
- {% endif %}
+ {% if DomainRequest.other_contacts.all %}
+ {% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.other_contacts.all contact='true' list='true' heading_level=heading_level %}
+ {% else %}
+ {% include "includes/summary_item.html" with title='Other employees from your organization' value=DomainRequest.no_other_contacts_rationale heading_level=heading_level %}
+ {% endif %}
- {# We always show this field even if None #}
- {% if DomainRequest %}
-
-
- {% if DomainRequest.cisa_representative_first_name %}
- {{ DomainRequest.get_formatted_cisa_rep_name }}
- {% else %}
- No
- {% endif %}
-
-
-
- {% if DomainRequest.anything_else %}
- {{DomainRequest.anything_else}}
- {% else %}
- No
- {% endif %}
-
- {% endif %}
- {% endwith %}
+ {# We always show this field even if None #}
+ {% if DomainRequest %}
+
+
+ {% if DomainRequest.cisa_representative_first_name %}
+ {{ DomainRequest.get_formatted_cisa_rep_name }}
+ {% else %}
+ No
+ {% endif %}
+
+
+
+ {% if DomainRequest.anything_else %}
+ {{DomainRequest.anything_else}}
+ {% else %}
+ No
+ {% endif %}
+
+ {% endif %}
+ {% endwith %}
+ {% endif %}
{% endblock request_summary%}
\ No newline at end of file
diff --git a/src/registrar/templatetags/custom_filters.py b/src/registrar/templatetags/custom_filters.py
index 50ff6c842..bd977d581 100644
--- a/src/registrar/templatetags/custom_filters.py
+++ b/src/registrar/templatetags/custom_filters.py
@@ -268,17 +268,17 @@ def display_requesting_entity(domain_request):
Boise, ID
"""
display = ""
- if domain_request.portfolio and domain_request.organization_name == domain_request.portfolio.organization_name:
- display = (
- f"{domain_request.portfolio.organization_name}\n"
- f"{domain_request.portfolio.city}, {domain_request.portfolio.state_territory}"
- )
- elif domain_request.sub_organization:
+ if domain_request.sub_organization:
display = domain_request.sub_organization
- elif domain_request.has_information_required_to_make_suborganization():
+ elif domain_request.requesting_entity_is_suborganization():
display = (
f"{domain_request.requested_suborganization}\n"
f"{domain_request.suborganization_city}, {domain_request.suborganization_state_territory}"
)
+ elif domain_request.requesting_entity_is_portfolio():
+ display = (
+ f"{domain_request.portfolio.organization_name}\n"
+ f"{domain_request.portfolio.city}, {domain_request.portfolio.state_territory}"
+ )
return display
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index 2469653bf..1ca2d2bcd 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -1685,9 +1685,9 @@ def test_requesting_entity_page_existing_suborg_submission(self):
form = response.forms[0]
# Test selecting an existing suborg
- form["portfolio_requesting_entity-is_suborganization"] = True
+ form["portfolio_requesting_entity-requesting_entity_is_suborganization"] = True
form["portfolio_requesting_entity-sub_organization"] = f"{self.suborganization.id}"
- form["portfolio_requesting_entity-is_custom_suborganization"] = False
+ form["portfolio_requesting_entity-is_requesting_new_suborganization"] = False
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@@ -1718,8 +1718,8 @@ def test_requesting_entity_page_new_suborg_submission(self):
form = response.forms[0]
# Test selecting an existing suborg
- form["portfolio_requesting_entity-is_suborganization"] = True
- form["portfolio_requesting_entity-is_custom_suborganization"] = True
+ form["portfolio_requesting_entity-requesting_entity_is_suborganization"] = True
+ form["portfolio_requesting_entity-is_requesting_new_suborganization"] = True
form["portfolio_requesting_entity-sub_organization"] = ""
form["portfolio_requesting_entity-requested_suborganization"] = "moon"
@@ -1759,7 +1759,7 @@ def test_requesting_entity_page_organization_submission(self):
form = response.forms[0]
# Test selecting an existing suborg
- form["portfolio_requesting_entity-is_suborganization"] = False
+ form["portfolio_requesting_entity-requesting_entity_is_suborganization"] = False
session_id = self.app.cookies[settings.SESSION_COOKIE_NAME]
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
@@ -1784,7 +1784,7 @@ def test_requesting_entity_page_errors(self):
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-requesting_entity_is_suborganization"] = True
form["portfolio_requesting_entity-sub_organization"] = ""
response = form.submit()
@@ -1792,7 +1792,7 @@ def test_requesting_entity_page_errors(self):
self.assertContains(response, "Select a suborganization.", status_code=200)
# Test missing custom suborganization details
- form["portfolio_requesting_entity-is_custom_suborganization"] = True
+ form["portfolio_requesting_entity-is_requesting_new_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)
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index d39833fab..16ca7a69f 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -596,13 +596,13 @@ def save(self, forms: list):
if the user selects one."""
requesting_entity_form = forms[1]
cleaned_data = requesting_entity_form.cleaned_data
- is_suborganization = cleaned_data.get("is_suborganization")
+ requesting_entity_is_suborganization = cleaned_data.get("requesting_entity_is_suborganization")
sub_organization = cleaned_data.get("sub_organization")
requested_suborganization = cleaned_data.get("requested_suborganization")
# If no suborganization presently exists but the user filled out
# org information then create a suborg automatically.
- if is_suborganization and (sub_organization or requested_suborganization):
+ if requesting_entity_is_suborganization and (sub_organization or requested_suborganization):
# Cleanup the organization name field, as this isn't for suborganizations.
self.domain_request.organization_name = None
self.domain_request.sub_organization = sub_organization
From ee71ba4c48d55648dfbbabb6763fe8aeb6d57deb Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 11:59:24 -0600
Subject: [PATCH 28/47] cleanup
---
src/registrar/assets/js/get-gov.js | 6 +-
src/registrar/forms/domain_request_wizard.py | 19 +-
...tion_requested_suborganization_and_more.py | 180 ------------------
src/registrar/views/domain_request.py | 2 -
4 files changed, 13 insertions(+), 194 deletions(-)
delete mode 100644 src/registrar/migrations/0135_domaininformation_requested_suborganization_and_more.py
diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js
index 6182bc828..81bb30e38 100644
--- a/src/registrar/assets/js/get-gov.js
+++ b/src/registrar/assets/js/get-gov.js
@@ -22,8 +22,7 @@ var SUCCESS = "success";
*
*/
const hideElement = (element) => {
- if (element && !element.classList.contains("display-none"))
- element.classList.add('display-none');
+ element.classList.add('display-none');
};
/**
@@ -31,8 +30,7 @@ const hideElement = (element) => {
*
*/
const showElement = (element) => {
- if (element && element.classList.contains("display-none"))
- element.classList.remove('display-none');
+ element.classList.remove('display-none');
};
/**
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index e6188eb33..2bc31fc37 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -27,21 +27,21 @@ class RequestingEntityForm(RegistrarForm):
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."""
- # Add a hidden field to store if the user is requesting a new suborganization
+ # Add a hidden field to store if the user is requesting a new suborganization.
+ # This hidden boolean is used for our javascript to communicate to us and to it.
+ # If true, the suborganization form will auto select a js value "Other".
+ # If this selection is made on the form (tracked by js), then it will toggle the form value of this.
+ # In other words, this essentially tracks if the suborganization field == "Other".
+ # "Other" is just an imaginary value that is otherwise invalid.
+ # Note the logic in `def clean` and line 2744 in get-gov.js
is_requesting_new_suborganization = forms.BooleanField(required=False, widget=forms.HiddenInput())
sub_organization = forms.ModelChoiceField(
label="Suborganization name",
- # not required because this field won't be filled out unless
- # it is a federal agency. Use clean to check programatically
- # if it has been filled in when required.
required=False,
queryset=Suborganization.objects.none(),
empty_label="--Select--",
)
-
- # We are using the current sub_organization naming convention here.
- # We may want to refactor this to suborganization eventually.
requested_suborganization = forms.CharField(
label="Requested suborganization",
required=False,
@@ -147,7 +147,10 @@ def __init__(self, *args, **kwargs):
@property
def form_is_checked(self):
"""
- Determines the initial checked state of the form based on the domain_request's attributes.
+ Determines if the requesting entity is a suborganization, or a portfolio.
+ For suborganizations, users have the ability to request a new one if the
+ desired suborg doesn't exist. We expose additional fields that denote this,
+ like `requested_suborganization`. So we also check on those.
"""
if self.domain_request.requesting_entity_is_suborganization():
diff --git a/src/registrar/migrations/0135_domaininformation_requested_suborganization_and_more.py b/src/registrar/migrations/0135_domaininformation_requested_suborganization_and_more.py
deleted file mode 100644
index b82619c78..000000000
--- a/src/registrar/migrations/0135_domaininformation_requested_suborganization_and_more.py
+++ /dev/null
@@ -1,180 +0,0 @@
-# Generated by Django 4.2.10 on 2024-10-24 16:30
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- (
- "registrar",
- "0134_rename_portfolio_additional_permissions_portfolioinvitation_additional_permissions_and_more",
- ),
- ]
-
- operations = [
- migrations.AddField(
- model_name="domaininformation",
- name="requested_suborganization",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domaininformation",
- name="suborganization_city",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domaininformation",
- name="suborganization_state_territory",
- field=models.CharField(
- blank=True,
- choices=[
- ("AL", "Alabama (AL)"),
- ("AK", "Alaska (AK)"),
- ("AS", "American Samoa (AS)"),
- ("AZ", "Arizona (AZ)"),
- ("AR", "Arkansas (AR)"),
- ("CA", "California (CA)"),
- ("CO", "Colorado (CO)"),
- ("CT", "Connecticut (CT)"),
- ("DE", "Delaware (DE)"),
- ("DC", "District of Columbia (DC)"),
- ("FL", "Florida (FL)"),
- ("GA", "Georgia (GA)"),
- ("GU", "Guam (GU)"),
- ("HI", "Hawaii (HI)"),
- ("ID", "Idaho (ID)"),
- ("IL", "Illinois (IL)"),
- ("IN", "Indiana (IN)"),
- ("IA", "Iowa (IA)"),
- ("KS", "Kansas (KS)"),
- ("KY", "Kentucky (KY)"),
- ("LA", "Louisiana (LA)"),
- ("ME", "Maine (ME)"),
- ("MD", "Maryland (MD)"),
- ("MA", "Massachusetts (MA)"),
- ("MI", "Michigan (MI)"),
- ("MN", "Minnesota (MN)"),
- ("MS", "Mississippi (MS)"),
- ("MO", "Missouri (MO)"),
- ("MT", "Montana (MT)"),
- ("NE", "Nebraska (NE)"),
- ("NV", "Nevada (NV)"),
- ("NH", "New Hampshire (NH)"),
- ("NJ", "New Jersey (NJ)"),
- ("NM", "New Mexico (NM)"),
- ("NY", "New York (NY)"),
- ("NC", "North Carolina (NC)"),
- ("ND", "North Dakota (ND)"),
- ("MP", "Northern Mariana Islands (MP)"),
- ("OH", "Ohio (OH)"),
- ("OK", "Oklahoma (OK)"),
- ("OR", "Oregon (OR)"),
- ("PA", "Pennsylvania (PA)"),
- ("PR", "Puerto Rico (PR)"),
- ("RI", "Rhode Island (RI)"),
- ("SC", "South Carolina (SC)"),
- ("SD", "South Dakota (SD)"),
- ("TN", "Tennessee (TN)"),
- ("TX", "Texas (TX)"),
- ("UM", "United States Minor Outlying Islands (UM)"),
- ("UT", "Utah (UT)"),
- ("VT", "Vermont (VT)"),
- ("VI", "Virgin Islands (VI)"),
- ("VA", "Virginia (VA)"),
- ("WA", "Washington (WA)"),
- ("WV", "West Virginia (WV)"),
- ("WI", "Wisconsin (WI)"),
- ("WY", "Wyoming (WY)"),
- ("AA", "Armed Forces Americas (AA)"),
- ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
- ("AP", "Armed Forces Pacific (AP)"),
- ],
- max_length=2,
- null=True,
- verbose_name="state, territory, or military post",
- ),
- ),
- migrations.AddField(
- model_name="domainrequest",
- name="requested_suborganization",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domainrequest",
- name="suborganization_city",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domainrequest",
- name="suborganization_state_territory",
- field=models.CharField(
- blank=True,
- choices=[
- ("AL", "Alabama (AL)"),
- ("AK", "Alaska (AK)"),
- ("AS", "American Samoa (AS)"),
- ("AZ", "Arizona (AZ)"),
- ("AR", "Arkansas (AR)"),
- ("CA", "California (CA)"),
- ("CO", "Colorado (CO)"),
- ("CT", "Connecticut (CT)"),
- ("DE", "Delaware (DE)"),
- ("DC", "District of Columbia (DC)"),
- ("FL", "Florida (FL)"),
- ("GA", "Georgia (GA)"),
- ("GU", "Guam (GU)"),
- ("HI", "Hawaii (HI)"),
- ("ID", "Idaho (ID)"),
- ("IL", "Illinois (IL)"),
- ("IN", "Indiana (IN)"),
- ("IA", "Iowa (IA)"),
- ("KS", "Kansas (KS)"),
- ("KY", "Kentucky (KY)"),
- ("LA", "Louisiana (LA)"),
- ("ME", "Maine (ME)"),
- ("MD", "Maryland (MD)"),
- ("MA", "Massachusetts (MA)"),
- ("MI", "Michigan (MI)"),
- ("MN", "Minnesota (MN)"),
- ("MS", "Mississippi (MS)"),
- ("MO", "Missouri (MO)"),
- ("MT", "Montana (MT)"),
- ("NE", "Nebraska (NE)"),
- ("NV", "Nevada (NV)"),
- ("NH", "New Hampshire (NH)"),
- ("NJ", "New Jersey (NJ)"),
- ("NM", "New Mexico (NM)"),
- ("NY", "New York (NY)"),
- ("NC", "North Carolina (NC)"),
- ("ND", "North Dakota (ND)"),
- ("MP", "Northern Mariana Islands (MP)"),
- ("OH", "Ohio (OH)"),
- ("OK", "Oklahoma (OK)"),
- ("OR", "Oregon (OR)"),
- ("PA", "Pennsylvania (PA)"),
- ("PR", "Puerto Rico (PR)"),
- ("RI", "Rhode Island (RI)"),
- ("SC", "South Carolina (SC)"),
- ("SD", "South Dakota (SD)"),
- ("TN", "Tennessee (TN)"),
- ("TX", "Texas (TX)"),
- ("UM", "United States Minor Outlying Islands (UM)"),
- ("UT", "Utah (UT)"),
- ("VT", "Vermont (VT)"),
- ("VI", "Virgin Islands (VI)"),
- ("VA", "Virginia (VA)"),
- ("WA", "Washington (WA)"),
- ("WV", "West Virginia (WV)"),
- ("WI", "Wisconsin (WI)"),
- ("WY", "Wyoming (WY)"),
- ("AA", "Armed Forces Americas (AA)"),
- ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
- ("AP", "Armed Forces Pacific (AP)"),
- ],
- max_length=2,
- null=True,
- verbose_name="state, territory, or military post",
- ),
- ),
- ]
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index 16ca7a69f..209a6c100 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -600,8 +600,6 @@ def save(self, forms: list):
sub_organization = cleaned_data.get("sub_organization")
requested_suborganization = cleaned_data.get("requested_suborganization")
- # If no suborganization presently exists but the user filled out
- # org information then create a suborg automatically.
if requesting_entity_is_suborganization and (sub_organization or requested_suborganization):
# Cleanup the organization name field, as this isn't for suborganizations.
self.domain_request.organization_name = None
From a94a5b20759f8cb3a5f9094fd4b512e7a81fdf3f Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 11:59:53 -0600
Subject: [PATCH 29/47] Readd migration after merge
---
...tion_requested_suborganization_and_more.py | 177 ++++++++++++++++++
1 file changed, 177 insertions(+)
create mode 100644 src/registrar/migrations/0136_domaininformation_requested_suborganization_and_more.py
diff --git a/src/registrar/migrations/0136_domaininformation_requested_suborganization_and_more.py b/src/registrar/migrations/0136_domaininformation_requested_suborganization_and_more.py
new file mode 100644
index 000000000..dc6ec9d5d
--- /dev/null
+++ b/src/registrar/migrations/0136_domaininformation_requested_suborganization_and_more.py
@@ -0,0 +1,177 @@
+# Generated by Django 4.2.10 on 2024-10-30 17:59
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("registrar", "0135_alter_federalagency_agency_and_more"),
+ ]
+
+ operations = [
+ migrations.AddField(
+ model_name="domaininformation",
+ name="requested_suborganization",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domaininformation",
+ name="suborganization_city",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domaininformation",
+ name="suborganization_state_territory",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ ("AL", "Alabama (AL)"),
+ ("AK", "Alaska (AK)"),
+ ("AS", "American Samoa (AS)"),
+ ("AZ", "Arizona (AZ)"),
+ ("AR", "Arkansas (AR)"),
+ ("CA", "California (CA)"),
+ ("CO", "Colorado (CO)"),
+ ("CT", "Connecticut (CT)"),
+ ("DE", "Delaware (DE)"),
+ ("DC", "District of Columbia (DC)"),
+ ("FL", "Florida (FL)"),
+ ("GA", "Georgia (GA)"),
+ ("GU", "Guam (GU)"),
+ ("HI", "Hawaii (HI)"),
+ ("ID", "Idaho (ID)"),
+ ("IL", "Illinois (IL)"),
+ ("IN", "Indiana (IN)"),
+ ("IA", "Iowa (IA)"),
+ ("KS", "Kansas (KS)"),
+ ("KY", "Kentucky (KY)"),
+ ("LA", "Louisiana (LA)"),
+ ("ME", "Maine (ME)"),
+ ("MD", "Maryland (MD)"),
+ ("MA", "Massachusetts (MA)"),
+ ("MI", "Michigan (MI)"),
+ ("MN", "Minnesota (MN)"),
+ ("MS", "Mississippi (MS)"),
+ ("MO", "Missouri (MO)"),
+ ("MT", "Montana (MT)"),
+ ("NE", "Nebraska (NE)"),
+ ("NV", "Nevada (NV)"),
+ ("NH", "New Hampshire (NH)"),
+ ("NJ", "New Jersey (NJ)"),
+ ("NM", "New Mexico (NM)"),
+ ("NY", "New York (NY)"),
+ ("NC", "North Carolina (NC)"),
+ ("ND", "North Dakota (ND)"),
+ ("MP", "Northern Mariana Islands (MP)"),
+ ("OH", "Ohio (OH)"),
+ ("OK", "Oklahoma (OK)"),
+ ("OR", "Oregon (OR)"),
+ ("PA", "Pennsylvania (PA)"),
+ ("PR", "Puerto Rico (PR)"),
+ ("RI", "Rhode Island (RI)"),
+ ("SC", "South Carolina (SC)"),
+ ("SD", "South Dakota (SD)"),
+ ("TN", "Tennessee (TN)"),
+ ("TX", "Texas (TX)"),
+ ("UM", "United States Minor Outlying Islands (UM)"),
+ ("UT", "Utah (UT)"),
+ ("VT", "Vermont (VT)"),
+ ("VI", "Virgin Islands (VI)"),
+ ("VA", "Virginia (VA)"),
+ ("WA", "Washington (WA)"),
+ ("WV", "West Virginia (WV)"),
+ ("WI", "Wisconsin (WI)"),
+ ("WY", "Wyoming (WY)"),
+ ("AA", "Armed Forces Americas (AA)"),
+ ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
+ ("AP", "Armed Forces Pacific (AP)"),
+ ],
+ max_length=2,
+ null=True,
+ verbose_name="state, territory, or military post",
+ ),
+ ),
+ migrations.AddField(
+ model_name="domainrequest",
+ name="requested_suborganization",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domainrequest",
+ name="suborganization_city",
+ field=models.CharField(blank=True, null=True),
+ ),
+ migrations.AddField(
+ model_name="domainrequest",
+ name="suborganization_state_territory",
+ field=models.CharField(
+ blank=True,
+ choices=[
+ ("AL", "Alabama (AL)"),
+ ("AK", "Alaska (AK)"),
+ ("AS", "American Samoa (AS)"),
+ ("AZ", "Arizona (AZ)"),
+ ("AR", "Arkansas (AR)"),
+ ("CA", "California (CA)"),
+ ("CO", "Colorado (CO)"),
+ ("CT", "Connecticut (CT)"),
+ ("DE", "Delaware (DE)"),
+ ("DC", "District of Columbia (DC)"),
+ ("FL", "Florida (FL)"),
+ ("GA", "Georgia (GA)"),
+ ("GU", "Guam (GU)"),
+ ("HI", "Hawaii (HI)"),
+ ("ID", "Idaho (ID)"),
+ ("IL", "Illinois (IL)"),
+ ("IN", "Indiana (IN)"),
+ ("IA", "Iowa (IA)"),
+ ("KS", "Kansas (KS)"),
+ ("KY", "Kentucky (KY)"),
+ ("LA", "Louisiana (LA)"),
+ ("ME", "Maine (ME)"),
+ ("MD", "Maryland (MD)"),
+ ("MA", "Massachusetts (MA)"),
+ ("MI", "Michigan (MI)"),
+ ("MN", "Minnesota (MN)"),
+ ("MS", "Mississippi (MS)"),
+ ("MO", "Missouri (MO)"),
+ ("MT", "Montana (MT)"),
+ ("NE", "Nebraska (NE)"),
+ ("NV", "Nevada (NV)"),
+ ("NH", "New Hampshire (NH)"),
+ ("NJ", "New Jersey (NJ)"),
+ ("NM", "New Mexico (NM)"),
+ ("NY", "New York (NY)"),
+ ("NC", "North Carolina (NC)"),
+ ("ND", "North Dakota (ND)"),
+ ("MP", "Northern Mariana Islands (MP)"),
+ ("OH", "Ohio (OH)"),
+ ("OK", "Oklahoma (OK)"),
+ ("OR", "Oregon (OR)"),
+ ("PA", "Pennsylvania (PA)"),
+ ("PR", "Puerto Rico (PR)"),
+ ("RI", "Rhode Island (RI)"),
+ ("SC", "South Carolina (SC)"),
+ ("SD", "South Dakota (SD)"),
+ ("TN", "Tennessee (TN)"),
+ ("TX", "Texas (TX)"),
+ ("UM", "United States Minor Outlying Islands (UM)"),
+ ("UT", "Utah (UT)"),
+ ("VT", "Vermont (VT)"),
+ ("VI", "Virgin Islands (VI)"),
+ ("VA", "Virginia (VA)"),
+ ("WA", "Washington (WA)"),
+ ("WV", "West Virginia (WV)"),
+ ("WI", "Wisconsin (WI)"),
+ ("WY", "Wyoming (WY)"),
+ ("AA", "Armed Forces Americas (AA)"),
+ ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
+ ("AP", "Armed Forces Pacific (AP)"),
+ ],
+ max_length=2,
+ null=True,
+ verbose_name="state, territory, or military post",
+ ),
+ ),
+ ]
From c465b7fb67c5ff2995a1cc30ecc4e062ba3a5acf Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:20:26 -0600
Subject: [PATCH 30/47] fix bug with form save
Since cleaned data was not being touched, the form was not resetting values correctly
---
src/registrar/assets/js/get-gov.js | 1 -
src/registrar/forms/domain_request_wizard.py | 4 ++-
src/registrar/tests/test_views_portfolio.py | 1 -
src/registrar/views/domain_request.py | 27 ++++++++++++--------
4 files changed, 20 insertions(+), 13 deletions(-)
diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js
index 81bb30e38..be658ad3e 100644
--- a/src/registrar/assets/js/get-gov.js
+++ b/src/registrar/assets/js/get-gov.js
@@ -2789,7 +2789,6 @@ document.addEventListener('DOMContentLoaded', function() {
subOrgSelect.add(fakeOption);
}
- console.log(isRequestingSuborganization.value)
if (isRequestingSuborganization.value === "True") {
subOrgSelect.value = "other"
}
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 2bc31fc37..b27a004c0 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -111,7 +111,9 @@ def clean(self):
# Get the value of the yes/no checkbox from RequestingEntityYesNoForm.
# Since self.data stores this as a string, we need to convert "True" => True.
- requesting_entity_is_suborganization = self.data.get("portfolio_requesting_entity-requesting_entity_is_suborganization")
+ requesting_entity_is_suborganization = self.data.get(
+ "portfolio_requesting_entity-requesting_entity_is_suborganization"
+ )
if requesting_entity_is_suborganization == "True":
if is_requesting_new_suborganization:
# Validate custom suborganization fields
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index 1ca2d2bcd..45357cbf7 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -1717,7 +1717,6 @@ def test_requesting_entity_page_new_suborg_submission(self):
self.assertContains(response, "Who will use the domain you’re requesting?")
form = response.forms[0]
- # Test selecting an existing suborg
form["portfolio_requesting_entity-requesting_entity_is_suborganization"] = True
form["portfolio_requesting_entity-is_requesting_new_suborganization"] = True
form["portfolio_requesting_entity-sub_organization"] = ""
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index 209a6c100..7fb2d9b16 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -594,25 +594,32 @@ 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."""
+ yesno_form = forms[0]
requesting_entity_form = forms[1]
+
+ yesno_cleaned_data = yesno_form.cleaned_data
+ requesting_entity_is_suborganization = yesno_cleaned_data.get("requesting_entity_is_suborganization")
+
cleaned_data = requesting_entity_form.cleaned_data
- requesting_entity_is_suborganization = cleaned_data.get("requesting_entity_is_suborganization")
sub_organization = cleaned_data.get("sub_organization")
requested_suborganization = cleaned_data.get("requested_suborganization")
if requesting_entity_is_suborganization and (sub_organization or requested_suborganization):
# Cleanup the organization name field, as this isn't for suborganizations.
- self.domain_request.organization_name = None
- self.domain_request.sub_organization = sub_organization
+ requesting_entity_form.cleaned_data.update({"organization_name": None})
else:
# If the user doesn't intend to create a suborg, simply don't make one and do some data cleanup
- if self.domain_request.portfolio:
- self.domain_request.organization_name = self.domain_request.portfolio.organization_name
-
- self.domain_request.sub_organization = None
- self.domain_request.requested_suborganization = None
- self.domain_request.suborganization_city = None
- self.domain_request.suborganization_state_territory = None
+ requesting_entity_form.cleaned_data.update(
+ {
+ "organization_name": (
+ self.domain_request.portfolio.organization_name if self.domain_request.portfolio else None
+ ),
+ "sub_organization": None,
+ "requested_suborganization": None,
+ "suborganization_city": None,
+ "suborganization_state_territory": None,
+ }
+ )
super().save(forms)
From dfb59a6a9d3ab4be10a941dbc32db390594f9a52 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:21:35 -0600
Subject: [PATCH 31/47] lint model
---
src/registrar/models/domain_request.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index a04cea526..96d98ed65 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1152,7 +1152,9 @@ def is_requesting_new_suborganization(self) -> bool:
# If a suborganization already exists, it can't possibly be a new one
if self.sub_organization:
return False
- return bool(self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory)
+ return bool(
+ self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory
+ )
# ## Form unlocking steps ## #
#
From 4f1febf24820589b1339c5cb8b92bbbcef2b8eaa Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:27:04 -0600
Subject: [PATCH 32/47] Update domain_request.py
---
src/registrar/models/domain_request.py | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index 96d98ed65..c8c21ea33 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1127,7 +1127,10 @@ def reject_with_prejudice(self):
def requesting_entity_is_portfolio(self) -> bool:
"""Determines if this record is requesting that a portfolio be their organization."""
- return self.portfolio and self.organization_name == self.portfolio.organization_name
+ if self.portfolio and self.organization_name == self.portfolio.organization_name:
+ return True
+ else:
+ return False
def requesting_entity_is_suborganization(self) -> bool:
"""Used to determine if this domain request is also requesting that it be tied to a suborganization.
@@ -1152,9 +1155,11 @@ def is_requesting_new_suborganization(self) -> bool:
# If a suborganization already exists, it can't possibly be a new one
if self.sub_organization:
return False
- return bool(
- self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory
- )
+
+ if self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory:
+ return True
+ else:
+ return False
# ## Form unlocking steps ## #
#
From bb9cb527d203ece5049135c4d59f437c8f86d567 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:33:30 -0600
Subject: [PATCH 33/47] Cleanup bool logic to be more concise
---
src/registrar/models/domain_request.py | 27 +++++++++++++-------------
1 file changed, 13 insertions(+), 14 deletions(-)
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index c8c21ea33..48b9f23c7 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1137,13 +1137,10 @@ def requesting_entity_is_suborganization(self) -> bool:
Checks if this record has a suborganization or not by checking if a suborganization exists,
and if it doesn't, determining if properties like requested_suborganization exist.
"""
-
- if self.portfolio:
- if self.sub_organization:
- return True
- if self.is_requesting_new_suborganization():
- return True
- return False
+ if self.portfolio and (self.sub_organization or self.is_requesting_new_suborganization()):
+ return True
+ else:
+ return False
def is_requesting_new_suborganization(self) -> bool:
"""Used on the requesting entity form to determine if a user is trying to request
@@ -1152,11 +1149,15 @@ def is_requesting_new_suborganization(self) -> bool:
This only occurs when no suborganization is selected, but they've filled out
the requested_suborganization, suborganization_city, and suborganization_state_territory fields.
"""
- # If a suborganization already exists, it can't possibly be a new one
- if self.sub_organization:
- return False
- if self.requested_suborganization and self.suborganization_city and self.suborganization_state_territory:
+ # If a suborganization already exists, it can't possibly be a new one.
+ # As well, we need all required fields to exist.
+ required_fields = [
+ self.requested_suborganization,
+ self.suborganization_city,
+ self.suborganization_state_territory
+ ]
+ if not self.sub_organization and all(required_fields):
return True
else:
return False
@@ -1167,9 +1168,7 @@ def is_requesting_new_suborganization(self) -> bool:
def unlock_requesting_entity(self) -> bool:
"""Unlocks the requesting entity step"""
- if self.requesting_entity_is_suborganization():
- return True
- elif self.requesting_entity_is_portfolio():
+ if self.requesting_entity_is_suborganization() or self.requesting_entity_is_portfolio():
return True
else:
return False
From 98842c1aa0c5e71cb6d861c19314a3d9895e5a11 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 14:40:25 -0600
Subject: [PATCH 34/47] add comments
---
src/registrar/forms/domain_request_wizard.py | 3 ++-
src/registrar/views/domain_request.py | 7 +++++--
2 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index b27a004c0..c188a7ab0 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -154,7 +154,8 @@ def form_is_checked(self):
desired suborg doesn't exist. We expose additional fields that denote this,
like `requested_suborganization`. So we also check on those.
"""
-
+ # True means that the requesting entity is a suborganization,
+ # whereas False means that the requesting entity is a portfolio.
if self.domain_request.requesting_entity_is_suborganization():
return True
elif self.domain_request.requesting_entity_is_portfolio():
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index 7fb2d9b16..41e9a07b8 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -594,16 +594,19 @@ 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."""
- yesno_form = forms[0]
- requesting_entity_form = forms[1]
+ # Get the yes/no dropdown value
+ yesno_form = forms[0]
yesno_cleaned_data = yesno_form.cleaned_data
requesting_entity_is_suborganization = yesno_cleaned_data.get("requesting_entity_is_suborganization")
+ # Get the suborg value, and the requested suborg value
+ requesting_entity_form = forms[1]
cleaned_data = requesting_entity_form.cleaned_data
sub_organization = cleaned_data.get("sub_organization")
requested_suborganization = cleaned_data.get("requested_suborganization")
+ # Do some data cleanup, depending on what option was checked
if requesting_entity_is_suborganization and (sub_organization or requested_suborganization):
# Cleanup the organization name field, as this isn't for suborganizations.
requesting_entity_form.cleaned_data.update({"organization_name": None})
From 603e2eb767a031df7d5705e9db1ae6d138fea771 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 15:28:05 -0600
Subject: [PATCH 35/47] simplify javascript (a lot)
---
src/registrar/assets/js/get-gov.js | 87 +++++++++++-------------------
1 file changed, 31 insertions(+), 56 deletions(-)
diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js
index be658ad3e..2bbcc781d 100644
--- a/src/registrar/assets/js/get-gov.js
+++ b/src/registrar/assets/js/get-gov.js
@@ -2742,78 +2742,53 @@ document.addEventListener('DOMContentLoaded', function() {
(function handleRequestingEntityFieldset() {
// Sadly, these ugly ids are the auto generated with this prefix
const formPrefix = "portfolio_requesting_entity"
-
- // This determines if we are on the requesting entity page or not.
- const isSubOrgFieldset = document.getElementById(`id_${formPrefix}-requesting_entity_is_suborganization__fieldset`);
- if (!isSubOrgFieldset) return;
-
- // Get the is_suborganization radio buttons
- const isSuborgRadios = isSubOrgFieldset.querySelectorAll(`input[name="${formPrefix}-requesting_entity_is_suborganization"]`);
- const subOrgSelect = document.getElementById(`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 radioFieldset = document.getElementById(`id_${formPrefix}-requesting_entity_is_suborganization__fieldset`);
+ const radios = radioFieldset?.querySelectorAll(`input[name="${formPrefix}-requesting_entity_is_suborganization"]`);
+ const select = document.getElementById(`id_${formPrefix}-sub_organization`);
const suborganizationContainer = document.getElementById("suborganization-container");
const suborganizationDetailsContainer = document.getElementById("suborganization-container__details");
+ if (!radios || !select || !suborganizationContainer || !suborganizationDetailsContainer) return;
- // This variable determines if the user is trying to *create* a new suborganization or not.
- var isRequestingSuborganization = document.getElementById(`id_${formPrefix}-is_requesting_new_suborganization`)
+ // requestingSuborganization: This just broadly determines if they're requesting a suborg at all
+ // requestingNewSuborganization: This variable determines if the user is trying to *create* a new suborganization or not.
+ var requestingSuborganization = false;
+ var requestingNewSuborganization = document.getElementById(`id_${formPrefix}-is_requesting_new_suborganization`);
- // Don't do anything if we are missing crucial page elements
- if (!isSuborgRadios || !subOrgSelect || !suborganizationContainer || !suborganizationDetailsContainer) return;
-
- // Function to toggle suborganization based on is_suborganization selection
function toggleSuborganization(radio) {
- if (radio && radio.checked && radio.value === "True") {
+ requestingSuborganization = radio?.checked && radio.value === "True";
+ if (requestingSuborganization) {
showElement(suborganizationContainer);
-
- // Handle custom suborganizations
- if (subOrgSelect.value === "other") {
- showElement(suborganizationDetailsContainer);
- isRequestingSuborganization.value = "True";
- } else {
- hideElement(suborganizationDetailsContainer);
- isRequestingSuborganization.value = "False";
- }
- } else {
+ }else {
hideElement(suborganizationContainer);
+ }
+ }
+
+ function toggleSuborganizationDetails() {
+ if (requestingSuborganization && select.value === "other") {
+ showElement(suborganizationDetailsContainer);
+ requestingNewSuborganization.value = "True";
+ }else {
hideElement(suborganizationDetailsContainer);
+ requestingNewSuborganization.value = "False";
}
- };
+ }
- // Add fake "other" option to sub_organization select
- if (subOrgSelect && !Array.from(subOrgSelect.options).some(option => option.value === "other")) {
- const fakeOption = document.createElement("option");
- fakeOption.value = "other";
- fakeOption.text = "Other (enter your organization manually)";
- subOrgSelect.add(fakeOption);
+ // Add fake "other" option to sub_organization select
+ if (select && !Array.from(select.options).some(option => option.value === "other")) {
+ select.add(new Option("Other (enter your organization manually)", "other"));
}
- if (isRequestingSuborganization.value === "True") {
- subOrgSelect.value = "other"
+ if (requestingNewSuborganization.value === "True") {
+ select.value = "other";
}
- // Add event listener to is_suborganization radio buttons
- isSuborgRadios.forEach(radio => {
- // Run this here for initial display.
- // Since there are only two radio buttons and since this has (practically speaking) no performance impact, this is fine to do.
+ // Add event listener to is_suborganization radio buttons, and run for initial display
+ radios.forEach(radio => {
toggleSuborganization(radio);
-
- // Add an event listener to each to show/hide the relevant fields
- radio.addEventListener("click", () => {
- toggleSuborganization(radio);
- });
+ radio.addEventListener("click", () => toggleSuborganization(radio));
});
// Add event listener to the suborg dropdown to show/hide the suborg details section
- subOrgSelect.addEventListener("change", () => {
- // Handle the custom suborganization field
- if (subOrgSelect.value === "other") {
- showElement(suborganizationDetailsContainer);
- isRequestingSuborganization.value = "True";
- } else {
- hideElement(suborganizationDetailsContainer);
- isRequestingSuborganization.value = "False";
- }
- });
+ toggleSuborganizationDetails();
+ select.addEventListener("change", () => toggleSuborganizationDetails());
})();
From b01e707b82a8de44e6781c41871bc2b59af895c2 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Wed, 30 Oct 2024 15:55:39 -0600
Subject: [PATCH 36/47] further simplify
---
src/registrar/assets/js/get-gov.js | 35 ++++++++++--------------------
1 file changed, 11 insertions(+), 24 deletions(-)
diff --git a/src/registrar/assets/js/get-gov.js b/src/registrar/assets/js/get-gov.js
index 2bbcc781d..d462af5ac 100644
--- a/src/registrar/assets/js/get-gov.js
+++ b/src/registrar/assets/js/get-gov.js
@@ -2745,32 +2745,20 @@ document.addEventListener('DOMContentLoaded', function() {
const radioFieldset = document.getElementById(`id_${formPrefix}-requesting_entity_is_suborganization__fieldset`);
const radios = radioFieldset?.querySelectorAll(`input[name="${formPrefix}-requesting_entity_is_suborganization"]`);
const select = document.getElementById(`id_${formPrefix}-sub_organization`);
- const suborganizationContainer = document.getElementById("suborganization-container");
- const suborganizationDetailsContainer = document.getElementById("suborganization-container__details");
- if (!radios || !select || !suborganizationContainer || !suborganizationDetailsContainer) return;
+ const suborgContainer = document.getElementById("suborganization-container");
+ const suborgDetailsContainer = document.getElementById("suborganization-container__details");
+ if (!radios || !select || !suborgContainer || !suborgDetailsContainer) return;
// requestingSuborganization: This just broadly determines if they're requesting a suborg at all
// requestingNewSuborganization: This variable determines if the user is trying to *create* a new suborganization or not.
- var requestingSuborganization = false;
+ var requestingSuborganization = Array.from(radios).find(radio => radio.checked)?.value === "True";
var requestingNewSuborganization = document.getElementById(`id_${formPrefix}-is_requesting_new_suborganization`);
- function toggleSuborganization(radio) {
- requestingSuborganization = radio?.checked && radio.value === "True";
- if (requestingSuborganization) {
- showElement(suborganizationContainer);
- }else {
- hideElement(suborganizationContainer);
- }
- }
-
- function toggleSuborganizationDetails() {
- if (requestingSuborganization && select.value === "other") {
- showElement(suborganizationDetailsContainer);
- requestingNewSuborganization.value = "True";
- }else {
- hideElement(suborganizationDetailsContainer);
- requestingNewSuborganization.value = "False";
- }
+ function toggleSuborganization(radio=null) {
+ if (radio != null) requestingSuborganization = radio?.checked && radio.value === "True";
+ requestingSuborganization ? showElement(suborgContainer) : hideElement(suborgContainer);
+ requestingNewSuborganization.value = requestingSuborganization && select.value === "other" ? "True" : "False";
+ requestingNewSuborganization.value === "True" ? showElement(suborgDetailsContainer) : hideElement(suborgDetailsContainer);
}
// Add fake "other" option to sub_organization select
@@ -2783,12 +2771,11 @@ document.addEventListener('DOMContentLoaded', function() {
}
// Add event listener to is_suborganization radio buttons, and run for initial display
+ toggleSuborganization();
radios.forEach(radio => {
- toggleSuborganization(radio);
radio.addEventListener("click", () => toggleSuborganization(radio));
});
// Add event listener to the suborg dropdown to show/hide the suborg details section
- toggleSuborganizationDetails();
- select.addEventListener("change", () => toggleSuborganizationDetails());
+ select.addEventListener("change", () => toggleSuborganization());
})();
From f0ba59611e60e90f64e41c4a376a09907328d9ea Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 31 Oct 2024 08:14:37 -0600
Subject: [PATCH 37/47] Initial logic
---
src/registrar/models/domain_request.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index 48b9f23c7..c2a8df257 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1155,7 +1155,7 @@ def is_requesting_new_suborganization(self) -> bool:
required_fields = [
self.requested_suborganization,
self.suborganization_city,
- self.suborganization_state_territory
+ self.suborganization_state_territory,
]
if not self.sub_organization and all(required_fields):
return True
From 510938430b2f389d148c8497fde0168faf4b3473 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 31 Oct 2024 10:05:28 -0600
Subject: [PATCH 38/47] Add some comments
---
src/registrar/forms/domain_request_wizard.py | 5 +++++
src/registrar/models/domain_request.py | 7 +++++--
2 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index c188a7ab0..16e5b66ea 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -27,6 +27,8 @@ class RequestingEntityForm(RegistrarForm):
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."""
+ # IMPORTANT: This is tied to DomainRequest.is_requesting_new_suborganization().
+ # This is due to the from_database method on DomainRequestWizard.
# Add a hidden field to store if the user is requesting a new suborganization.
# This hidden boolean is used for our javascript to communicate to us and to it.
# If true, the suborganization form will auto select a js value "Other".
@@ -134,6 +136,9 @@ class RequestingEntityYesNoForm(BaseYesNoForm):
# This first option will change dynamically
form_choices = ((False, "Current Organization"), (True, "A suborganization. (choose from list)"))
+
+ # IMPORTANT: This is tied to DomainRequest.is_requesting_new_suborganization().
+ # This is due to the from_database method on DomainRequestWizard.
field_name = "requesting_entity_is_suborganization"
def __init__(self, *args, **kwargs):
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index c2a8df257..c3fc5335d 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1126,7 +1126,8 @@ def reject_with_prejudice(self):
self.creator.restrict_user()
def requesting_entity_is_portfolio(self) -> bool:
- """Determines if this record is requesting that a portfolio be their organization."""
+ """Determines if this record is requesting that a portfolio be their organization.
+ Used for the RequestingEntity page."""
if self.portfolio and self.organization_name == self.portfolio.organization_name:
return True
else:
@@ -1136,6 +1137,7 @@ def requesting_entity_is_suborganization(self) -> bool:
"""Used to determine if this domain request is also requesting that it be tied to a suborganization.
Checks if this record has a suborganization or not by checking if a suborganization exists,
and if it doesn't, determining if properties like requested_suborganization exist.
+ Used for the RequestingEntity page.
"""
if self.portfolio and (self.sub_organization or self.is_requesting_new_suborganization()):
return True
@@ -1148,6 +1150,7 @@ def is_requesting_new_suborganization(self) -> bool:
This only occurs when no suborganization is selected, but they've filled out
the requested_suborganization, suborganization_city, and suborganization_state_territory fields.
+ Used for the RequestingEntity page.
"""
# If a suborganization already exists, it can't possibly be a new one.
@@ -1167,7 +1170,7 @@ def is_requesting_new_suborganization(self) -> bool:
# These methods control the conditions in which we should unlock certain domain wizard steps.
def unlock_requesting_entity(self) -> bool:
- """Unlocks the requesting entity step"""
+ """Unlocks the requesting entity step. Used for the RequestingEntity page."""
if self.requesting_entity_is_suborganization() or self.requesting_entity_is_portfolio():
return True
else:
From 1d345987ca9b85e17c0d63298a766ea23ae936b1 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 08:49:32 -0600
Subject: [PATCH 39/47] PR suggestions (part 1)
---
src/registrar/forms/domain_request_wizard.py | 19 +++++-----
...tion_requested_suborganization_and_more.py | 25 +++++++++++++
src/registrar/models/domain_information.py | 18 ----------
src/registrar/models/domain_request.py | 35 ++++++++++---------
.../templates/includes/header_extended.html | 2 +-
.../portfolio_request_review_steps.html | 2 +-
src/registrar/views/domain_request.py | 10 +++++-
7 files changed, 62 insertions(+), 49 deletions(-)
create mode 100644 src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 16e5b66ea..f74622b51 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -35,7 +35,7 @@ class RequestingEntityForm(RegistrarForm):
# If this selection is made on the form (tracked by js), then it will toggle the form value of this.
# In other words, this essentially tracks if the suborganization field == "Other".
# "Other" is just an imaginary value that is otherwise invalid.
- # Note the logic in `def clean` and line 2744 in get-gov.js
+ # Note the logic in `def clean` and `handleRequestingEntityFieldset` in get-gov.js
is_requesting_new_suborganization = forms.BooleanField(required=False, widget=forms.HiddenInput())
sub_organization = forms.ModelChoiceField(
@@ -43,24 +43,22 @@ class RequestingEntityForm(RegistrarForm):
required=False,
queryset=Suborganization.objects.none(),
empty_label="--Select--",
+ error_messages={
+ "required": ("Requesting entity is required.")
+ },
)
requested_suborganization = forms.CharField(
label="Requested suborganization",
required=False,
- error_messages={"required": "Enter the name of your organization."},
)
suborganization_city = forms.CharField(
label="City",
required=False,
- error_messages={"required": "Enter the city where your organization is located."},
)
suborganization_state_territory = forms.ChoiceField(
label="State, territory, or military post",
required=False,
choices=[("", "--Select--")] + DomainRequest.StateTerritoryChoices.choices,
- error_messages={
- "required": ("Select the state, territory, or military post where your organization is located.")
- },
)
def __init__(self, *args, **kwargs):
@@ -147,17 +145,16 @@ def __init__(self, *args, **kwargs):
if self.domain_request.portfolio:
self.form_choices = (
(False, self.domain_request.portfolio),
- (True, "A suborganization. (choose from list)"),
+ (True, "A suborganization (choose from list)"),
)
self.fields[self.field_name] = self.get_typed_choice_field()
@property
def form_is_checked(self):
"""
- Determines if the requesting entity is a suborganization, or a portfolio.
- For suborganizations, users have the ability to request a new one if the
- desired suborg doesn't exist. We expose additional fields that denote this,
- like `requested_suborganization`. So we also check on those.
+ Determines the initial checked state of the form.
+ Returns True (checked) if the requesting entity is a suborganization,
+ and False if it is a portfolio. Returns None if neither condition is met.
"""
# True means that the requesting entity is a suborganization,
# whereas False means that the requesting entity is a portfolio.
diff --git a/src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py b/src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py
new file mode 100644
index 000000000..dfa716ef4
--- /dev/null
+++ b/src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py
@@ -0,0 +1,25 @@
+# Generated by Django 4.2.10 on 2024-11-01 14:25
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ("registrar", "0136_domaininformation_requested_suborganization_and_more"),
+ ]
+
+ operations = [
+ migrations.RemoveField(
+ model_name="domaininformation",
+ name="requested_suborganization",
+ ),
+ migrations.RemoveField(
+ model_name="domaininformation",
+ name="suborganization_city",
+ ),
+ migrations.RemoveField(
+ model_name="domaininformation",
+ name="suborganization_state_territory",
+ ),
+ ]
diff --git a/src/registrar/models/domain_information.py b/src/registrar/models/domain_information.py
index 7dc257b22..7dadf26ac 100644
--- a/src/registrar/models/domain_information.py
+++ b/src/registrar/models/domain_information.py
@@ -75,24 +75,6 @@ class Meta:
verbose_name="Suborganization",
)
- requested_suborganization = models.CharField(
- null=True,
- blank=True,
- )
-
- suborganization_city = models.CharField(
- null=True,
- blank=True,
- )
-
- suborganization_state_territory = models.CharField(
- max_length=2,
- choices=StateTerritoryChoices.choices,
- null=True,
- blank=True,
- verbose_name="state, territory, or military post",
- )
-
domain_request = models.OneToOneField(
"registrar.DomainRequest",
on_delete=models.PROTECT,
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index c3fc5335d..2a0c65505 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1127,30 +1127,30 @@ def reject_with_prejudice(self):
def requesting_entity_is_portfolio(self) -> bool:
"""Determines if this record is requesting that a portfolio be their organization.
- Used for the RequestingEntity page."""
+ Used for the RequestingEntity page.
+ Returns True if the portfolio exists and if organization_name matches portfolio.organization_name.
+ """
if self.portfolio and self.organization_name == self.portfolio.organization_name:
return True
- else:
- return False
+ return False
def requesting_entity_is_suborganization(self) -> bool:
"""Used to determine if this domain request is also requesting that it be tied to a suborganization.
- Checks if this record has a suborganization or not by checking if a suborganization exists,
- and if it doesn't, determining if properties like requested_suborganization exist.
- Used for the RequestingEntity page.
+ Returns True if portfolio exists and either sub_organization exists,
+ or if is_requesting_new_suborganization() is true.
+ Returns False otherwise.
"""
if self.portfolio and (self.sub_organization or self.is_requesting_new_suborganization()):
return True
- else:
- return False
+ return False
def is_requesting_new_suborganization(self) -> bool:
"""Used on the requesting entity form to determine if a user is trying to request
- a new suborganization using the domain request form.
+ a new suborganization using the domain request form, rather than one that already exists.
- This only occurs when no suborganization is selected, but they've filled out
- the requested_suborganization, suborganization_city, and suborganization_state_territory fields.
- Used for the RequestingEntity page.
+ Returns True if a sub_organization does not exist and if requested_suborganization,
+ suborganization_city, and suborganization_state_territory all exist.
+ Returns False otherwise.
"""
# If a suborganization already exists, it can't possibly be a new one.
@@ -1162,19 +1162,20 @@ def is_requesting_new_suborganization(self) -> bool:
]
if not self.sub_organization and all(required_fields):
return True
- else:
- return False
+ return False
# ## Form unlocking steps ## #
#
# These methods control the conditions in which we should unlock certain domain wizard steps.
def unlock_requesting_entity(self) -> bool:
- """Unlocks the requesting entity step. Used for the RequestingEntity page."""
+ """Unlocks the requesting entity step. Used for the RequestingEntity page.
+ Returns true if requesting_entity_is_suborganization() and requesting_entity_is_portfolio().
+ Returns False otherwise.
+ """
if self.requesting_entity_is_suborganization() or self.requesting_entity_is_portfolio():
return True
- else:
- return False
+ return False
# ## Form policies ## #
#
diff --git a/src/registrar/templates/includes/header_extended.html b/src/registrar/templates/includes/header_extended.html
index 7c0f55dc3..a954eb30f 100644
--- a/src/registrar/templates/includes/header_extended.html
+++ b/src/registrar/templates/includes/header_extended.html
@@ -93,7 +93,7 @@
{% endif %}
- {% if has_organization_members_flag %}
+ {% if has_organization_members_flag and not hide_members %}
Members
diff --git a/src/registrar/templates/includes/portfolio_request_review_steps.html b/src/registrar/templates/includes/portfolio_request_review_steps.html
index ed727fee8..175a9bdc1 100644
--- a/src/registrar/templates/includes/portfolio_request_review_steps.html
+++ b/src/registrar/templates/includes/portfolio_request_review_steps.html
@@ -14,7 +14,7 @@
{% comment %} We don't have city or state_territory for suborganizations yet, so no data should display {% endcomment %}
{% elif domain_request.requesting_entity_is_suborganization %}
{% include "includes/summary_item.html" with value=domain_request.requested_suborganization edit_link=domain_request_url %}
- {{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
+ {{domain_request.suborganization_city}}, {{domain_request.suborganization_state_territory}}
{% elif domain_request.requesting_entity_is_portfolio %}
{% include "includes/summary_item.html" with value=domain_request.portfolio.organization_name edit_link=domain_request_url %}
{% if domain_request.portfolio.city and domain_request.portfolio.state_territory %}
diff --git a/src/registrar/views/domain_request.py b/src/registrar/views/domain_request.py
index 41e9a07b8..b1c5c9c89 100644
--- a/src/registrar/views/domain_request.py
+++ b/src/registrar/views/domain_request.py
@@ -319,7 +319,15 @@ def get(self, request, *args, **kwargs):
# Clear context so the prop getter won't create a request here.
# Creating a request will be handled in the post method for the
# intro page.
- return render(request, "domain_request_intro.html", {"hide_requests": True, "hide_domains": True})
+ return render(
+ request,
+ "domain_request_intro.html",
+ {
+ "hide_requests": True,
+ "hide_domains": True,
+ "hide_members": True,
+ },
+ )
else:
return self.goto(self.steps.first)
From 83720c359fd6448dc4647f610934bf5a126c7ef7 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 08:54:32 -0600
Subject: [PATCH 40/47] Update admin.py
---
src/registrar/admin.py | 3 ---
1 file changed, 3 deletions(-)
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index fdadde436..84609468f 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -1484,9 +1484,6 @@ class DomainInformationAdmin(ListHeaderAdmin, ImportExportModelAdmin):
"fields": [
"portfolio",
"sub_organization",
- "requested_suborganization",
- "suborganization_city",
- "suborganization_state_territory",
"creator",
"domain_request",
"notes",
From b05a62ecd195bad8e7585c7cb7985bb298e4dad5 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 09:10:28 -0600
Subject: [PATCH 41/47] Rework readonly fields for analysts + hide when org
flag is off
---
src/registrar/admin.py | 25 +++++++++++++------------
src/registrar/models/domain_request.py | 6 ++++--
2 files changed, 17 insertions(+), 14 deletions(-)
diff --git a/src/registrar/admin.py b/src/registrar/admin.py
index 84609468f..fe59c7d1f 100644
--- a/src/registrar/admin.py
+++ b/src/registrar/admin.py
@@ -28,6 +28,7 @@
from registrar.models import Contact, Domain, DomainRequest, DraftDomain, User, Website, SeniorOfficial
from registrar.utility.constants import BranchChoices
from registrar.utility.errors import FSMDomainRequestError, FSMErrorCodes
+from registrar.utility.waffle import flag_is_active_for_user
from registrar.views.utility.mixins import OrderableFieldsMixin
from django.contrib.admin.views.main import ORDER_VAR
from registrar.widgets import NoAutocompleteFilteredSelectMultiple
@@ -1863,6 +1864,9 @@ def status_history(self, obj):
"cisa_representative_first_name",
"cisa_representative_last_name",
"cisa_representative_email",
+ "requested_suborganization",
+ "suborganization_city",
+ "suborganization_state_territory",
]
autocomplete_fields = [
"approved_domain",
@@ -1882,24 +1886,21 @@ def status_history(self, obj):
change_form_template = "django/admin/domain_request_change_form.html"
- # 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_fieldsets(self, request, obj=None):
fieldsets = super().get_fieldsets(request, obj)
- # Create a modified version of fieldsets to exclude certain fields
- if not request.user.has_perm("registrar.full_access_permission"):
+ # Hide certain suborg fields behind the organization feature flag
+ # if it is not enabled
+ if not flag_is_active_for_user(request.user, "organization_feature"):
+ excluded_fields = [
+ "requested_suborganization",
+ "suborganization_city",
+ "suborganization_state_territory",
+ ]
modified_fieldsets = []
for name, data in fieldsets:
fields = data.get("fields", [])
- fields = tuple(field for field in fields if field not in self.superuser_only_fields)
+ fields = tuple(field for field in fields if field not in excluded_fields)
modified_fieldsets.append((name, {**data, "fields": fields}))
return modified_fieldsets
return fieldsets
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index 2a0c65505..ca4b322be 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -1135,7 +1135,8 @@ def requesting_entity_is_portfolio(self) -> bool:
return False
def requesting_entity_is_suborganization(self) -> bool:
- """Used to determine if this domain request is also requesting that it be tied to a suborganization.
+ """Determines if this record is also requesting that it be tied to a suborganization.
+ Used for the RequestingEntity page.
Returns True if portfolio exists and either sub_organization exists,
or if is_requesting_new_suborganization() is true.
Returns False otherwise.
@@ -1145,8 +1146,9 @@ def requesting_entity_is_suborganization(self) -> bool:
return False
def is_requesting_new_suborganization(self) -> bool:
- """Used on the requesting entity form to determine if a user is trying to request
+ """Determines if a user is trying to request
a new suborganization using the domain request form, rather than one that already exists.
+ Used for the RequestingEntity page.
Returns True if a sub_organization does not exist and if requested_suborganization,
suborganization_city, and suborganization_state_territory all exist.
From 7cdfb7a35fcbae398a2786643068ec1a3272ad5f Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 10:15:40 -0600
Subject: [PATCH 42/47] fix test + lint
---
src/registrar/forms/domain_request_wizard.py | 14 ++++++--------
src/registrar/tests/test_admin_request.py | 5 ++++-
2 files changed, 10 insertions(+), 9 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index f74622b51..010890b04 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -43,9 +43,6 @@ class RequestingEntityForm(RegistrarForm):
required=False,
queryset=Suborganization.objects.none(),
empty_label="--Select--",
- error_messages={
- "required": ("Requesting entity is required.")
- },
)
requested_suborganization = forms.CharField(
label="Requested suborganization",
@@ -118,13 +115,13 @@ def clean(self):
if is_requesting_new_suborganization:
# Validate custom suborganization fields
if not cleaned_data.get("requested_suborganization"):
- self.add_error("requested_suborganization", "Enter details for your organization name.")
+ self.add_error("requested_suborganization", "Organization name is required.")
if not cleaned_data.get("suborganization_city"):
- self.add_error("suborganization_city", "Enter details for your city.")
+ self.add_error("suborganization_city", "City is required.")
if not cleaned_data.get("suborganization_state_territory"):
- self.add_error("suborganization_state_territory", "Enter details for your state or territory.")
+ self.add_error("suborganization_state_territory", "State or territory is required.")
elif not suborganization:
- self.add_error("sub_organization", "Select a suborganization.")
+ self.add_error("sub_organization", "Suborganization is required.")
return cleaned_data
@@ -138,6 +135,7 @@ class RequestingEntityYesNoForm(BaseYesNoForm):
# IMPORTANT: This is tied to DomainRequest.is_requesting_new_suborganization().
# This is due to the from_database method on DomainRequestWizard.
field_name = "requesting_entity_is_suborganization"
+ required_error_message = "Requesting entity is required."
def __init__(self, *args, **kwargs):
"""Extend the initialization of the form from RegistrarForm __init__"""
@@ -153,7 +151,7 @@ def __init__(self, *args, **kwargs):
def form_is_checked(self):
"""
Determines the initial checked state of the form.
- Returns True (checked) if the requesting entity is a suborganization,
+ Returns True (checked) if the requesting entity is a suborganization,
and False if it is a portfolio. Returns None if neither condition is met.
"""
# True means that the requesting entity is a suborganization,
diff --git a/src/registrar/tests/test_admin_request.py b/src/registrar/tests/test_admin_request.py
index 4b65c401d..57d7d9ac6 100644
--- a/src/registrar/tests/test_admin_request.py
+++ b/src/registrar/tests/test_admin_request.py
@@ -1689,7 +1689,7 @@ def test_readonly_fields_for_analyst(self):
request.user = self.staffuser
readonly_fields = self.admin.get_readonly_fields(request)
-
+ self.maxDiff = None
expected_fields = [
"other_contacts",
"current_websites",
@@ -1709,6 +1709,9 @@ def test_readonly_fields_for_analyst(self):
"cisa_representative_first_name",
"cisa_representative_last_name",
"cisa_representative_email",
+ "requested_suborganization",
+ "suborganization_city",
+ "suborganization_state_territory",
]
self.assertEqual(readonly_fields, expected_fields)
From c59289d2150498444d01244e78afaecf9670de7d Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 10:18:46 -0600
Subject: [PATCH 43/47] Update domain_request_wizard.py
---
src/registrar/forms/domain_request_wizard.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 010890b04..d9754c87e 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -115,13 +115,13 @@ def clean(self):
if is_requesting_new_suborganization:
# Validate custom suborganization fields
if not cleaned_data.get("requested_suborganization"):
- self.add_error("requested_suborganization", "Organization name is required.")
+ self.add_error("requested_suborganization", "Enter details for your organization name.")
if not cleaned_data.get("suborganization_city"):
- self.add_error("suborganization_city", "City is required.")
+ self.add_error("suborganization_city", "Enter details for your city.")
if not cleaned_data.get("suborganization_state_territory"):
- self.add_error("suborganization_state_territory", "State or territory is required.")
+ self.add_error("suborganization_state_territory", "Enter details for your state or territory.")
elif not suborganization:
- self.add_error("sub_organization", "Suborganization is required.")
+ self.add_error("sub_organization", "Select a suborganization.")
return cleaned_data
From 7141eaed924ae2cd26b7382cd96e7312c0ea1ea9 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 11:02:16 -0600
Subject: [PATCH 44/47] consolidate migration
---
...est_requested_suborganization_and_more.py} | 84 +------------------
...tion_requested_suborganization_and_more.py | 25 ------
2 files changed, 1 insertion(+), 108 deletions(-)
rename src/registrar/migrations/{0136_domaininformation_requested_suborganization_and_more.py => 0136_domainrequest_requested_suborganization_and_more.py} (50%)
delete mode 100644 src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py
diff --git a/src/registrar/migrations/0136_domaininformation_requested_suborganization_and_more.py b/src/registrar/migrations/0136_domainrequest_requested_suborganization_and_more.py
similarity index 50%
rename from src/registrar/migrations/0136_domaininformation_requested_suborganization_and_more.py
rename to src/registrar/migrations/0136_domainrequest_requested_suborganization_and_more.py
index dc6ec9d5d..e1b130b4f 100644
--- a/src/registrar/migrations/0136_domaininformation_requested_suborganization_and_more.py
+++ b/src/registrar/migrations/0136_domainrequest_requested_suborganization_and_more.py
@@ -1,4 +1,4 @@
-# Generated by Django 4.2.10 on 2024-10-30 17:59
+# Generated by Django 4.2.10 on 2024-11-01 17:01
from django.db import migrations, models
@@ -10,88 +10,6 @@ class Migration(migrations.Migration):
]
operations = [
- migrations.AddField(
- model_name="domaininformation",
- name="requested_suborganization",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domaininformation",
- name="suborganization_city",
- field=models.CharField(blank=True, null=True),
- ),
- migrations.AddField(
- model_name="domaininformation",
- name="suborganization_state_territory",
- field=models.CharField(
- blank=True,
- choices=[
- ("AL", "Alabama (AL)"),
- ("AK", "Alaska (AK)"),
- ("AS", "American Samoa (AS)"),
- ("AZ", "Arizona (AZ)"),
- ("AR", "Arkansas (AR)"),
- ("CA", "California (CA)"),
- ("CO", "Colorado (CO)"),
- ("CT", "Connecticut (CT)"),
- ("DE", "Delaware (DE)"),
- ("DC", "District of Columbia (DC)"),
- ("FL", "Florida (FL)"),
- ("GA", "Georgia (GA)"),
- ("GU", "Guam (GU)"),
- ("HI", "Hawaii (HI)"),
- ("ID", "Idaho (ID)"),
- ("IL", "Illinois (IL)"),
- ("IN", "Indiana (IN)"),
- ("IA", "Iowa (IA)"),
- ("KS", "Kansas (KS)"),
- ("KY", "Kentucky (KY)"),
- ("LA", "Louisiana (LA)"),
- ("ME", "Maine (ME)"),
- ("MD", "Maryland (MD)"),
- ("MA", "Massachusetts (MA)"),
- ("MI", "Michigan (MI)"),
- ("MN", "Minnesota (MN)"),
- ("MS", "Mississippi (MS)"),
- ("MO", "Missouri (MO)"),
- ("MT", "Montana (MT)"),
- ("NE", "Nebraska (NE)"),
- ("NV", "Nevada (NV)"),
- ("NH", "New Hampshire (NH)"),
- ("NJ", "New Jersey (NJ)"),
- ("NM", "New Mexico (NM)"),
- ("NY", "New York (NY)"),
- ("NC", "North Carolina (NC)"),
- ("ND", "North Dakota (ND)"),
- ("MP", "Northern Mariana Islands (MP)"),
- ("OH", "Ohio (OH)"),
- ("OK", "Oklahoma (OK)"),
- ("OR", "Oregon (OR)"),
- ("PA", "Pennsylvania (PA)"),
- ("PR", "Puerto Rico (PR)"),
- ("RI", "Rhode Island (RI)"),
- ("SC", "South Carolina (SC)"),
- ("SD", "South Dakota (SD)"),
- ("TN", "Tennessee (TN)"),
- ("TX", "Texas (TX)"),
- ("UM", "United States Minor Outlying Islands (UM)"),
- ("UT", "Utah (UT)"),
- ("VT", "Vermont (VT)"),
- ("VI", "Virgin Islands (VI)"),
- ("VA", "Virginia (VA)"),
- ("WA", "Washington (WA)"),
- ("WV", "West Virginia (WV)"),
- ("WI", "Wisconsin (WI)"),
- ("WY", "Wyoming (WY)"),
- ("AA", "Armed Forces Americas (AA)"),
- ("AE", "Armed Forces Africa, Canada, Europe, Middle East (AE)"),
- ("AP", "Armed Forces Pacific (AP)"),
- ],
- max_length=2,
- null=True,
- verbose_name="state, territory, or military post",
- ),
- ),
migrations.AddField(
model_name="domainrequest",
name="requested_suborganization",
diff --git a/src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py b/src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py
deleted file mode 100644
index dfa716ef4..000000000
--- a/src/registrar/migrations/0137_remove_domaininformation_requested_suborganization_and_more.py
+++ /dev/null
@@ -1,25 +0,0 @@
-# Generated by Django 4.2.10 on 2024-11-01 14:25
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ("registrar", "0136_domaininformation_requested_suborganization_and_more"),
- ]
-
- operations = [
- migrations.RemoveField(
- model_name="domaininformation",
- name="requested_suborganization",
- ),
- migrations.RemoveField(
- model_name="domaininformation",
- name="suborganization_city",
- ),
- migrations.RemoveField(
- model_name="domaininformation",
- name="suborganization_state_territory",
- ),
- ]
From a1d37ee207e3d16b14a5c1d4dde486965cd01590 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 11:02:43 -0600
Subject: [PATCH 45/47] Update
src/registrar/templates/includes/portfolio_request_review_steps.html
Co-authored-by: Rachid Mrad <107004823+rachidatecs@users.noreply.github.com>
---
.../templates/includes/portfolio_request_review_steps.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/templates/includes/portfolio_request_review_steps.html b/src/registrar/templates/includes/portfolio_request_review_steps.html
index 175a9bdc1..d0bdd7ba0 100644
--- a/src/registrar/templates/includes/portfolio_request_review_steps.html
+++ b/src/registrar/templates/includes/portfolio_request_review_steps.html
@@ -18,7 +18,7 @@
{% elif domain_request.requesting_entity_is_portfolio %}
{% include "includes/summary_item.html" with value=domain_request.portfolio.organization_name edit_link=domain_request_url %}
{% if domain_request.portfolio.city and domain_request.portfolio.state_territory %}
- {{domain_request.portfolio.city}}, {{domain_request.portfolio.state_territory}}
+ {{domain_request.portfolio.city}}, {{domain_request.portfolio.state_territory}}
{% endif %}
{% else %}
{% with value="Incomplete "|safe %}
From 706dd4fe2e9dcc0076bb46aa39aaa0d204b5753a Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 11:17:38 -0600
Subject: [PATCH 46/47] error messages! @abroddrick
---
src/registrar/forms/domain_request_wizard.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index d9754c87e..0b560f19c 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -115,11 +115,11 @@ def clean(self):
if is_requesting_new_suborganization:
# Validate custom suborganization fields
if not cleaned_data.get("requested_suborganization"):
- self.add_error("requested_suborganization", "Enter details for your organization name.")
+ self.add_error("requested_suborganization", "Requested suborganization is required")
if not cleaned_data.get("suborganization_city"):
- self.add_error("suborganization_city", "Enter details for your city.")
+ self.add_error("suborganization_city", "City is required")
if not cleaned_data.get("suborganization_state_territory"):
- self.add_error("suborganization_state_territory", "Enter details for your state or territory.")
+ self.add_error("suborganization_state_territory", "State, territory, or military post is required")
elif not suborganization:
self.add_error("sub_organization", "Select a suborganization.")
From e4c15ee28b30817d201130ed0e79384d78ff65e9 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Fri, 1 Nov 2024 11:40:19 -0600
Subject: [PATCH 47/47] Fix unit test
---
src/registrar/forms/domain_request_wizard.py | 8 ++++----
src/registrar/tests/test_views_portfolio.py | 8 ++++----
2 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/src/registrar/forms/domain_request_wizard.py b/src/registrar/forms/domain_request_wizard.py
index 0b560f19c..bfbc22124 100644
--- a/src/registrar/forms/domain_request_wizard.py
+++ b/src/registrar/forms/domain_request_wizard.py
@@ -115,13 +115,13 @@ def clean(self):
if is_requesting_new_suborganization:
# Validate custom suborganization fields
if not cleaned_data.get("requested_suborganization"):
- self.add_error("requested_suborganization", "Requested suborganization is required")
+ self.add_error("requested_suborganization", "Requested suborganization is required.")
if not cleaned_data.get("suborganization_city"):
- self.add_error("suborganization_city", "City is required")
+ self.add_error("suborganization_city", "City is required.")
if not cleaned_data.get("suborganization_state_territory"):
- self.add_error("suborganization_state_territory", "State, territory, or military post is required")
+ self.add_error("suborganization_state_territory", "State, territory, or military post is required.")
elif not suborganization:
- self.add_error("sub_organization", "Select a suborganization.")
+ self.add_error("sub_organization", "Suborganization is required.")
return cleaned_data
diff --git a/src/registrar/tests/test_views_portfolio.py b/src/registrar/tests/test_views_portfolio.py
index 45357cbf7..402d23b70 100644
--- a/src/registrar/tests/test_views_portfolio.py
+++ b/src/registrar/tests/test_views_portfolio.py
@@ -1788,15 +1788,15 @@ def test_requesting_entity_page_errors(self):
response = form.submit()
self.app.set_cookie(settings.SESSION_COOKIE_NAME, session_id)
- self.assertContains(response, "Select a suborganization.", status_code=200)
+ self.assertContains(response, "Suborganization is required.", status_code=200)
# Test missing custom suborganization details
form["portfolio_requesting_entity-is_requesting_new_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)
+ self.assertContains(response, "Requested suborganization is required.", status_code=200)
+ self.assertContains(response, "City is required.", status_code=200)
+ self.assertContains(response, "State, territory, or military post is required.", status_code=200)
@override_flag("organization_feature", active=True)
@override_flag("organization_requests", active=True)