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

Revised Auditee & Auditor Certification Workflow #1680

Merged
merged 40 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
f74d09f
auditor certification new frontend
jperson1 Jul 25, 2023
4f5bcf4
Merge branch 'main' into jp/auditor-certification-frontend
jperson1 Aug 1, 2023
a96a4fc
Auditor/ee cert - two-step forms & DB update
jperson1 Aug 2, 2023
761f3f6
Linting!
jperson1 Aug 2, 2023
53f0573
Remove mixin bypass that I shouldn't have committed
jperson1 Aug 2, 2023
d5de449
Test updates - fake auditor/auditee form data
jperson1 Aug 2, 2023
10512d2
Linting for test changes
jperson1 Aug 2, 2023
8a39d34
Merge branch 'main' into jp/auditor-certification-frontend
jperson1 Aug 7, 2023
97be772
Migration. Auditee/or certification from 30 -> 31
jperson1 Aug 7, 2023
a3e00ca
Linting, yay!
jperson1 Aug 7, 2023
437a36d
Add date fields to HTML, form, and schemas
jperson1 Aug 7, 2023
e8265b4
Linting
jperson1 Aug 7, 2023
362d180
Mocked certification includes datefield
jperson1 Aug 7, 2023
a7e0296
Lint fixture change
jperson1 Aug 7, 2023
12d6dae
Merge branch 'main' into jp/auditor-certification-frontend
jperson1 Aug 7, 2023
d1ba960
Fake datetime object, not a string
jperson1 Aug 7, 2023
515176d
Fixtures - datetime.date, not datetime.datetime...
jperson1 Aug 7, 2023
75acd19
Now the other one... Last try!
jperson1 Aug 7, 2023
f5d3497
Merge branch 'main' into jp/auditor-certification-frontend
tadhg-ohiggins Aug 7, 2023
e2e839d
Merge main into branch and rename migration file.
tadhg-ohiggins Aug 7, 2023
6e4123c
Changes to submission progress page and related logic.
tadhg-ohiggins Aug 8, 2023
05de2cf
Attempt to reify users who are already present in the system when cre…
tadhg-ohiggins Aug 8, 2023
c4276f8
WIP checkin.
tadhg-ohiggins Aug 9, 2023
e9e6e27
WIP check-in.
tadhg-ohiggins Aug 9, 2023
29a3d29
Remove debugging statements and some stray logic.
tadhg-ohiggins Aug 9, 2023
4cb4288
Check that the hidden sections are hidden on submission progress page.
tadhg-ohiggins Aug 9, 2023
0b39539
Make MyPy ignore some lines.
tadhg-ohiggins Aug 9, 2023
caffa26
Merge branch 'main' into jp/auditor-certification-frontend
tadhg-ohiggins Aug 9, 2023
96d840e
Merge branch 'main' into jp/auditor-certification-frontend
timoballard Aug 10, 2023
5795e78
rm redundant access reification
timoballard Aug 10, 2023
73c2e93
Merge branch 'main' into jp/auditor-certification-frontend
jperson1 Aug 10, 2023
8e555b0
Merge branch 'main' into jp/auditor-certification-frontend
jperson1 Aug 10, 2023
7453f40
Linting change from merge.
jperson1 Aug 10, 2023
aded179
Add tribal_data_consent to the model, that connot be inserted into
jperson1 Aug 10, 2023
e2eaa73
Merge branch 'main' into jp/auditor-certification-frontend
jperson1 Aug 10, 2023
3756ed4
Fixed linting.
jadudm Aug 11, 2023
08363a4
adjust test assertion to match template updates
timoballard Aug 11, 2023
84cde52
Unrequire EIN xlsx, notes to sefa, tribal consent
jperson1 Aug 11, 2023
bbe7055
Linting, remove pprint.
jperson1 Aug 11, 2023
ce5a7bf
Merge branch 'jp/auditor-certification-frontend' of https://github.co…
jperson1 Aug 11, 2023
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
4 changes: 4 additions & 0 deletions backend/api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from django.http import Http404, HttpResponse, JsonResponse
from django.urls import reverse
from django.views import View, generic
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from rest_framework import viewsets
from rest_framework.authentication import BaseAuthentication
Expand All @@ -23,6 +24,8 @@
UEISerializer,
)

UserModel = get_user_model()

AUDITEE_INFO_PREVIOUS_STEP_DATA_WE_NEED = [
"user_provided_organization_type",
"met_spending_threshold",
Expand Down Expand Up @@ -128,6 +131,7 @@ def access_and_submission_check(user, data):
role="certifying_auditor_contact",
email=serializer.data.get("certifying_auditor_contact"),
)

