Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Audit Info - Optional Fields JS Fixes #1877

Merged
merged 18 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,18 @@ class CheckFindingsCountConsistencyTests(TestCase):
FINDINGS_MIN = 1
FINDINGS_MAX = 5

def _award_reference(self):
return f"AWARD-{generate_random_integer(self.AWARD_MIN,self.AWARD_MAX)}"

def _make_federal_awards(self, findings_count) -> dict:
number_of_award = generate_random_integer(2, 4)
return {
"FederalAwards": {
"federal_awards": [
{
"program": {"number_of_audit_findings": findings_count},
"award_reference": self._award_reference(),
}
for _ in range(number_of_award)
"award_reference": f"AWARD-{self.AWARD_MIN}",
},
{
"program": {"number_of_audit_findings": findings_count},
"award_reference": f"AWARD-{self.AWARD_MAX}",
},
]
}
}
Expand Down
3 changes: 0 additions & 3 deletions backend/audit/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,6 @@ def clean_booleans(self):
data[k] = True
elif v == ["False"]:
data[k] = False
# Hack fix until JSON Schema for this is improved:
if data["is_sp_framework_required"] == []:
del data["is_sp_framework_required"]
self.cleaned_data = data
return data

Expand Down
5 changes: 1 addition & 4 deletions backend/audit/templates/audit/audit-info-form.html
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ <h1>Financial statements</h1>
{% endfor %}
<span class="usa-error-message margin-top-2">{{ form.errors.gaap_results|striptags }}</span>
</fieldset>
<div id="sp_framework_section">
<div class="margin-left-10" id="sp_framework_section">
<fieldset class="usa-fieldset margin-top-2">
<legend>
If the financial statements of the auditee were prepared in accordance with GAAP, proceed to question b.
Expand All @@ -52,7 +52,6 @@ <h1>Financial statements</h1>
<input id="sp_framework_basis--{{ pair.tag }}"
name="sp_framework_basis"
class="usa-radio__input"
required
type="radio"
value="{{ pair.tag }}"
{% if pair.tag in form.cleaned_data.sp_framework_basis %}checked{% endif %} />
Expand All @@ -69,7 +68,6 @@ <h1>Financial statements</h1>
<input id="is_sp_framework_required--true"
name="is_sp_framework_required"
class="usa-radio__input"
required
type="radio"
value="True"
{% if form.cleaned_data.is_sp_framework_required is True %}checked{% endif %} />
tadhg-ohiggins marked this conversation as resolved.
Show resolved Hide resolved
Expand All @@ -96,7 +94,6 @@ <h1>Financial statements</h1>
<input id="sp_framework_opinions--{{ pair.tag }}"
name="sp_framework_opinions"
class="usa-checkbox__input"
required
type="checkbox"
value="{{ pair.tag }}"
{% if pair.tag in form.cleaned_data.sp_framework_opinions %}checked{% endif %} />
Expand Down
137 changes: 87 additions & 50 deletions backend/audit/test_schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,25 +364,37 @@ def test_invalid_phone(self):


