diff --git a/atst/domain/task_orders.py b/atst/domain/task_orders.py index 65e9b8dd..235351a0 100644 --- a/atst/domain/task_orders.py +++ b/atst/domain/task_orders.py @@ -1,5 +1,7 @@ from sqlalchemy.orm.exc import NoResultFound +from flask import current_app as app + from atst.database import db from atst.models.task_order import TaskOrder from atst.models.permissions import Permissions @@ -50,6 +52,8 @@ class TaskOrders(object): ], } + UNCLASSIFIED_FUNDING = ["performance_length", "csp_estimate", "clin_01", "clin_03"] + @classmethod def get(cls, user, task_order_id): try: @@ -90,7 +94,7 @@ class TaskOrders(object): @classmethod def is_section_complete(cls, task_order, section): - if section in TaskOrders.SECTIONS: + if section in TaskOrders.sections(): for attr in TaskOrders.SECTIONS[section]: if getattr(task_order, attr) is None: return False @@ -108,6 +112,13 @@ class TaskOrders(object): return True + @classmethod + def sections(cls): + section_list = TaskOrders.SECTIONS + if not app.config.get("CLASSIFIED"): + section_list["funding"] = TaskOrders.UNCLASSIFIED_FUNDING + return section_list + OFFICERS = [ "contracting_officer", "contracting_officer_representative", diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index 5196e9d0..2075e834 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -1,6 +1,6 @@ from wtforms.fields import ( BooleanField, - IntegerField, + DecimalField, RadioField, SelectField, SelectMultipleField, @@ -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 +from wtforms.validators import Length, InputRequired from flask_wtf.file import FileAllowed from atst.forms.validators import IsNumber, PhoneNumber, RequiredIf @@ -97,20 +97,41 @@ class FundingForm(CacheableForm): ], render_kw={"accept": ".pdf,.png,application/pdf,image/png"}, ) - clin_01 = IntegerField(translate("forms.task_order.clin_01_label")) - clin_02 = IntegerField(translate("forms.task_order.clin_02_label")) - clin_03 = IntegerField(translate("forms.task_order.clin_03_label")) - clin_04 = IntegerField(translate("forms.task_order.clin_04_label")) + clin_01 = DecimalField( + translate("forms.task_order.clin_01_label"), + validators=[ + InputRequired(message=(translate("forms.task_order.clin_validation_error"))) + ], + ) + clin_02 = DecimalField( + translate("forms.task_order.clin_02_label"), + validators=[ + InputRequired(message=(translate("forms.task_order.clin_validation_error"))) + ], + ) + clin_03 = DecimalField( + translate("forms.task_order.clin_03_label"), + validators=[ + InputRequired(message=(translate("forms.task_order.clin_validation_error"))) + ], + ) + clin_04 = DecimalField( + translate("forms.task_order.clin_04_label"), + validators=[ + InputRequired(message=(translate("forms.task_order.clin_validation_error"))) + ], + ) class UnclassifiedFundingForm(FundingForm): - clin_02 = IntegerField(translate("forms.task_order.unclassified_clin_02_label")) - clin_04 = IntegerField(translate("forms.task_order.unclassified_clin_04_label")) - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.clin_02.data = "0" - self.clin_04.data = "0" + clin_02 = StringField( + translate("forms.task_order.unclassified_clin_02_label"), + filters=[lambda x: x or None], + ) + clin_04 = StringField( + translate("forms.task_order.unclassified_clin_04_label"), + filters=[lambda x: x or None], + ) class OversightForm(CacheableForm): diff --git a/atst/routes/task_orders/new.py b/atst/routes/task_orders/new.py index 77fa64e3..3a860311 100644 --- a/atst/routes/task_orders/new.py +++ b/atst/routes/task_orders/new.py @@ -128,7 +128,12 @@ class UpdateTaskOrderWorkflow(ShowTaskOrderWorkflow): self.portfolio_id = portfolio_id self._task_order = None self._section = TASK_ORDER_SECTIONS[screen - 1] - self._form = self._section["form"](self.form_data) + form_type = ( + "unclassified_form" + if "unclassified_form" in self._section and not app.config.get("CLASSIFIED") + else "form" + ) + self._form = self._section[form_type](self.form_data) @property def form(self): @@ -277,7 +282,6 @@ def update(screen, task_order_id=None, portfolio_id=None): workflow = UpdateTaskOrderWorkflow( g.current_user, form_data, screen, task_order_id, portfolio_id ) - if workflow.validate(): workflow.update() return redirect( diff --git a/js/components/forms/funding.js b/js/components/forms/funding.js index dc1f6f46..20b25a2c 100644 --- a/js/components/forms/funding.js +++ b/js/components/forms/funding.js @@ -48,22 +48,21 @@ export default { totalBudget: function() { return [this.clin_01, this.clin_02, this.clin_03, this.clin_04].reduce( function(acc, curr) { - curr = !curr ? 0 : parseInt(curr) + curr = !curr ? 0 : parseFloat(curr) return acc + curr }, 0 ) }, totalBudgetStr: function() { - return this.formatDollars(this.totalBudget) + return this.totalBudget.toLocaleString('us-US', { + style: 'currency', + currency: 'USD', + }) }, }, methods: { - formatDollars: function(intValue) { - const mask = createNumberMask({ prefix: '$', allowDecimal: true }) - return conformToMask(intValue.toString(), mask).conformedValue - }, showUploadInput: function() { this.showUpload = true }, diff --git a/tests/routes/task_orders/test_new_task_order.py b/tests/routes/task_orders/test_new_task_order.py index 16a14c63..5a107bb1 100644 --- a/tests/routes/task_orders/test_new_task_order.py +++ b/tests/routes/task_orders/test_new_task_order.py @@ -119,7 +119,7 @@ def test_task_order_form_shows_errors(client, user_session): body = response.data.decode() assert "There were some errors" in body - assert "Not a valid integer" in body + assert "Not a valid decimal" in body @pytest.fixture diff --git a/translations.yaml b/translations.yaml index 471911ed..d49085d7 100644 --- a/translations.yaml +++ b/translations.yaml @@ -222,6 +222,7 @@ 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