for contact in serializer.data.get("auditee_contacts"):
Access.objects.create(sac=sac, role="editor", email=contact)
for contact in serializer.data.get("auditor_contacts"):
Expand Down
4 changes: 3 additions & 1 deletion backend/audit/cross_validation/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,13 @@
]

"""
from .auditee_ueis_match import auditee_ueis_match
from .additional_ueis import additional_ueis
from .auditee_ueis_match import auditee_ueis_match
from .audit_findings import audit_findings
from .sac_validation_shape import sac_validation_shape # noqa: F401

functions = [
audit_findings,
auditee_ueis_match,
additional_ueis,
]
28 changes: 28 additions & 0 deletions backend/audit/cross_validation/audit_findings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
def audit_findings(sac_dict):
"""
Checks that the number of audit findings in the Federal Awards and Audit Findings
sections are consistent.
"""
all_sections = sac_dict["sf_sac_sections"]
awards_outer = all_sections.get("federal_awards", {})
awards = awards_outer.get("federal_awards", []) if awards_outer else []
finds_outer = all_sections.get("findings_text", {})
findings = finds_outer.get("findings_text_entries", []) if finds_outer else []
# If both empty, that's consistent:
if not awards and not findings:
return []
if awards:
# How to determine if findings is required? Loop through the awards?
num_expected = sum(_.get("number_of_audit_findings", 0) for _ in findings)
# For now, just check for non-zero
if num_expected and not awards:
return [
{
"error": "There are findings listed in Federal Awards "
" but none in Findings Text"
}
]

if False > 1:
return [{"error": "error message"}]
return []
4 changes: 4 additions & 0 deletions backend/audit/cross_validation/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ def err_auditee_ueis_match():
return "Not all auditee UEIs matched."


def err_awards_findings_but_no_findings_text():
return "There are findings indicated in Federal Awards but" "none in Findings Text."


def err_missing_tribal_data_sharing_consent():
return (
"As a tribal organization, you must complete the data "
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"FederalAwards": {
"auditee_uei": "TEST0001TEST",
"total_amount_expended": 12345,
"federal_awards": [
{
"program": {
"federal_agency_prefix": "42",
"three_digit_extension": "RD",
"additional_award_identification": 1234,
"program_name": "FLOWER DREAMING",
"is_major": "N",
"audit_report_type": "",
"number_of_audit_findings": 0,
"amount_expended": 500,
"federal_program_total": 500
},
"loan_or_loan_guarantee": {
"is_guaranteed": "N",
"loan_balance_at_audit_period_end": 0
},
"direct_or_indirect_award": {
"is_direct": "N",
"entities": [
{
"passthrough_name": "Lothlórien",
"passthrough_identifying_number": "54321"
}
]
},
"cluster": {
"cluster_name": "N/A",
"cluster_total": 0
},
"subrecipients": {
"is_passed": "N"
},
"award_reference": "AWARD-0001"
}
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"ein": "123456789",
"audit_type": "single-audit",
"auditee_uei": "TEST0001TEST",
"auditee_zip": "94109",
"auditor_ein": "987654321",
"auditor_zip": "94109",
"auditee_city": "HAL",
"auditee_name": "IBM",
"auditor_city": "Rivendell",
"is_usa_based": true,
"auditee_email": "fictional.fac.tester+test@gsa.gov",
"auditee_phone": "5558675309",
"auditee_state": "CA",
"auditor_email": "fictional.fac.tester+test@gsa.gov",
"auditor_phone": "5558675309",
"auditor_state": "CA",
"auditor_country": "United States",
"auditor_firm_name": "Fellowship",
"audit_period_covered": "annual",
"auditee_contact_name": "Frodo Baggins",
"auditor_contact_name": "Sauron",
"auditee_contact_title": "Ringbearer",
"auditor_contact_title": "Dark Lord",
"multiple_eins_covered": false,
"multiple_ueis_covered": false,
"auditee_address_line_1": "HAL",
"auditor_address_line_1": "Main Hall",
"met_spending_threshold": true,
"auditee_fiscal_period_end": "2023-01-01",
"ein_not_an_ssn_attestation": true,
"auditee_fiscal_period_start": "2022-01-01",
"user_provided_organization_type": "non-profit",
"auditor_ein_not_an_ssn_attestation": true
}
41 changes: 41 additions & 0 deletions backend/audit/fixtures/single_audit_checklist.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,47 @@ def _fake_general_information(auditee_name=None):
return general_information


def fake_auditor_certification():
"""Create fake auditor confirmation form data."""
fake = Faker()
data_step_1 = {
"is_OMB_limited": True,
"is_auditee_responsible": True,
"has_used_auditors_report": True,
"has_no_auditee_procedures": True,
"is_FAC_releasable": True,
}
data_step_2 = {
"auditor_name": fake.name(),
"auditor_title": fake.job(),
"auditor_certification_date_signed": fake.date(),
}

return data_step_1, data_step_2


def fake_auditee_certification():
"""Create fake auditor confirmation form data."""
fake = Faker()
data_step_1 = {
"has_no_PII": True,
"has_no_BII": True,
"meets_2CFR_specifications": True,
"is_2CFR_compliant": True,
"is_complete_and_accurate": True,
"has_engaged_auditor": True,
"is_issued_and_signed": True,
"is_FAC_releasable": True,
}
data_step_2 = {
"auditee_name": fake.name(),
"auditee_title": fake.job(),
"auditee_certification_date_signed": fake.date(),
}

return data_step_1, data_step_2


def _create_sac(user, auditee_name):
"""Create a single example SAC."""
SingleAuditChecklist = apps.get_model("audit.SingleAuditChecklist")
Expand Down
31 changes: 31 additions & 0 deletions backend/audit/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,34 @@ def clean_booleans(self):
dollar_threshold = forms.IntegerField(min_value=1)
is_low_risk_auditee = forms.MultipleChoiceField(choices=choices_YoN)
agencies = forms.MultipleChoiceField(choices=choices_agencies)


class AuditorCertificationStep1Form(forms.Form):
is_OMB_limited = forms.BooleanField()
is_auditee_responsible = forms.BooleanField()
has_used_auditors_report = forms.BooleanField()
has_no_auditee_procedures = forms.BooleanField()
is_FAC_releasable = forms.BooleanField()


class AuditorCertificationStep2Form(forms.Form):
auditor_name = forms.CharField()
auditor_title = forms.CharField()
auditor_certification_date_signed = forms.DateField()


class AuditeeCertificationStep1Form(forms.Form):
has_no_PII = forms.BooleanField()
has_no_BII = forms.BooleanField()
meets_2CFR_specifications = forms.BooleanField()
is_2CFR_compliant = forms.BooleanField()
is_complete_and_accurate = forms.BooleanField()
has_engaged_auditor = forms.BooleanField()
is_issued_and_signed = forms.BooleanField()
is_FAC_releasable = forms.BooleanField()


class AuditeeCertificationStep2Form(forms.Form):
auditee_name = forms.CharField()
auditee_title = forms.CharField()
auditee_certification_date_signed = forms.DateField()
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Generated by Django 4.2.3 on 2023-08-07 18:21

import audit.validators
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("audit", "0031_singleauditreportfile_component_page_numbers"),
]

operations = [
migrations.AddField(
model_name="singleauditchecklist",
name="auditee_certification",
field=models.JSONField(
blank=True,
null=True,
validators=[audit.validators.validate_auditee_certification_json],
),
),
migrations.AddField(
model_name="singleauditchecklist",
name="auditor_certification",
field=models.JSONField(
blank=True,
null=True,
validators=[audit.validators.validate_auditor_certification_json],
),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Generated by Django 4.2.3 on 2023-08-10 19:49

import audit.validators
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("audit", "0032_singleauditchecklist_auditee_certification_and_more"),
]

operations = [
migrations.AddField(
model_name="singleauditchecklist",
name="tribal_data_consent",
field=models.JSONField(
blank=True,
null=True,
validators=[audit.validators.validate_tribal_data_consent_json],
),
),
]
15 changes: 15 additions & 0 deletions backend/audit/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@
validate_secondary_auditors_json,
validate_notes_to_sefa_json,
validate_single_audit_report_file,
validate_auditor_certification_json,
validate_auditee_certification_json,
validate_tribal_data_consent_json,
validate_audit_information_json,
validate_component_page_numbers,
)
Expand Down Expand Up @@ -273,6 +276,18 @@ class STATUS:
blank=True, null=True, validators=[validate_notes_to_sefa_json]
)

auditor_certification = models.JSONField(
blank=True, null=True, validators=[validate_auditor_certification_json]
)

auditee_certification = models.JSONField(
blank=True, null=True, validators=[validate_auditee_certification_json]
)

tribal_data_consent = models.JSONField(
blank=True, null=True, validators=[validate_tribal_data_consent_json]
)

def validate_full(self):
"""
Full validation, intended for use when the user indicates that the
Expand Down
Loading