diff --git a/atst/forms/financial.py b/atst/forms/financial.py deleted file mode 100644 index 0d614997..00000000 --- a/atst/forms/financial.py +++ /dev/null @@ -1,248 +0,0 @@ -import re -import pendulum -from wtforms.fields.html5 import DateField, EmailField -from wtforms.fields import StringField, FileField, FormField -from wtforms.validators import InputRequired, Email, Regexp, Optional -from flask_wtf.file import FileAllowed -from werkzeug.datastructures import FileStorage - -from .fields import NewlineListField, SelectField, NumberStringField -from atst.forms.forms import CacheableForm -from atst.utils.localization import translate -from .data import FUNDING_TYPES -from .validators import DateRange - - -TREASURY_CODE_REGEX = re.compile(r"^0*([1-9]{4}|[1-9]{6})$") - -BA_CODE_REGEX = re.compile(r"[0-9]{2}\w?$") - - -def number_to_int(num): - if num: - return int(num) - - -def coerce_choice(val): - if val is None: - return None - elif isinstance(val, str): - return val - else: - return val.value - - -class TaskOrderForm(CacheableForm): - def do_validate_number(self): - for field in self: - if field.name != "legacy_task_order-number": - field.validators.insert(0, Optional()) - - valid = super().validate() - - for field in self: - if field.name != "legacy_task_order-number": - field.validators.pop(0) - - return valid - - number = StringField( - translate("forms.financial.number_label"), - description=translate("forms.financial.number_description"), - validators=[InputRequired()], - ) - - funding_type = SelectField( - description=translate("forms.financial.funding_type_description"), - choices=FUNDING_TYPES, - validators=[InputRequired()], - coerce=coerce_choice, - render_kw={"required": False}, - ) - - funding_type_other = StringField( - translate("forms.financial.funding_type_other_label") - ) - - expiration_date = DateField( - translate("forms.financial.expiration_date_label"), - description=translate("forms.financial.expiration_date_description"), - validators=[ - InputRequired(), - DateRange( - lower_bound=pendulum.duration(days=0), - upper_bound=pendulum.duration(years=100), - message="Must be a date in the future.", - ), - ], - format="%m/%d/%Y", - ) - - clin_0001 = NumberStringField( - translate("forms.financial.clin_0001_label"), - validators=[InputRequired()], - description=translate("forms.financial.clin_0001_description"), - filters=[number_to_int], - ) - - clin_0003 = NumberStringField( - translate("forms.financial.clin_0003_label"), - validators=[InputRequired()], - description=translate("forms.financial.clin_0003_description"), - filters=[number_to_int], - ) - - clin_1001 = NumberStringField( - translate("forms.financial.clin_1001_label"), - validators=[InputRequired()], - description=translate("forms.financial.clin_1001_description"), - filters=[number_to_int], - ) - - clin_1003 = NumberStringField( - translate("forms.financial.clin_1003_label"), - validators=[InputRequired()], - description=translate("forms.financial.clin_1003_description"), - filters=[number_to_int], - ) - - clin_2001 = NumberStringField( - translate("forms.financial.clin_2001_label"), - validators=[InputRequired()], - description=translate("forms.financial.clin_2001_description"), - filters=[number_to_int], - ) - - clin_2003 = NumberStringField( - translate("forms.financial.clin_2003_label"), - validators=[InputRequired()], - description=translate("forms.financial.clin_2003_description"), - filters=[number_to_int], - ) - - pdf = FileField( - translate("forms.financial.pdf_label"), - validators=[ - FileAllowed(["pdf"], translate("forms.financial.pdf_allowed_description")), - InputRequired(), - ], - render_kw={"required": False}, - ) - - -class RequestFinancialVerificationForm(CacheableForm): - uii_ids = NewlineListField( - translate("forms.financial.uii_ids_label"), - description=translate("forms.financial.uii_ids_description"), - ) - - pe_id = StringField( - translate("forms.financial.pe_id_label"), - description=translate("forms.financial.pe_id_description"), - validators=[InputRequired()], - ) - - treasury_code = StringField( - translate("forms.financial.treasury_code_label"), - description=translate("forms.financial.treasury_code_description"), - validators=[InputRequired(), Regexp(TREASURY_CODE_REGEX)], - ) - - ba_code = StringField( - translate("forms.financial.ba_code_label"), - description=translate("forms.financial.ba_code_description"), - validators=[InputRequired(), Regexp(BA_CODE_REGEX)], - ) - - fname_co = StringField( - translate("forms.financial.fname_co_label"), validators=[InputRequired()] - ) - lname_co = StringField( - translate("forms.financial.lname_co_label"), validators=[InputRequired()] - ) - - email_co = EmailField( - translate("forms.financial.email_co_label"), - validators=[InputRequired(), Email()], - ) - - office_co = StringField( - translate("forms.financial.office_co_label"), validators=[InputRequired()] - ) - - fname_cor = StringField( - translate("forms.financial.fname_cor_label"), validators=[InputRequired()] - ) - - lname_cor = StringField( - translate("forms.financial.lname_cor_label"), validators=[InputRequired()] - ) - - email_cor = EmailField( - translate("forms.financial.email_cor_label"), - validators=[InputRequired(), Email()], - ) - - office_cor = StringField( - translate("forms.financial.office_cor_label"), validators=[InputRequired()] - ) - - def reset(self): - """ - Reset UII info so that it can be de-parsed rendered properly. - This is a stupid workaround, and there's probably a better way. - """ - self.uii_ids.process_data(self.uii_ids.data) - - -class FinancialVerificationForm(CacheableForm): - - legacy_task_order = FormField(TaskOrderForm) - request = FormField(RequestFinancialVerificationForm) - - def validate(self, *args, **kwargs): - if not kwargs.get("is_extended", True): - return self.do_validate_request() - - if self.legacy_task_order.funding_type.data == "OTHER": - self.legacy_task_order.funding_type_other.validators.append(InputRequired()) - - to_pdf_validators = None - if kwargs.get("has_attachment"): - to_pdf_validators = list(self.legacy_task_order.pdf.validators) - self.legacy_task_order.pdf.validators = [] - - valid = super().validate() - - if to_pdf_validators: - self.legacy_task_order.pdf.validators = to_pdf_validators - - return valid - - def do_validate_request(self): - """ - Called do_validate_request to avoid being considered an inline - validator by wtforms. - """ - request_valid = self.request.validate(self) - task_order_valid = self.legacy_task_order.do_validate_number() - return request_valid and task_order_valid - - def reset(self): - self.request.reset() - - @property - def pe_id(self): - return self.request.pe_id - - @property - def has_pdf_upload(self): - return isinstance(self.legacy_task_order.pdf.data, FileStorage) - - @property - def is_missing_task_order_number(self): - return "number" in self.errors.get("legacy_task_order", {}) - - @property - def is_only_missing_task_order_number(self): - return "task_order_number" in self.errors and len(self.errors) == 1 diff --git a/atst/forms/new_request.py b/atst/forms/new_request.py deleted file mode 100644 index e160070c..00000000 --- a/atst/forms/new_request.py +++ /dev/null @@ -1,222 +0,0 @@ -import pendulum -from wtforms.fields.html5 import DateField, EmailField, IntegerField -from wtforms.fields import BooleanField, RadioField, StringField, TextAreaField -from wtforms.validators import Email, Length, Optional, InputRequired, DataRequired - -from .fields import SelectField -from .forms import CacheableForm -from .edit_user import USER_FIELDS, inherit_field -from .data import ( - SERVICE_BRANCHES, - ASSISTANCE_ORG_TYPES, - DATA_TRANSFER_AMOUNTS, - COMPLETION_DATE_RANGES, -) -from .validators import DateRange, IsNumber -from atst.domain.requests import Requests -from atst.utils.localization import translate - - -class DetailsOfUseForm(CacheableForm): - def validate(self, *args, **kwargs): - if self.jedi_migration.data == "no": - self.rationalization_software_systems.validators.append(Optional()) - self.technical_support_team.validators.append(Optional()) - self.organization_providing_assistance.validators.append(Optional()) - self.engineering_assessment.validators.append(Optional()) - self.data_transfers.validators.append(Optional()) - self.expected_completion_date.validators.append(Optional()) - elif self.jedi_migration.data == "yes": - if self.technical_support_team.data == "no": - self.organization_providing_assistance.validators.append(Optional()) - self.cloud_native.validators.append(Optional()) - - try: - annual_spend = int(self.estimated_monthly_spend.data or 0) * 12 - except ValueError: - annual_spend = 0 - - if annual_spend > Requests.ANNUAL_SPEND_THRESHOLD: - self.number_user_sessions.validators.append(InputRequired()) - self.average_daily_traffic.validators.append(InputRequired()) - - return super(DetailsOfUseForm, self).validate(*args, **kwargs) - - # Details of Use: General - dod_component = SelectField( - translate("forms.new_request.dod_component_label"), - description=translate("forms.new_request.dod_component_description"), - choices=SERVICE_BRANCHES, - validators=[InputRequired()], - ) - - jedi_usage = TextAreaField( - translate("forms.new_request.jedi_usage_label"), - description=translate("forms.new_request.jedi_usage_description"), - validators=[InputRequired()], - ) - - # Details of Use: Cloud Readiness - num_software_systems = IntegerField( - translate("forms.new_request.num_software_systems_label"), - description=translate("forms.new_request.num_software_systems_description"), - ) - - jedi_migration = RadioField( - translate("forms.new_request.jedi_migration_label"), - description=translate("forms.new_request.jedi_migration_description"), - choices=[("yes", "Yes"), ("no", "No")], - default="", - ) - - rationalization_software_systems = RadioField( - description=translate( - "forms.new_request.rationalization_software_systems_description" - ), - choices=[("yes", "Yes"), ("no", "No"), ("In Progress", "In Progress")], - default="", - ) - - technical_support_team = RadioField( - description=translate("forms.new_request.technical_support_team_description"), - choices=[("yes", "Yes"), ("no", "No")], - default="", - ) - - organization_providing_assistance = RadioField( # this needs to be updated to use checkboxes instead of radio - description=translate( - "forms.new_request.organization_providing_assistance_description" - ), - choices=ASSISTANCE_ORG_TYPES, - default="", - ) - - engineering_assessment = RadioField( - description=translate("forms.new_request.engineering_assessment_description"), - choices=[("yes", "Yes"), ("no", "No"), ("In Progress", "In Progress")], - default="", - ) - - data_transfers = SelectField( - description=translate("forms.new_request.data_transfers_description"), - choices=DATA_TRANSFER_AMOUNTS, - validators=[DataRequired()], - ) - - expected_completion_date = SelectField( - description=translate("forms.new_request.expected_completion_date_description"), - choices=COMPLETION_DATE_RANGES, - validators=[DataRequired()], - ) - - cloud_native = RadioField( - description=translate("forms.new_request.cloud_native_description"), - choices=[("yes", "Yes"), ("no", "No")], - default="", - ) - - # Details of Use: Financial Usage - estimated_monthly_spend = IntegerField( - translate("forms.new_request.estimated_monthly_spend_label"), - description=translate("forms.new_request.estimated_monthly_spend_description"), - ) - - dollar_value = IntegerField( - translate("forms.new_request.dollar_value_label"), - description=translate("forms.new_request.dollar_value_description"), - ) - - number_user_sessions = IntegerField( - description=translate("forms.new_request.number_user_sessions_description") - ) - - average_daily_traffic = IntegerField( - translate("forms.new_request.average_daily_traffic_label"), - description=translate("forms.new_request.average_daily_traffic_description"), - ) - - average_daily_traffic_gb = IntegerField( - translate("forms.new_request.average_daily_traffic_gb_label"), - description=translate("forms.new_request.average_daily_traffic_gb_description"), - ) - - start_date = DateField( - description=translate("forms.new_request.start_date_label"), - validators=[ - InputRequired(), - DateRange( - lower_bound=pendulum.duration(days=1), - upper_bound=None, - message=translate( - "forms.new_request.start_date_date_range_validation_message" - ), - ), - ], - format="%m/%d/%Y", - ) - - name = StringField( - translate("forms.new_request.name_label"), - description=translate("forms.new_request.name_description"), - validators=[ - InputRequired(), - Length( - min=4, - max=100, - message=translate("forms.new_request.name_length_validation_message"), - ), - ], - ) - - -class InformationAboutYouForm(CacheableForm): - fname_request = inherit_field(USER_FIELDS["first_name"]) - lname_request = inherit_field(USER_FIELDS["last_name"]) - email_request = inherit_field(USER_FIELDS["email"]) - phone_number = inherit_field(USER_FIELDS["phone_number"]) - phone_ext = inherit_field(USER_FIELDS["phone_ext"], required=False) - service_branch = inherit_field(USER_FIELDS["service_branch"]) - citizenship = inherit_field(USER_FIELDS["citizenship"]) - designation = inherit_field(USER_FIELDS["designation"]) - date_latest_training = inherit_field(USER_FIELDS["date_latest_training"]) - - -class PortfolioOwnerForm(CacheableForm): - def validate(self, *args, **kwargs): - if self.am_poc.data: - # Prepend Optional validators so that the validation chain - # halts if no data exists. - self.fname_poc.validators.insert(0, Optional()) - self.lname_poc.validators.insert(0, Optional()) - self.email_poc.validators.insert(0, Optional()) - self.dodid_poc.validators.insert(0, Optional()) - - return super().validate(*args, **kwargs) - - am_poc = BooleanField( - translate("forms.new_request.am_poc_label"), - default=False, - false_values=(False, "false", "False", "no", ""), - ) - - fname_poc = StringField( - translate("forms.new_request.fname_poc_label"), validators=[InputRequired()] - ) - - lname_poc = StringField( - translate("forms.new_request.lname_poc_label"), validators=[InputRequired()] - ) - - email_poc = EmailField( - translate("forms.new_request.email_poc_label"), - validators=[InputRequired(), Email()], - ) - - dodid_poc = StringField( - translate("forms.new_request.dodid_poc_label"), - validators=[InputRequired(), Length(min=10), IsNumber()], - ) - - -class ReviewAndSubmitForm(CacheableForm): - reviewed = BooleanField(translate("forms.new_request.reviewed_label")) diff --git a/tests/forms/test_financial.py b/tests/forms/test_financial.py deleted file mode 100644 index 39a02a87..00000000 --- a/tests/forms/test_financial.py +++ /dev/null @@ -1,92 +0,0 @@ -import pytest -from werkzeug.datastructures import ImmutableMultiDict - -from atst.forms.financial import FinancialVerificationForm -from atst.domain.requests.financial_verification import PENumberValidator - - -@pytest.mark.parametrize( - "input_,expected", - [ - ("0603502N", None), - ("0603502NZ", None), - ("603502N", "0603502N"), - ("063502N", "0603502N"), - ("63502N", "0603502N"), - ], -) -def test_suggest_pe_id(input_, expected): - assert PENumberValidator().suggest_pe_id(input_) == expected - - -def test_funding_type_other_not_required_if_funding_type_is_not_other(): - form_data = ImmutableMultiDict({"legacy_task_order-funding_type": "PROC"}) - form = FinancialVerificationForm(form_data) - form.validate() - assert "funding_type_other" not in form.errors - - -def test_funding_type_other_required_if_funding_type_is_other(): - form_data = ImmutableMultiDict({"legacy_task_order-funding_type": "OTHER"}) - form = FinancialVerificationForm(form_data) - form.validate() - assert "funding_type_other" in form.errors["legacy_task_order"] - - -@pytest.mark.parametrize( - "input_,expected", - [ - ("1234", True), - ("123456", True), - ("0001234", True), - ("000123456", True), - ("12345", False), - ("00012345", False), - ("0001234567", False), - ("000000", False), - ], -) -def test_treasury_code_validation(input_, expected): - form_data = ImmutableMultiDict([("request-treasury_code", input_)]) - form = FinancialVerificationForm(form_data) - form.validate() - is_valid = "treasury_code" not in form.errors["request"] - - assert is_valid == expected - - -@pytest.mark.parametrize( - "input_,expected", - [ - ("1", False), - ("12", True), - ("01", True), - ("0A", False), - ("A", False), - ("AB", False), - ("123", True), - ("012", True), - ("12A", True), - ("02A", True), - ("0012", False), - ("012A", False), - ("2AB", False), - ], -) -def test_ba_code_validation(input_, expected): - form_data = ImmutableMultiDict([("request-ba_code", input_)]) - form = FinancialVerificationForm(form_data) - form.validate() - is_valid = "ba_code" not in form.errors["request"] - - assert is_valid == expected - - -def test_can_submit_zero_for_clin(): - form_first = FinancialVerificationForm() - form_first.validate() - assert "clin_0001" in form_first.errors["legacy_task_order"] - form_data = ImmutableMultiDict([("legacy_task_order-clin_0001", "0")]) - form_second = FinancialVerificationForm(form_data) - form_second.validate() - assert "clin_0001" not in form_second.errors["legacy_task_order"] diff --git a/tests/forms/test_new_request.py b/tests/forms/test_new_request.py deleted file mode 100644 index 91cbf0dd..00000000 --- a/tests/forms/test_new_request.py +++ /dev/null @@ -1,103 +0,0 @@ -import pytest -from werkzeug.datastructures import ImmutableMultiDict - -from atst.forms.new_request import DetailsOfUseForm - - -class TestDetailsOfUseForm: - - form_data = { - "dod_component": "Army and Air Force Exchange Service", - "jedi_usage": "cloud-ify all the things", - "num_software_systems": "12", - "estimated_monthly_spend": "1000000", - "dollar_value": "42", - "number_user_sessions": "6", - "average_daily_traffic": "0", - "start_date": "12/12/2050", - "name": "blue-beluga", - } - migration_data = { - "jedi_migration": "yes", - "rationalization_software_systems": "yes", - "technical_support_team": "yes", - "organization_providing_assistance": "In-house staff", - "engineering_assessment": "yes", - "data_transfers": "Less than 100GB", - "expected_completion_date": "Less than 1 month", - } - - def _make_form(self, data): - form_data = ImmutableMultiDict(data.items()) - return DetailsOfUseForm(form_data) - - def test_require_cloud_native_when_not_migrating(self): - extra_data = {"jedi_migration": "no"} - request_form = self._make_form({**self.form_data, **extra_data}) - assert not request_form.validate() - assert request_form.errors == {"cloud_native": ["Not a valid choice"]} - - def test_require_migration_questions_when_migrating(self): - extra_data = { - "jedi_migration": "yes", - "data_transfers": "", - "expected_completion_date": "", - } - request_form = self._make_form({**self.form_data, **extra_data}) - assert not request_form.validate() - assert request_form.errors == { - "rationalization_software_systems": ["Not a valid choice"], - "technical_support_team": ["Not a valid choice"], - "organization_providing_assistance": ["Not a valid choice"], - "engineering_assessment": ["Not a valid choice"], - "data_transfers": ["This field is required."], - "expected_completion_date": ["This field is required."], - } - - def test_require_organization_when_technical_support_team(self): - data = {**self.form_data, **self.migration_data} - del data["organization_providing_assistance"] - - request_form = self._make_form(data) - assert not request_form.validate() - assert request_form.errors == { - "organization_providing_assistance": ["Not a valid choice"] - } - - def test_valid_form_data(self): - data = {**self.form_data, **self.migration_data} - data["technical_support_team"] = "no" - del data["organization_providing_assistance"] - - request_form = self._make_form(data) - assert request_form.validate() - - def test_sessions_required_for_large_applications(self): - data = {**self.form_data, **self.migration_data} - data["estimated_monthly_spend"] = "9999999" - del data["number_user_sessions"] - del data["average_daily_traffic"] - - request_form = self._make_form(data) - assert not request_form.validate() - assert request_form.errors == { - "number_user_sessions": ["This field is required."], - "average_daily_traffic": ["This field is required."], - } - - def test_sessions_not_required_low_monthly_spend(self): - data = {**self.form_data, **self.migration_data} - data["estimated_monthly_spend"] = "10" - del data["number_user_sessions"] - del data["average_daily_traffic"] - - request_form = self._make_form(data) - assert request_form.validate() - - def test_start_date_must_be_in_the_future(self): - data = {**self.form_data, **self.migration_data} - data["start_date"] = "01/01/2018" - - request_form = self._make_form(data) - assert not request_form.validate() - assert "Must be a date in the future." in request_form.errors["start_date"]