initial logic

This commit is contained in:
zandercymatics 2024-10-23 11:00:32 -06:00
parent 65f1e628a7
commit 860f8f4e3c
No known key found for this signature in database
GPG key ID: FF4636ABEC9682B7
7 changed files with 302 additions and 94 deletions

View file

@ -22,6 +22,7 @@ var SUCCESS = "success";
*
*/
const hideElement = (element) => {
if (element && !element.classList.contains("display-none"))
element.classList.add('display-none');
};
@ -30,6 +31,7 @@ const hideElement = (element) => {
*
*/
const showElement = (element) => {
if (element && element.classList.contains("display-none"))
element.classList.remove('display-none');
};
@ -2385,26 +2387,44 @@ document.addEventListener('DOMContentLoaded', function() {
// This determines if we are on the requesting entity page or not.
const fieldset = document.getElementById("requesting-entity-fieldset");
if (!fieldset) return;
console.log("past here")
// 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"]`);
var selectedRequestingEntityValue = document.querySelector(`input[name="${formPrefix}-is_suborganization"]:checked`)?.value;
const subOrgSelect = document.querySelector(`#id_${formPrefix}-sub_organization`);
const orgName = document.querySelector(`#id_${formPrefix}-organization_name`);
const city = document.querySelector(`#id_${formPrefix}-city`);
const stateTerritory = document.querySelector(`#id_${formPrefix}-state_territory`);
console.log(isSuborgRadios)
console.log(subOrgSelect)
console.log(orgName)
console.log(city)
console.log(stateTerritory)
console.log(selectedRequestingEntityValue)
// The suborganization section is its own div
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");
// 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 || !orgName || !city || !stateTerritory) return;
console.log("past here x2")
if (!isSuborgRadios || !subOrgSelect || !suborganizationFieldset || !suborganizationDetailsFieldset) 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);
}
};
// 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);
} else {
hideElement(suborganizationDetailsFieldset);
}
};
// Add fake "other" option to sub_organization select
if (subOrgSelect && !Array.from(subOrgSelect.options).some(option => option.value === "other")) {
@ -2414,52 +2434,21 @@ document.addEventListener('DOMContentLoaded', function() {
subOrgSelect.add(fakeOption);
}
// Hide organization_name, city, state_territory by default
hideElement(orgName.parentElement);
hideElement(city.parentElement);
hideElement(stateTerritory.parentElement);
// Function to toggle forms based on is_suborganization selection
function toggleSubOrganizationFields () {
selectedRequestingEntityValue = document.querySelector(`input[name="${formPrefix}-is_suborganization"]:checked`)?.value;
if (selectedRequestingEntityValue === "True") {
showElement(subOrgSelect.parentElement);
toggleOrganizationDetails();
} else {
hideElement(subOrgSelect.parentElement);
hideElement(orgName.parentElement);
hideElement(city.parentElement);
hideElement(stateTerritory.parentElement);
}
};
// Function to toggle organization details based on sub_organization selection
function toggleOrganizationDetails () {
// We should hide the org name fields when we select the special other value
if (subOrgSelect.value === "other") {
showElement(orgName.parentElement);
showElement(city.parentElement);
showElement(stateTerritory.parentElement);
} else {
hideElement(orgName.parentElement);
hideElement(city.parentElement);
hideElement(stateTerritory.parentElement);
}
};
// Initialize visibility
toggleSubOrganizationFields();
// Add event listeners to is_suborganization radio buttons
// Add event listener to is_suborganization radio buttons
isSuborgRadios.forEach(radio => {
radio.addEventListener("change", () => {
toggleSubOrganizationFields();
// 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.
toggleSuborganization(radio);
// Add an event listener to each to show/hide the relevant fields
radio.addEventListener("click", () => {
toggleSuborganization(radio);
});
});
// Add event listener to the suborg dropdown to show/hide the suborg details section
subOrgSelect.addEventListener("change", () => {
if (selectedRequestingEntityValue === "True") {
toggleOrganizationDetails();
}
toggleSuborganizationDetails();
});
})();

View file

@ -31,17 +31,20 @@ class RequestingEntityForm(RegistrarForm):
queryset=Suborganization.objects.none(),
empty_label="--Select--",
)
organization_name = forms.CharField(
# 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,
error_messages={"required": "Enter the name of your organization."},
)
city = forms.CharField(
suborganization_city = forms.CharField(
label="City",
required=False,
error_messages={"required": "Enter the city where your organization is located."},
)
state_territory = forms.ChoiceField(
suborganization_state_territory = forms.ChoiceField(
label="State, territory, or military post",
required=False,
choices=[("", "--Select--")] + DomainRequest.StateTerritoryChoices.choices,
@ -66,26 +69,52 @@ class RequestingEntityForm(RegistrarForm):
def clean_sub_organization(self):
"""Require something to be selected when this is a federal agency."""
is_suborganization = self.cleaned_data.get("is_suborganization", None)
sub_organization = self.cleaned_data.get("sub_organization", None)
if self.cleaned_data.get("is_suborganization", None):
# TODO - logic for if other is selected, display other stuff
if not sub_organization:
# no answer was selected
print(f"sub org is: {sub_organization} vs is_suborg: {is_suborganization}")
if is_suborganization and not sub_organization:
raise forms.ValidationError(
"Select a suborganization.",
code="required",
)
# Maybe we just represent this with none?
elif sub_organization == "other":
org_name = self.cleaned_data.get("organization_name", None)
city = self.cleaned_data.get("city", None)
state = self.cleaned_data.get("state_territory", None)
if not org_name or not city or not state:
return sub_organization
def clean_requested_suborganization(self):
field = self.cleaned_data.get("requested_suborganization")
if self.is_custom_suborg() and not field:
raise forms.ValidationError(
"Enter details for your suborganization.",
"Enter details for your organization name.",
code="required",
)
return sub_organization
return field
def clean_suborganization_city(self):
field = self.cleaned_data.get("suborganization_city")
if self.is_custom_suborg():
if not field:
raise forms.ValidationError(
"Enter details for your city.",
code="required",
)
return field
def clean_suborganization_state_territory(self):
field = self.cleaned_data.get("suborganization_state_territory")
if self.is_custom_suborg():
if not field:
raise forms.ValidationError(
"Enter details for your state or territory.",
code="required",
)
return field
def is_custom_suborg(self):
"""Checks that the select suborg is 'other', which is a custom field indicating
that we should create a new suborganization."""
is_suborganization = self.cleaned_data.get("is_suborganization")
sub_organization = self.cleaned_data.get("sub_organization")
return is_suborganization and sub_organization == "other"
class RequestingEntityYesNoForm(BaseYesNoForm):
"""The yes/no field for the RequestingEntity form."""
@ -107,10 +136,11 @@ class RequestingEntityYesNoForm(BaseYesNoForm):
Determines the initial checked state of the form based on the domain_request's attributes.
"""
if self.domain_request.portfolio and (self.domain_request.sub_organization or self.domain_request.organization_name):
return 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:
# No pre-selection for new domain requests
return None
class OrganizationTypeForm(RegistrarForm):

View file

@ -0,0 +1,95 @@
# Generated by Django 4.2.10 on 2024-10-23 15:01
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",
),
),
]

View file

@ -344,6 +344,24 @@ class DomainRequest(TimeStampedModel):
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",
)
# This is the domain request user who created this domain request.
creator = models.ForeignKey(
"registrar.User",
@ -1104,6 +1122,15 @@ class DomainRequest(TimeStampedModel):
self.creator.restrict_user()
# 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:
return True
else:
return self.is_suborganization()
# ## Form policies ###
#
# These methods control what questions need to be answered by applicants
@ -1174,15 +1201,24 @@ class DomainRequest(TimeStampedModel):
return False
def is_suborganization(self) -> bool:
"""Determines if this record is a suborganization or not"""
if self.portfolio:
if self.sub_organization:
return True
if self.organization_name != self.portfolio.organization_name:
if self.has_information_required_to_make_suborganization():
return True
return False
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
)
def to_dict(self):
"""This is to process to_dict for Domain Information, making it friendly
to "copy" it

View file

@ -1,4 +1,5 @@
from django.db import models
from registrar.models import DomainRequest
from .utility.time_stamped_model import TimeStampedModel

View file

@ -11,13 +11,38 @@
<legend>
<h2>Who will use the domain youre requesting?</h2>
</legend>
{# 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 %}
{% endwith %}
{# forms.0 is a small yes/no form that toggles the visibility of "requesting entity" formset #}
{% endwith %}
<div id="requesting-entity-fieldset__suborganization" class="margin-top-4">
<h2>Add suborganization information</h2>
<p>
This information will be published in <a class="usa-link usa-link--always-blue" href="{% public_site_url 'about/data' %}">.govs public data</a>. If you dont see your suborganization in the list,
select “other” and enter the name or your suborganization.
</p>
{% with attr_required=True %}
{% input_with_errors forms.1.sub_organization %}
{% input_with_errors forms.1.organization_name %}
{% input_with_errors forms.1.city %}
{% input_with_errors forms.1.state_territory %}
{% endwith %}
{% comment %} This will be toggled if a special value, "other", is selected.
Otherwise this field is invisible.
{% endcomment %}
<div id="requesting-entity-fieldset__suborganization__details">
{% with attr_required=True %}
{% input_with_errors forms.1.requested_suborganization %}
{% endwith %}
{% with attr_required=True %}
{% input_with_errors forms.1.suborganization_city %}
{% endwith %}
{% with attr_required=True %}
{% input_with_errors forms.1.suborganization_state_territory %}
{% endwith %}
</div>
</div>
</fieldset>
{% endblock %}

View file

@ -11,6 +11,7 @@ from registrar.forms import domain_request_wizard as forms
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
@ -137,7 +138,7 @@ class DomainRequestWizard(DomainRequestWizardPermissionView, TemplateView):
}
PORTFOLIO_UNLOCKING_STEPS = {
PortfolioDomainRequestStep.REQUESTING_ENTITY: lambda self: self.domain_request.organization_name is not None,
PortfolioDomainRequestStep.REQUESTING_ENTITY: lambda w: w.from_model("unlock_requesting_entity", False),
PortfolioDomainRequestStep.CURRENT_SITES: lambda self: (
self.domain_request.current_websites.exists() or self.domain_request.requested_domain is not None
),
@ -590,6 +591,37 @@ class RequestingEntity(DomainRequestWizard):
template_name = "domain_request_requesting_entity.html"
forms = [forms.RequestingEntityYesNoForm, forms.RequestingEntityForm]
def save(self, forms: list):
requesting_entity_form = forms[1]
cleaned_data = requesting_entity_form.cleaned_data
is_suborganization = cleaned_data.get("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):
# 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
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
super().save(forms)
class PortfolioAdditionalDetails(DomainRequestWizard):
template_name = "portfolio_domain_request_additional_details.html"