diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index f1bf67c9..e84f5e07 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -10,9 +10,9 @@ from wtforms.fields import ( ) from wtforms.fields.html5 import DateField, TelField from wtforms.widgets import ListWidget, CheckboxInput -from wtforms.validators import Required, Length +from wtforms.validators import Length -from atst.forms.validators import IsNumber, PhoneNumber, RequiredIfNot +from atst.forms.validators import IsNumber, PhoneNumber, RequiredIf from .forms import CacheableForm from .data import ( @@ -117,7 +117,11 @@ class OversightForm(CacheableForm): ) ko_dod_id = StringField( translate("forms.task_order.oversight_dod_id_label"), - validators=[Required(), Length(min=10), IsNumber()], + validators=[ + RequiredIf(lambda form: form._fields.get("ko_invite").data), + Length(min=10), + IsNumber(), + ], ) am_cor = BooleanField(translate("forms.task_order.oversight_am_cor_label")) @@ -128,11 +132,21 @@ class OversightForm(CacheableForm): cor_email = StringField(translate("forms.task_order.oversight_email_label")) cor_phone_number = TelField( translate("forms.task_order.oversight_phone_label"), - validators=[RequiredIfNot("am_cor"), PhoneNumber()], + validators=[ + RequiredIf(lambda form: not form._fields.get("am_cor").data), + PhoneNumber(), + ], ) cor_dod_id = StringField( translate("forms.task_order.oversight_dod_id_label"), - validators=[RequiredIfNot("am_cor"), Length(min=10), IsNumber()], + validators=[ + RequiredIf( + lambda form: not form._fields.get("am_cor").data + and form._fields.get("cor_invite").data + ), + Length(min=10), + IsNumber(), + ], ) so_first_name = StringField( @@ -145,7 +159,11 @@ class OversightForm(CacheableForm): ) so_dod_id = StringField( translate("forms.task_order.oversight_dod_id_label"), - validators=[Required(), Length(min=10), IsNumber()], + validators=[ + RequiredIf(lambda form: form._fields.get("so_invite").data), + Length(min=10), + IsNumber(), + ], ) ko_invite = BooleanField( diff --git a/atst/forms/validators.py b/atst/forms/validators.py index d8b59d97..b6606062 100644 --- a/atst/forms/validators.py +++ b/atst/forms/validators.py @@ -80,26 +80,22 @@ def ListItemsUnique(message=translate("forms.validators.list_items_unique_messag return _list_items_unique -def RequiredIfNot(other_field_name, message=translate("forms.validators.is_required")): +def RequiredIf(criteria_function, message=translate("forms.validators.is_required")): """ A validator which makes a field required only if another field - has a falsy value + has a truthy value Args: - other_field_name (str): the name of the field we check before - determining if this field is required; if this other field is falsy, - the field will be required + criteria_function (function): calling this function on form results + in a boolean value that we want to check against; + if it's True, we require the field message (str): an optional message to display if the field is required but hasNone value """ - def _required_if_not(form, field): - other_field = form._fields.get(other_field_name) - if other_field is None: - raise Exception('no field named "%s" in form' % self.other_field_name) - - if not bool(other_field.data): + def _required_if(form, field): + if criteria_function(form): if field.data is None: raise ValidationError(message) else: raise StopValidation() - return _required_if_not + return _required_if diff --git a/atst/routes/task_orders/new.py b/atst/routes/task_orders/new.py index b021ec39..2426293e 100644 --- a/atst/routes/task_orders/new.py +++ b/atst/routes/task_orders/new.py @@ -81,6 +81,12 @@ class ShowTaskOrderWorkflow: elif self._section["section"] == "oversight": if self.user.dod_id == self.task_order.cor_dod_id: self._form.am_cor.data = True + if self.task_order.contracting_officer: + self._form.ko_invite.data = True + if self.task_order.contracting_officer_representative: + self._form.cor_invite.data = True + if self.task_order.security_officer: + self._form.so_invite.data = True else: self._form = self._section[form_type]() diff --git a/js/components/forms/cor.js b/js/components/forms/oversight.js similarity index 67% rename from js/components/forms/cor.js rename to js/components/forms/oversight.js index 11e8e578..b9864ca7 100644 --- a/js/components/forms/cor.js +++ b/js/components/forms/oversight.js @@ -3,7 +3,7 @@ import textinput from '../text_input' import checkboxinput from '../checkbox_input' export default { - name: 'cor', + name: 'oversight', mixins: [FormMixin], @@ -21,11 +21,17 @@ export default { data: function () { const { - am_cor = false + am_cor = false, + ko_invite = false, + cor_invite = false, + so_invite = false, } = this.initialData return { - am_cor + am_cor, + ko_invite, + cor_invite, + so_invite, } } } diff --git a/js/index.js b/js/index.js index 9c79fcd3..455c89d7 100644 --- a/js/index.js +++ b/js/index.js @@ -11,7 +11,7 @@ import textinput from './components/text_input' import checkboxinput from './components/checkbox_input' import DetailsOfUse from './components/forms/details_of_use' import poc from './components/forms/poc' -import cor from './components/forms/cor' +import oversight from './components/forms/oversight' import financial from './components/forms/financial' import toggler from './components/toggler' import NewApplication from './components/forms/new_application' @@ -45,7 +45,7 @@ const app = new Vue({ checkboxinput, DetailsOfUse, poc, - cor, + oversight, financial, NewApplication, selector, diff --git a/templates/task_orders/new/oversight.html b/templates/task_orders/new/oversight.html index 050c7bc3..e5dbb52b 100644 --- a/templates/task_orders/new/oversight.html +++ b/templates/task_orders/new/oversight.html @@ -13,31 +13,37 @@
{{ "task_orders.new.oversight.ko_info_paragraph" | translate }}
-{{ UserInfo(form.ko_first_name, form.ko_last_name, form.ko_email, form.ko_phone_number) }} -{{ CheckboxInput(form.ko_invite) }} -{{ TextInput(form.ko_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}} - -{{ "task_orders.new.oversight.cor_info_paragraph" | translate }}
-{{ "task_orders.new.oversight.cor_info_paragraph" | translate }}
{{ CheckboxInput(form.am_cor) }} {{ UserInfo(form.cor_first_name, form.cor_last_name, form.cor_email, form.cor_phone_number) }} {{ CheckboxInput(form.cor_invite) }} - {{ TextInput(form.cor_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}} + + {{ TextInput(form.cor_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}} + + + +{{ "task_orders.new.oversight.so_info_paragraph" | translate }}
+ {{ UserInfo(form.so_first_name, form.so_last_name, form.so_email, form.so_phone_number) }} + {{ CheckboxInput(form.so_invite) }} + + {{ TextInput(form.so_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}}{{ "task_orders.new.oversight.so_info_paragraph" | translate }}
-{{ UserInfo(form.so_first_name, form.so_last_name, form.so_email, form.so_phone_number) }} -{{ CheckboxInput(form.so_invite) }} -{{ TextInput(form.so_dod_id, placeholder="1234567890", tooltip="Why", tooltip_title='Why', validation='dodId')}} + {% endblock %} diff --git a/tests/conftest.py b/tests/conftest.py index 781fd515..def8bf93 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -103,15 +103,6 @@ def dummy_form(): return DummyForm() -@pytest.fixture -def dummy_form_with_field(): - def set_field(name, value): - data = DummyField(data=value, name=name) - return DummyForm(data=OrderedDict({name: data})) - - return set_field - - @pytest.fixture def dummy_field(): return DummyField() diff --git a/tests/forms/test_validators.py b/tests/forms/test_validators.py index d01ec7a8..fe12cd58 100644 --- a/tests/forms/test_validators.py +++ b/tests/forms/test_validators.py @@ -6,7 +6,7 @@ from atst.forms.validators import ( IsNumber, PhoneNumber, ListItemsUnique, - RequiredIfNot, + RequiredIf, ) @@ -81,30 +81,19 @@ class TestListItemsUnique: validator(dummy_form, dummy_field) -class TestRequiredIfNot: - def test_RequiredIfNot_requires_field_if_arg_is_falsy( - self, dummy_form_with_field, dummy_field - ): - form = dummy_form_with_field("arg", False) - validator = RequiredIfNot("arg") +class TestRequiredIf: + def test_RequiredIf_requires_field_if_arg_is_truthy(self, dummy_form, dummy_field): + validator = RequiredIf(lambda form: True) dummy_field.data = None with pytest.raises(ValidationError): - validator(form, dummy_field) + validator(dummy_form, dummy_field) - def test_RequiredIfNot_does_not_require_field_if_arg_is_truthy( - self, dummy_form_with_field, dummy_field + def test_RequiredIf_does_not_require_field_if_arg_is_falsy( + self, dummy_form, dummy_field ): - form = dummy_form_with_field("arg", True) - validator = RequiredIfNot("arg") + validator = RequiredIf(lambda form: False) dummy_field.data = None with pytest.raises(StopValidation): - validator(form, dummy_field) - - def test_RequiredIfNot_arg_is_None_raises_error(self, dummy_form, dummy_field): - validator = RequiredIfNot("arg") - dummy_field.data = "some data" - - with pytest.raises(Exception): validator(dummy_form, dummy_field)