class AuditInformationSchemaValidityTest(SimpleTestCase):
AUDIT_INFO_SCHEMA = json.loads(
(SECTION_SCHEMA_DIR / "AuditInformation.schema.json").read_text(
encoding="utf-8"
def setUp(self):
self.AUDIT_INFO_SCHEMA = json.loads(
(SECTION_SCHEMA_DIR / "AuditInformation.schema.json").read_text(
encoding="utf-8"
)
)
)

SIMPLE_CASE = json.loads(SIMPLE_CASES_TEST_FILE.read_text(encoding="utf-8"))[
"AuditInformationCase"
]
self.SIMPLE_CASE = json.loads(
SIMPLE_CASES_TEST_FILE.read_text(encoding="utf-8")
)["AuditInformationCases"]
self.BAD_VALUES = [
"state",
"local",
"tribal",
"higher-ed",
"non-profit",
"unknown",
"none",
]
self.ALL_OPINIONS = [
"unmodified_opinion",
"qualified_opinion",
"adverse_opinion",
"disclaimer_of_opinion",
]

def test_schema(self):
"""Try to test Audit Info schema."""
schema = self.AUDIT_INFO_SCHEMA
validate(self.SIMPLE_CASE, schema)
validate(self.SIMPLE_CASE[0], self.AUDIT_INFO_SCHEMA)

def test_all_booleans(self):
schema = self.AUDIT_INFO_SCHEMA
simple_case = jsoncopy(self.SIMPLE_CASE)

simple_case = jsoncopy(self.SIMPLE_CASE[0])
boolean_fields = [
"is_going_concern_included",
"is_internal_control_deficiency_disclosed",
Expand All @@ -391,52 +403,77 @@ def test_all_booleans(self):
"is_aicpa_audit_guide_included",
"is_low_risk_auditee",
]
for value in [True, False]:
for field in boolean_fields:

for field in boolean_fields:
for value in [True, False]:
simple_case[field] = value
validate(simple_case, schema)
validate(simple_case, self.AUDIT_INFO_SCHEMA)
boolean_fields.append("is_sp_framework_required")
simple_case = jsoncopy(self.SIMPLE_CASE[1])

def test_all_gaap_results(self):
schema = self.AUDIT_INFO_SCHEMA
simple_case = jsoncopy(self.SIMPLE_CASE)
gaap_results = [
"unmodified_opinion",
"qualified_opinion",
"adverse_opinion",
"disclaimer_of_opinion",
"not_gaap",
]
for field in boolean_fields:
for value in [True, False]:
simple_case[field] = value
validate(simple_case, self.AUDIT_INFO_SCHEMA)

for result in gaap_results:
simple_case["gaap_results"] = [result]
validate(simple_case, schema)
def test_all_gaap_results(self):
simple_case = jsoncopy(self.SIMPLE_CASE[0])

for _ in range(10):
for n in range(2, 5):
ls = random.sample(gaap_results, n)
for _ in range(5):
for n in range(1, 5):
ls = random.sample(self.ALL_OPINIONS, n)
simple_case["gaap_results"] = ls
validate(simple_case, schema)
validate(simple_case, self.AUDIT_INFO_SCHEMA)

def test_bad_gaap_results(self):
schema = self.AUDIT_INFO_SCHEMA
simple_case = jsoncopy(self.SIMPLE_CASE)
not_gaap_values = [
"state",
"local",
"tribal",
"higher-ed",
"non-profit",
"unknown",
"none",
def test_all_framework_basis(self):
simple_case = jsoncopy(self.SIMPLE_CASE[1])

sp_framework_basis = [
"cash_basis",
"tax_basis",
"contractual_basis",
"other_basis",
]

for word in not_gaap_values:
simple_case["gaap_results"] = [word]
self.assertRaises(exceptions.ValidationError, validate, simple_case, schema)
for _ in range(5):
for n in range(1, 5):
opinions = random.sample(self.ALL_OPINIONS, n)
basis = random.sample(sp_framework_basis, n)
simple_case["sp_framework_opinions"] = opinions
simple_case["sp_framework_basis"] = basis
validate(simple_case, self.AUDIT_INFO_SCHEMA)

def test_bad_gaap_results(self):
simple_case = jsoncopy(self.SIMPLE_CASE[0])

for _ in range(5):
for n in range(1, 5):
ls = random.sample(self.BAD_VALUES, n)
simple_case["gaap_results"] = ls
self.assertRaises(
exceptions.ValidationError,
validate,
simple_case,
self.AUDIT_INFO_SCHEMA,
)

def test_bad_framework_basis(self):
simple_case = jsoncopy(self.SIMPLE_CASE[1])

for _ in range(5):
for n in range(1, 5):
bad_values = random.sample(self.BAD_VALUES, n)
simple_case["sp_framework_opinions"] = bad_values
simple_case["sp_framework_basis"] = bad_values
self.assertRaises(
exceptions.ValidationError,
validate,
simple_case,
self.AUDIT_INFO_SCHEMA,
)

def test_valid_aln_prefixes(self):
schema = self.AUDIT_INFO_SCHEMA
simple_case = jsoncopy(self.SIMPLE_CASE)
simple_case = jsoncopy(self.SIMPLE_CASE[0])
# Why "likely?" I have no idea what is authoritative.
# Fix the tests as we discover changes, and update the
# validation schema while we're at it.
Expand Down Expand Up @@ -508,7 +545,7 @@ def test_valid_aln_prefixes(self):
for n in range(2, 10):
ls = random.sample(likely_valid_aln_prefixes, n)
simple_case["agencies"] = ls
validate(simple_case, schema)
validate(simple_case, self.AUDIT_INFO_SCHEMA)


class FederalAwardsSchemaValidityTest(SimpleTestCase):
Expand Down
32 changes: 32 additions & 0 deletions backend/audit/test_validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
validate_uei_leading_char,
validate_uei_nine_digit_sequences,
validate_component_page_numbers,
validate_audit_information_json,
)

# Simplest way to create a new copy of simple case rather than getting
Expand Down Expand Up @@ -865,3 +866,34 @@ def test_optional_pages(self):
self.fail(
"validate_component_page_numbers rejected an object with optional pages"
)


class AuditInformationTests(SimpleTestCase):
def setUp(self):
"""Set up common test data"""
self.SIMPLE_CASES = json.loads(
SIMPLE_CASES_TEST_FILE.read_text(encoding="utf-8")
)["AuditInformationCases"]

def test_no_errors_when_audit_information_is_valid(self):
"""No errors should be raised when audit information is valid"""
for case in self.SIMPLE_CASES:
validate_audit_information_json(case)

def test_error_raised_for_missing_required_fields_with_not_gaap(self):
"""Test that missing certain fields raises a validation error when 'gaap_results' contains 'not_gaap'."""
for required_field in [
"is_sp_framework_required",
"sp_framework_basis",
"sp_framework_opinions",
]:
case = jsoncopy(self.SIMPLE_CASES[1])
del case[required_field]
self.assertRaises(ValidationError, validate_excel_file_integrity, case)

def test_error_raised_for_missing_required_fields(self):
"""Test that missing required fields raises a validation error."""
for key in self.SIMPLE_CASES[0].keys():
case = jsoncopy(self.SIMPLE_CASES[0])
del case[key]
self.assertRaises(ValidationError, validate_excel_file_integrity, case)
15 changes: 12 additions & 3 deletions backend/audit/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -833,10 +833,19 @@ def post(self, request, *args, **kwargs):

if form.is_valid():
form.clean_booleans()
# List of keys to delete if "not_gaap" not in form.cleaned_data["gaap_results"]
keys_to_delete = [
"sp_framework_basis",
"is_sp_framework_required",
"sp_framework_opinions",
]

if "not_gaap" not in form.cleaned_data["gaap_results"]:
for key in keys_to_delete:
form.cleaned_data.pop(key, None)

validated = validate_audit_information_json(form.cleaned_data)

audit_information = sac.audit_information or {}
audit_information.update(form.cleaned_data)
validated = validate_audit_information_json(audit_information)
sac.audit_information = validated
sac.save()
return redirect(reverse("audit:SubmissionProgress", args=[report_id]))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
[
{
"dollar_threshold": 1000000,
"gaap_results": ["unmodified_opinion"],
"is_going_concern_included": true,
"is_internal_control_deficiency_disclosed": false,
"is_internal_control_material_weakness_disclosed": true,
"is_material_noncompliance_disclosed": true,
"is_aicpa_audit_guide_included": true,
"is_low_risk_auditee": false,
"agencies": ["31", "44"]
},
{
"dollar_threshold": 1000000,
"gaap_results": ["not_gaap"],
"is_going_concern_included": true,
"is_sp_framework_required": true,
"sp_framework_basis": ["cash_basis", "tax_basis", "contractual_basis"],
"sp_framework_opinions": ["unmodified_opinion", "qualified_opinion"],
"is_internal_control_deficiency_disclosed": false,
"is_internal_control_material_weakness_disclosed": true,
"is_material_noncompliance_disclosed": true,
"is_aicpa_audit_guide_included": true,
"is_low_risk_auditee": false,
"agencies": ["32", "45"]
}
]
39 changes: 28 additions & 11 deletions backend/data_fixtures/audit/test_data_entries/simple-cases.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,34 @@
"auditor_phone": "999-999-9999",
"auditor_email": "jane@test.test"
},
"AuditInformationCase": {
"gaap_results": [],
"dollar_threshold": 1,
"is_going_concern_included": true,
"is_internal_control_deficiency_disclosed": false,
"is_internal_control_material_weakness_disclosed": false,
"is_material_noncompliance_disclosed": true,
"is_aicpa_audit_guide_included": false,
"is_low_risk_auditee": true,
"agencies": []
},
"AuditInformationCases":
[
{
"dollar_threshold": 1000000,
"gaap_results": ["unmodified_opinion"],
"is_going_concern_included": true,
"is_internal_control_deficiency_disclosed": false,
"is_internal_control_material_weakness_disclosed": true,
"is_material_noncompliance_disclosed": true,
"is_aicpa_audit_guide_included": true,
"is_low_risk_auditee": false,
"agencies": ["31", "44"]
},
{
"dollar_threshold": 1000000,
"gaap_results": ["not_gaap"],
"is_going_concern_included": true,
"is_sp_framework_required": true,
"sp_framework_basis": ["cash_basis", "tax_basis", "contractual_basis"],
"sp_framework_opinions": ["unmodified_opinion", "qualified_opinion"],
"is_internal_control_deficiency_disclosed": false,
"is_internal_control_material_weakness_disclosed": true,
"is_material_noncompliance_disclosed": true,
"is_aicpa_audit_guide_included": true,
"is_low_risk_auditee": false,
"agencies": ["32", "45"]
}
],
"FederalAwardsCases": [
{
"Meta": {
Expand Down
Binary file modified backend/schemas/output/excel/xlsx/additional-eins-workbook.xlsx
Binary file not shown.
Binary file modified backend/schemas/output/excel/xlsx/additional-ueis-workbook.xlsx
Binary file not shown.
Binary file modified backend/schemas/output/excel/xlsx/audit-findings-text-workbook.xlsx
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified backend/schemas/output/excel/xlsx/federal-awards-workbook.xlsx
Binary file not shown.
Binary file modified backend/schemas/output/excel/xlsx/notes-to-sefa-workbook.xlsx
Binary file not shown.
Binary file modified backend/schemas/output/excel/xlsx/secondary-auditors-workbook.xlsx
Binary file not shown.
Loading