diff --git a/atst/domain/task_orders.py b/atst/domain/task_orders.py index 235351a0..d54cd249 100644 --- a/atst/domain/task_orders.py +++ b/atst/domain/task_orders.py @@ -1,5 +1,4 @@ from sqlalchemy.orm.exc import NoResultFound - from flask import current_app as app from atst.database import db @@ -17,6 +16,7 @@ class TaskOrderError(Exception): class TaskOrders(object): SECTIONS = { "app_info": [ + "portfolio_name", "scope", "defense_component", "app_migration", @@ -93,27 +93,37 @@ class TaskOrders(object): return task_order @classmethod - def is_section_complete(cls, task_order, section): - if section in TaskOrders.sections(): + def section_completion_status(cls, task_order, section): + if section in TaskOrders.mission_owner_sections(): + passed = [] + failed = [] + for attr in TaskOrders.SECTIONS[section]: - if getattr(task_order, attr) is None: - return False + if getattr(task_order, attr): + passed.append(attr) + else: + failed.append(attr) - return True + if not failed: + return "complete" + elif passed and failed: + return "draft" - else: - return False + return "incomplete" @classmethod def all_sections_complete(cls, task_order): for section in TaskOrders.SECTIONS.keys(): - if not TaskOrders.is_section_complete(task_order, section): + if ( + TaskOrders.section_completion_status(task_order, section) + is not "complete" + ): return False return True @classmethod - def sections(cls): + def mission_owner_sections(cls): section_list = TaskOrders.SECTIONS if not app.config.get("CLASSIFIED"): section_list["funding"] = TaskOrders.UNCLASSIFIED_FUNDING diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index 2075e834..9a536db1 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -10,7 +10,7 @@ from wtforms.fields import ( ) from wtforms.fields.html5 import DateField, TelField from wtforms.widgets import ListWidget, CheckboxInput -from wtforms.validators import Length, InputRequired +from wtforms.validators import Length, Required, Optional from flask_wtf.file import FileAllowed from atst.forms.validators import IsNumber, PhoneNumber, RequiredIf @@ -31,6 +31,7 @@ class AppInfoForm(CacheableForm): portfolio_name = StringField( translate("forms.task_order.portfolio_name_label"), description=translate("forms.task_order.portfolio_name_description"), + validators=[Required()], ) scope = TextAreaField( translate("forms.task_order.scope_label"), @@ -44,11 +45,14 @@ class AppInfoForm(CacheableForm): description=translate("forms.task_order.app_migration.description"), choices=APP_MIGRATION, default="", + validators=[Optional()], ) native_apps = RadioField( translate("forms.task_order.native_apps.label"), description=translate("forms.task_order.native_apps.description"), choices=[("yes", "Yes"), ("no", "No"), ("not_sure", "Not Sure")], + default="", + validators=[Optional()], ) complexity = SelectMultipleField( translate("forms.task_order.complexity.label"), @@ -73,6 +77,7 @@ class AppInfoForm(CacheableForm): description=translate("forms.task_order.team_experience.description"), choices=TEAM_EXPERIENCE, default="", + validators=[Optional()], ) @@ -98,28 +103,16 @@ class FundingForm(CacheableForm): render_kw={"accept": ".pdf,.png,application/pdf,image/png"}, ) clin_01 = DecimalField( - translate("forms.task_order.clin_01_label"), - validators=[ - InputRequired(message=(translate("forms.task_order.clin_validation_error"))) - ], + translate("forms.task_order.clin_01_label"), validators=[Optional()] ) clin_02 = DecimalField( - translate("forms.task_order.clin_02_label"), - validators=[ - InputRequired(message=(translate("forms.task_order.clin_validation_error"))) - ], + translate("forms.task_order.clin_02_label"), validators=[Optional()] ) clin_03 = DecimalField( - translate("forms.task_order.clin_03_label"), - validators=[ - InputRequired(message=(translate("forms.task_order.clin_validation_error"))) - ], + translate("forms.task_order.clin_03_label"), validators=[Optional()] ) clin_04 = DecimalField( - translate("forms.task_order.clin_04_label"), - validators=[ - InputRequired(message=(translate("forms.task_order.clin_validation_error"))) - ], + translate("forms.task_order.clin_04_label"), validators=[Optional()] ) @@ -141,7 +134,8 @@ class OversightForm(CacheableForm): ko_last_name = StringField(translate("forms.task_order.oversight_last_name_label")) ko_email = StringField(translate("forms.task_order.oversight_email_label")) ko_phone_number = TelField( - translate("forms.task_order.oversight_phone_label"), validators=[PhoneNumber()] + translate("forms.task_order.oversight_phone_label"), + validators=[Optional(), PhoneNumber()], ) ko_dod_id = StringField( translate("forms.task_order.oversight_dod_id_label"), @@ -162,6 +156,7 @@ class OversightForm(CacheableForm): translate("forms.task_order.oversight_phone_label"), validators=[ RequiredIf(lambda form: not form._fields.get("am_cor").data), + Optional(), PhoneNumber(), ], ) @@ -183,7 +178,8 @@ class OversightForm(CacheableForm): so_last_name = StringField(translate("forms.task_order.oversight_last_name_label")) so_email = StringField(translate("forms.task_order.oversight_email_label")) so_phone_number = TelField( - translate("forms.task_order.oversight_phone_label"), validators=[PhoneNumber()] + translate("forms.task_order.oversight_phone_label"), + validators=[Optional(), PhoneNumber()], ) so_dod_id = StringField( translate("forms.task_order.oversight_dod_id_label"), diff --git a/atst/routes/task_orders/new.py b/atst/routes/task_orders/new.py index 3a860311..c8ff4e14 100644 --- a/atst/routes/task_orders/new.py +++ b/atst/routes/task_orders/new.py @@ -103,8 +103,9 @@ class ShowTaskOrderWorkflow: if self.task_order: for section in screen_info: - if TaskOrders.is_section_complete(self.task_order, section["section"]): - section["complete"] = True + section["completion"] = TaskOrders.section_completion_status( + self.task_order, section["section"] + ) return screen_info diff --git a/styles/components/_progress_menu.scss b/styles/components/_progress_menu.scss index 01e2de26..0da6d34e 100644 --- a/styles/components/_progress_menu.scss +++ b/styles/components/_progress_menu.scss @@ -107,6 +107,13 @@ padding: 1px 2px; } + &--draft:before { + background: $color-gold; + border: none; + font-size: $h6-font-size; + mask-image: url('#{$asset-path}/icons/alert.svg'); + } + &--incomplete:before { border: 2px solid $color-gray-light; } diff --git a/templates/task_orders/_new.html b/templates/task_orders/_new.html index b80bced9..37f1ce74 100644 --- a/templates/task_orders/_new.html +++ b/templates/task_orders/_new.html @@ -40,7 +40,6 @@
-
{% endblock %} diff --git a/templates/task_orders/new/menu.html b/templates/task_orders/new/menu.html index c6620a83..296c44fd 100644 --- a/templates/task_orders/new/menu.html +++ b/templates/task_orders/new/menu.html @@ -1,15 +1,7 @@
- {{ ReviewField(("forms.task_order.team_experience.label" |translate), ("forms.task_order.team_experience.{}".format(task_order.team_experience)) | translate) }} + {{ + ReviewField( + ("forms.task_order.team_experience.label" |translate), + ( + ("forms.task_order.team_experience.{}".format(task_order.team_experience) | translate) if task_order.team_experience + ) + ) + }}
diff --git a/tests/domain/test_task_orders.py b/tests/domain/test_task_orders.py index ab92e024..d62a8634 100644 --- a/tests/domain/test_task_orders.py +++ b/tests/domain/test_task_orders.py @@ -12,17 +12,21 @@ from tests.factories import ( ) -def test_is_section_complete(): +def test_section_completion_status(): dict_keys = [k for k in TaskOrders.SECTIONS.keys()] section = dict_keys[0] attrs = TaskOrders.SECTIONS[section].copy() + attrs.remove("portfolio_name") task_order = TaskOrderFactory.create(**{k: None for k in attrs}) leftover = attrs.pop() + for attr in attrs: setattr(task_order, attr, "str12345") - assert not TaskOrders.is_section_complete(task_order, section) + + assert TaskOrders.section_completion_status(task_order, section) == "draft" + setattr(task_order, leftover, "str12345") - assert TaskOrders.is_section_complete(task_order, section) + assert TaskOrders.section_completion_status(task_order, section) == "complete" def test_all_sections_complete(): diff --git a/tests/routes/task_orders/test_new_task_order.py b/tests/routes/task_orders/test_new_task_order.py index 5a107bb1..bbe461e9 100644 --- a/tests/routes/task_orders/test_new_task_order.py +++ b/tests/routes/task_orders/test_new_task_order.py @@ -170,9 +170,9 @@ def test_show_task_order_display_screen(task_order): screens = workflow.display_screens # every form section is complete for i in range(2): - assert screens[i]["complete"] + assert screens[i]["completion"] == "complete" # the review section is not - assert not screens[3].get("complete") + assert screens[3]["completion"] == "incomplete" def test_update_task_order_with_no_task_order(): diff --git a/translations.yaml b/translations.yaml index a6d68dd3..6b7ad7fa 100644 --- a/translations.yaml +++ b/translations.yaml @@ -222,7 +222,6 @@ forms: clin_02_label: 'CLIN 02: Classified' clin_03_label: 'CLIN 03: Unclassified' clin_04_label: 'CLIN 04: Classified' - clin_validation_error: Please enter a dollar amount unclassified_clin_02_label: 'CLIN 02: Classified (available soon)' unclassified_clin_04_label: 'CLIN 04: Classified (available soon)' oversight_first_name_label: First Name