{% if certification.auditor_certified %}
{% include './icon-list-icon.html' with completed=certification.auditor_certified %}
diff --git a/backend/audit/test_schemas.py b/backend/audit/test_schemas.py
index b12c3bb328..a5d3ca162d 100644
--- a/backend/audit/test_schemas.py
+++ b/backend/audit/test_schemas.py
@@ -97,17 +97,6 @@ def test_invalid_auditee_fiscal_period_end(self):
schema,
)
- def test_null_auditee_name(self):
- """
- If the auditee_name is null, validation should pass
- """
- schema = self.GENERAL_INFO_SCHEMA
- instance = jsoncopy(self.SIMPLE_CASE)
-
- instance["auditee_name"] = ""
-
- validate(instance, schema)
-
def test_invalid_ein(self):
"""
If the EIN is not in a valid format, validation should fail
diff --git a/backend/audit/test_validators.py b/backend/audit/test_validators.py
index 2b0cb6ae8d..eb3f789253 100644
--- a/backend/audit/test_validators.py
+++ b/backend/audit/test_validators.py
@@ -1108,6 +1108,65 @@ def setUp(self):
"audit_period_covered": "Invalid",
}
+ character_limits = settings.CHARACTER_LIMITS_GENERAL
+
+ self.too_short_emails = self.valid_general_information | {
+ "auditee_email": "a@b",
+ "auditor_email": "a@b",
+ }
+
+ self.too_long_emails = self.valid_general_information | {
+ "auditee_email": "a" * character_limits["auditee_email"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditor_email": "a" * character_limits["auditor_email"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ }
+
+ self.too_short_addresses = self.valid_general_information | {
+ "auditee_address_line_1": "a",
+ "auditee_city": "a",
+ }
+
+ self.too_long_addresses = self.valid_general_information | {
+ "auditee_address_line_1": "a"
+ * character_limits["auditee_address_line_1"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditee_city": "a" * character_limits["auditee_city"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ }
+
+ self.too_short_names = self.valid_general_information | {
+ "auditee_name": "a",
+ "auditee_certify_name": "a",
+ "auditee_certify_title": "a",
+ "auditee_contact_name": "a",
+ "auditor_firm_name": "a",
+ "auditor_contact_title": "a",
+ "auditor_contact_name": "a",
+ }
+
+ self.too_long_names = self.valid_general_information | {
+ "auditee_name": "a" * character_limits["auditee_name"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditee_certify_name": "a"
+ * character_limits["auditee_certify_name"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditee_certify_title": "a"
+ * character_limits["auditee_certify_title"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditee_contact_name": "a"
+ * character_limits["auditee_contact_name"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditor_firm_name": "a" * character_limits["auditor_firm_name"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditor_contact_title": "a"
+ * character_limits["auditor_contact_title"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ "auditor_contact_name": "a"
+ * character_limits["auditor_contact_name"]["max"]
+ + "NowItIsDefinitelyTooLong",
+ }
+
def test_validate_general_information_schema_with_valid_data(self):
"""
Test the validation method with valid general information data.
@@ -1139,6 +1198,18 @@ def test_validate_general_information_schema_with_invalid_data(self):
validate_general_information_schema(self.invalid_email)
with self.assertRaises(ValidationError):
validate_general_information_schema(self.invalid_audit_period_covered)
+ with self.assertRaises(ValidationError):
+ validate_general_information_schema(self.too_short_emails)
+ with self.assertRaises(ValidationError):
+ validate_general_information_schema(self.too_long_emails)
+ with self.assertRaises(ValidationError):
+ validate_general_information_schema(self.too_short_addresses)
+ with self.assertRaises(ValidationError):
+ validate_general_information_schema(self.too_long_addresses)
+ with self.assertRaises(ValidationError):
+ validate_general_information_schema(self.too_short_names)
+ with self.assertRaises(ValidationError):
+ validate_general_information_schema(self.too_long_names)
def test_validate_general_information_schema_rules_with_valid_data(self):
"""
diff --git a/backend/config/settings.py b/backend/config/settings.py
index ed53df7ec4..2437ceeadd 100644
--- a/backend/config/settings.py
+++ b/backend/config/settings.py
@@ -510,6 +510,9 @@
STATE_ABBREVS = json.load(open(f"{SCHEMA_BASE_DIR}/States.json"))[
"UnitedStatesStateAbbr"
]
+CHARACTER_LIMITS_GENERAL = json.load(
+ open(f"{SCHEMA_BASE_DIR}/character_limits/general.json")
+)
ENABLE_DEBUG_TOOLBAR = (
env.bool("ENABLE_DEBUG_TOOLBAR", False) and ENVIRONMENT == "LOCAL" and not TEST_RUN
diff --git a/backend/report_submission/forms.py b/backend/report_submission/forms.py
index 0394bb6339..5507f3d46e 100644
--- a/backend/report_submission/forms.py
+++ b/backend/report_submission/forms.py
@@ -1,6 +1,6 @@
from django import forms
from django.core.validators import RegexValidator
-from config.settings import STATE_ABBREVS
+from config.settings import CHARACTER_LIMITS_GENERAL, STATE_ABBREVS
from api.uei import get_uei_info_from_sam_gov
@@ -15,7 +15,7 @@
)
date_validator = RegexValidator(
r"^([0-9]{2}/[0-9]{2}/[0-9]{4})|([0-9]{4}-[0-9]{2}-[0-9]{4})$",
- "Dates should be in the format 00/00/0000",
+ "Dates should be in the format MM/DD/YYYY",
)
ein_validator = RegexValidator(
r"^[0-9]{9}$", "EINs should be nine characters long and be made up of only numbers."
@@ -27,6 +27,10 @@
zip_validator = RegexValidator(
r"^[0-9]{5}(?:[0-9]{4})?$", "Zip codes should be in the format 12345 or 12345-1234."
)
+audit_period_other_months_validator = RegexValidator(
+ r"^0?[1-9]$|^1[0-8]$",
+ "The audit period should be between 1 and 18 months, with an optional leading zero.",
+)
def validate_uei(value):
@@ -59,7 +63,6 @@ def clean(self):
class GeneralInformationForm(forms.Form):
- max_string_length = 100
foreign_address_max_length = 500
choices_state_abbrevs = list((i, i) for i in STATE_ABBREVS)
@@ -71,7 +74,12 @@ class GeneralInformationForm(forms.Form):
required=False, validators=[date_validator]
)
audit_period_covered = forms.CharField(required=False)
- audit_period_other_months = forms.CharField(required=False)
+ audit_period_other_months = forms.CharField(
+ min_length=CHARACTER_LIMITS_GENERAL["number_months"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["number_months"]["max"],
+ required=False,
+ validators=[audit_period_other_months_validator],
+ )
ein = forms.CharField(
required=False,
validators=[ein_validator], # validators are not run against empty fields
@@ -80,24 +88,45 @@ class GeneralInformationForm(forms.Form):
multiple_eins_covered = forms.BooleanField(required=False)
auditee_uei = forms.CharField(required=False)
multiple_ueis_covered = forms.BooleanField(required=False)
- auditee_name = forms.CharField(max_length=max_string_length, required=False)
+ auditee_name = forms.CharField(
+ min_length=CHARACTER_LIMITS_GENERAL["auditee_name"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditee_name"]["max"],
+ required=False,
+ )
auditee_address_line_1 = forms.CharField(
- max_length=max_string_length, required=False
+ min_length=CHARACTER_LIMITS_GENERAL["auditee_address_line_1"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditee_address_line_1"]["max"],
+ required=False,
)
auditee_city = forms.CharField(
- max_length=max_string_length,
+ min_length=CHARACTER_LIMITS_GENERAL["auditee_city"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditee_city"]["max"],
required=False,
validators=[alpha_validator], # validators are not run against empty fields
)
auditee_state = forms.ChoiceField(choices=choices_state_abbrevs, required=False)
auditee_zip = forms.CharField(required=False, validators=[zip_validator])
- auditee_contact_name = forms.CharField(max_length=max_string_length, required=False)
+ auditee_contact_name = forms.CharField(
+ min_length=CHARACTER_LIMITS_GENERAL["auditee_contact_name"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditee_contact_name"]["max"],
+ required=False,
+ )
auditee_contact_title = forms.CharField(
- max_length=max_string_length, required=False
+ min_length=CHARACTER_LIMITS_GENERAL["auditee_contact_title"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditee_contact_title"]["max"],
+ required=False,
)
auditee_phone = forms.CharField(required=False, validators=[phone_validator])
- auditee_email = forms.CharField(max_length=max_string_length, required=False)
- auditor_firm_name = forms.CharField(max_length=max_string_length, required=False)
+ auditee_email = forms.CharField(
+ min_length=CHARACTER_LIMITS_GENERAL["auditee_email"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditee_email"]["max"],
+ required=False,
+ )
+ auditor_firm_name = forms.CharField(
+ min_length=CHARACTER_LIMITS_GENERAL["auditor_firm_name"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditor_firm_name"]["max"],
+ required=False,
+ )
auditor_ein = forms.CharField(
required=False,
validators=[ein_validator], # validators are not run against empty fields
@@ -105,22 +134,37 @@ class GeneralInformationForm(forms.Form):
auditor_ein_not_an_ssn_attestation = forms.BooleanField(required=False)
auditor_country = forms.CharField(required=False)
auditor_international_address = forms.CharField(
- max_length=foreign_address_max_length, required=False
+ min_length=CHARACTER_LIMITS_GENERAL["auditor_foreign_address"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditor_foreign_address"]["max"],
+ required=False,
)
auditor_address_line_1 = forms.CharField(
- max_length=max_string_length, required=False
+ min_length=CHARACTER_LIMITS_GENERAL["auditor_address_line_1"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditor_address_line_1"]["max"],
+ required=False,
)
auditor_city = forms.CharField(
- max_length=max_string_length,
+ min_length=CHARACTER_LIMITS_GENERAL["auditor_city"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditor_city"]["max"],
required=False,
validators=[alpha_validator], # validators are not run against empty fields
)
auditor_state = forms.ChoiceField(choices=choices_state_abbrevs, required=False)
auditor_zip = forms.CharField(required=False, validators=[zip_validator])
- auditor_contact_name = forms.CharField(max_length=max_string_length, required=False)
+ auditor_contact_name = forms.CharField(
+ min_length=CHARACTER_LIMITS_GENERAL["auditor_contact_name"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditor_contact_name"]["max"],
+ required=False,
+ )
auditor_contact_title = forms.CharField(
- max_length=max_string_length, required=False
+ min_length=CHARACTER_LIMITS_GENERAL["auditor_contact_title"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditor_contact_title"]["max"],
+ required=False,
)
auditor_phone = forms.CharField(required=False, validators=[phone_validator])
- auditor_email = forms.CharField(max_length=max_string_length, required=False)
+ auditor_email = forms.CharField(
+ min_length=CHARACTER_LIMITS_GENERAL["auditor_email"]["min"],
+ max_length=CHARACTER_LIMITS_GENERAL["auditor_email"]["max"],
+ required=False,
+ )
secondary_auditors_exist = forms.BooleanField(required=False)
diff --git a/backend/report_submission/templates/report_submission/gen-form.html b/backend/report_submission/templates/report_submission/gen-form.html
index eb9cba5fad..f17abbb2ad 100644
--- a/backend/report_submission/templates/report_submission/gen-form.html
+++ b/backend/report_submission/templates/report_submission/gen-form.html
@@ -1,5 +1,6 @@
{% extends "base.html" %}
{% load static %}
+{% load space_before_striptags %}
{% block metatags %}
{{ post.title }}
@@ -40,6 +41,18 @@
General inform
{% if errors %}
There were errors when attempting to submit the form. Scroll down for more details.
{% endif %}
+ {% if unexpected_errors %}
+ There was an unexpected error when attempting to submit the form. See below for more details, or reach out to our
+
+ Helpdesk
+
+ .
+
+ Error: {{ errors }}
+ {% endif %}
aria-required="false"
hidden
value="{{ auditee_uei | default_if_none:'' }}" />
+ {% comment %} The UEI is a given from the previous steps, no error message necessary {% endcomment %}
diff --git a/backend/report_submission/templatetags/space_before_striptags.py b/backend/report_submission/templatetags/space_before_striptags.py
new file mode 100644
index 0000000000..d43071d3ae
--- /dev/null
+++ b/backend/report_submission/templatetags/space_before_striptags.py
@@ -0,0 +1,22 @@
+"""
+Custom tag to add spaces between list items when using `striptags`.
+Example:
+"