Skip to content

Commit 9b521d2

Browse files
[Issue #7071] Filter out select application forms from XML generation (#7085)
## Summary <!-- Use "Fixes" to automatically close issue upon PR merge. Use "Work for" when UAT is required. --> Fixes #7071 ## Changes proposed <!-- What was added, updated, or removed in this PR. --> Update XML assembler to apply the same rules as we do for application submission for form inclusion. ## Context for reviewers <!-- Technical or background context, more in-depth details of the implementation, and anything else you'd like reviewers to know about that will help them understand the changes in the PR. --> Similar to application submission, confirm only required and forms included in the submission are part of the XML generation process. ## Validation steps <!-- Manual testing instructions, as well as any helpful references (screenshots, GIF demos, code examples or output). --> See new unit tests.
1 parent 57c91dd commit 9b521d2

File tree

2 files changed

+130
-0
lines changed

2 files changed

+130
-0
lines changed

api/src/services/xml_generation/submission_xml_assembler.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from lxml import etree as lxml_etree
77

88
from src.db.models.competition_models import Application, ApplicationForm, ApplicationSubmission
9+
from src.services.applications.application_validation import is_form_required
910
from src.services.xml_generation.header_generator import (
1011
generate_application_footer_xml,
1112
generate_application_header_xml,
@@ -33,13 +34,22 @@ def __init__(
3334
def get_supported_forms(self) -> list[ApplicationForm]:
3435
"""Get list of application forms that are supported for XML generation.
3536
37+
Filters out forms that are:
38+
- Not required AND not included in submission
39+
- Don't have XML transform config
40+
3641
Returns:
3742
List of ApplicationForm objects for supported forms only
3843
"""
3944
supported_forms = []
4045

4146
for app_form in self.application.application_forms:
4247
form_name = app_form.form.short_form_name
48+
49+
# Skip forms that are not required and not included in submission
50+
if not is_form_required(app_form) and not app_form.is_included_in_submission:
51+
continue
52+
4353
if app_form.form.json_to_xml_schema is not None:
4454
supported_forms.append(app_form)
4555
else:

api/tests/src/services/xml_generation/test_submission_xml_assembler.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,123 @@ def test_generate_complete_submission_xml_namespace_handling(
468468
assert "http://apply.grants.gov/system/Header-V1.0" in root.nsmap.values()
469469
assert "http://apply.grants.gov/system/Footer-V1.0" in root.nsmap.values()
470470
assert "http://apply.grants.gov/system/Global-V1.0" in root.nsmap.values()
471+
472+
def test_get_supported_forms_filters_non_required_not_included(
473+
self, sample_application, sample_application_submission, enable_factory_create
474+
):
475+
"""Test that non-required forms with is_included_in_submission=False are filtered out."""
476+
# Create a non-required form with XML support
477+
optional_form = FormFactory.create(
478+
form_name="Optional Form",
479+
short_form_name="OPTIONAL_1_0",
480+
form_version="1.0",
481+
json_to_xml_schema=FORM_XML_TRANSFORM_RULES, # Has XML support
482+
)
483+
484+
# Create competition form marked as NOT required
485+
competition_form = CompetitionFormFactory.create(
486+
competition=sample_application.competition,
487+
form=optional_form,
488+
is_required=False, # NOT required
489+
)
490+
491+
# Create application form with is_included_in_submission=False
492+
ApplicationFormFactory.create(
493+
application=sample_application,
494+
competition_form=competition_form,
495+
application_response={"some_field": "some_value"},
496+
is_included_in_submission=False, # NOT included
497+
)
498+
499+
assembler = SubmissionXMLAssembler(sample_application, sample_application_submission)
500+
supported_forms = assembler.get_supported_forms()
501+
502+
# Should only return SF424_4_0 (required), not the optional form
503+
assert len(supported_forms) == 1
504+
assert supported_forms[0].form.short_form_name == "SF424_4_0"
505+
506+
def test_get_supported_forms_includes_non_required_when_included(
507+
self, sample_application, sample_application_submission, enable_factory_create
508+
):
509+
"""Test that non-required forms with is_included_in_submission=True are included."""
510+
# Create a non-required form with XML support
511+
optional_form = FormFactory.create(
512+
form_name="Optional Form",
513+
short_form_name="OPTIONAL_1_0",
514+
form_version="1.0",
515+
json_to_xml_schema=FORM_XML_TRANSFORM_RULES, # Has XML support
516+
)
517+
518+
# Create competition form marked as NOT required
519+
competition_form = CompetitionFormFactory.create(
520+
competition=sample_application.competition,
521+
form=optional_form,
522+
is_required=False, # NOT required
523+
)
524+
525+
# Create application form with is_included_in_submission=True
526+
ApplicationFormFactory.create(
527+
application=sample_application,
528+
competition_form=competition_form,
529+
application_response={"some_field": "some_value"},
530+
is_included_in_submission=True, # IS included
531+
)
532+
533+
assembler = SubmissionXMLAssembler(sample_application, sample_application_submission)
534+
supported_forms = assembler.get_supported_forms()
535+
536+
# Should return both SF424_4_0 and the optional form
537+
assert len(supported_forms) == 2
538+
form_names = {form.form.short_form_name for form in supported_forms}
539+
assert "SF424_4_0" in form_names
540+
assert "OPTIONAL_1_0" in form_names
541+
542+
def test_get_supported_forms_includes_required_regardless_of_is_included(
543+
self, sample_application, sample_application_submission, enable_factory_create, db_session
544+
):
545+
"""Test that required forms are included regardless of is_included_in_submission value."""
546+
# Update the existing SF424 form to have is_included_in_submission=False
547+
# (should still be included because it's required)
548+
sample_application.application_forms[0].is_included_in_submission = False
549+
db_session.flush()
550+
551+
assembler = SubmissionXMLAssembler(sample_application, sample_application_submission)
552+
supported_forms = assembler.get_supported_forms()
553+
554+
# Should still return SF424_4_0 because it's required
555+
assert len(supported_forms) == 1
556+
assert supported_forms[0].form.short_form_name == "SF424_4_0"
557+
558+
def test_get_supported_forms_filters_non_required_null_is_included(
559+
self, sample_application, sample_application_submission, enable_factory_create
560+
):
561+
"""Test that non-required forms with is_included_in_submission=None are filtered out."""
562+
# Create a non-required form with XML support
563+
optional_form = FormFactory.create(
564+
form_name="Optional Form",
565+
short_form_name="OPTIONAL_1_0",
566+
form_version="1.0",
567+
json_to_xml_schema=FORM_XML_TRANSFORM_RULES, # Has XML support
568+
)
569+
570+
# Create competition form marked as NOT required
571+
competition_form = CompetitionFormFactory.create(
572+
competition=sample_application.competition,
573+
form=optional_form,
574+
is_required=False, # NOT required
575+
)
576+
577+
# Create application form with is_included_in_submission=None
578+
ApplicationFormFactory.create(
579+
application=sample_application,
580+
competition_form=competition_form,
581+
application_response={"some_field": "some_value"},
582+
is_included_in_submission=None, # NULL/None
583+
)
584+
585+
assembler = SubmissionXMLAssembler(sample_application, sample_application_submission)
586+
supported_forms = assembler.get_supported_forms()
587+
588+
# Should only return SF424_4_0 (required), not the optional form
589+
assert len(supported_forms) == 1
590+
assert supported_forms[0].form.short_form_name == "SF424_4_0"

0 commit comments

Comments
 (0)