From f3eea39536148e539deeb5f829abba9a6fcd3530 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Thu, 5 Sep 2019 15:09:20 -0400 Subject: [PATCH 01/24] Add "total amount" to CLINS - includes migration for change to model --- .../f03333c42bdb_add_total_amount_to_clins.py | 29 +++++++++++++++++++ atst/domain/task_orders.py | 1 + atst/forms/task_order.py | 4 +++ atst/models/clin.py | 2 ++ tests/factories.py | 1 + 5 files changed, 37 insertions(+) create mode 100644 alembic/versions/f03333c42bdb_add_total_amount_to_clins.py diff --git a/alembic/versions/f03333c42bdb_add_total_amount_to_clins.py b/alembic/versions/f03333c42bdb_add_total_amount_to_clins.py new file mode 100644 index 00000000..8201bb61 --- /dev/null +++ b/alembic/versions/f03333c42bdb_add_total_amount_to_clins.py @@ -0,0 +1,29 @@ +"""Add total amount to CLINs + +Revision ID: f03333c42bdb +Revises: 4a3122ffe898 +Create Date: 2019-09-04 15:35:39.446486 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'f03333c42bdb' # pragma: allowlist secret +down_revision = '30ea1cb20807' # pragma: allowlist secret +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('clins', sa.Column('total_amount', sa.Numeric(scale=2), nullable=True)) + op.execute("UPDATE clins SET total_amount = 0") + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('clins', 'total_amount') + # ### end Alembic commands ### diff --git a/atst/domain/task_orders.py b/atst/domain/task_orders.py index 2c27977c..93521843 100644 --- a/atst/domain/task_orders.py +++ b/atst/domain/task_orders.py @@ -58,6 +58,7 @@ class TaskOrders(BaseDomainClass): number=clin_data["number"], start_date=clin_data["start_date"], end_date=clin_data["end_date"], + total_amount=clin_data["total_amount"], obligated_amount=clin_data["obligated_amount"], jedi_clin_type=clin_data["jedi_clin_type"], ) diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index 09a8fe8e..579c0cc0 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -47,6 +47,10 @@ class CLINForm(FlaskForm): format="%m/%d/%Y", validators=[Optional()], ) + total_amount = DecimalField( + label=translate("task_orders.form.total_funds_label"), + validators=[Optional()], + ) obligated_amount = DecimalField( label=translate("task_orders.form.obligated_funds_label"), validators=[Optional()], diff --git a/atst/models/clin.py b/atst/models/clin.py index da2d1d02..6107a1f3 100644 --- a/atst/models/clin.py +++ b/atst/models/clin.py @@ -23,6 +23,7 @@ class CLIN(Base, mixins.TimestampsMixin): number = Column(String, nullable=True) start_date = Column(Date, nullable=True) end_date = Column(Date, nullable=True) + total_amount = Column(Numeric(scale=2), nullable=True) obligated_amount = Column(Numeric(scale=2), nullable=True) jedi_clin_type = Column(SQLAEnum(JEDICLINType, native_enum=False), nullable=True) @@ -46,6 +47,7 @@ class CLIN(Base, mixins.TimestampsMixin): self.number, self.start_date, self.end_date, + self.total_amount, self.obligated_amount, self.jedi_clin_type, ] diff --git a/tests/factories.py b/tests/factories.py index 7dd4a2f6..45c0be86 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -291,6 +291,7 @@ class CLINFactory(Base): number = factory.LazyFunction(random_task_order_number) start_date = datetime.date.today() end_date = factory.LazyFunction(random_future_date) + total_amount = factory.LazyFunction(lambda *args: random.randint(100, 999999)) obligated_amount = factory.LazyFunction(lambda *args: random.randint(100, 999999)) jedi_clin_type = factory.LazyFunction( lambda *args: random.choice(list(clin.JEDICLINType)) From 6f13b8a179bbc4acec9f3deee3c08d0d919bcf20 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Mon, 9 Sep 2019 15:31:48 -0400 Subject: [PATCH 02/24] Add WTForms min and max validation for clin $ - above 0 and below $1,000,000,000.00, inclusive --- atst/forms/task_order.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/atst/forms/task_order.py b/atst/forms/task_order.py index 579c0cc0..e9bd9531 100644 --- a/atst/forms/task_order.py +++ b/atst/forms/task_order.py @@ -7,7 +7,7 @@ from wtforms.fields import ( HiddenField, ) from wtforms.fields.html5 import DateField -from wtforms.validators import Required, Optional, Length +from wtforms.validators import Required, Optional, Length, NumberRange from flask_wtf import FlaskForm from datetime import datetime @@ -49,11 +49,21 @@ class CLINForm(FlaskForm): ) total_amount = DecimalField( label=translate("task_orders.form.total_funds_label"), - validators=[Optional()], + validators=[ + Optional(), + NumberRange( + 0, 1000000000, "dollar amount must be from $0.00 to $1,000,000,000.00" + ), + ], ) obligated_amount = DecimalField( label=translate("task_orders.form.obligated_funds_label"), - validators=[Optional()], + validators=[ + Optional(), + NumberRange( + 0, 1000000000, "dollar amount must be from $0.00 to $1,000,000,000.00" + ), + ], ) def validate(self, *args, **kwargs): @@ -93,6 +103,12 @@ class CLINForm(FlaskForm): ) valid = False + if self.total_amount.data < self.obligated_amount.data: + self.obligated_amount.errors.append( + translate("forms.task_order.obligated_amount") + ) + valid = False + return valid From 8c1a1ac33e9429c33558f863123ae7cd30eccbbc Mon Sep 17 00:00:00 2001 From: graham-dds Date: Mon, 9 Sep 2019 15:33:29 -0400 Subject: [PATCH 03/24] Add default values for obligated and total CLIN $ --- templates/task_orders/step_3.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/task_orders/step_3.html b/templates/task_orders/step_3.html index bff66003..1515e6b0 100644 --- a/templates/task_orders/step_3.html +++ b/templates/task_orders/step_3.html @@ -16,6 +16,8 @@ Date: Mon, 9 Sep 2019 16:11:05 -0400 Subject: [PATCH 04/24] class methods for oblig. and contract amount total --- atst/models/task_order.py | 10 +++------- js/components/text_input.js | 1 - 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/atst/models/task_order.py b/atst/models/task_order.py index bd592bea..3fe3d3e3 100644 --- a/atst/models/task_order.py +++ b/atst/models/task_order.py @@ -6,7 +6,6 @@ from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import relationship from atst.models import Attachment, Base, mixins, types -from atst.models.clin import JEDICLINType from atst.utils.clock import Clock @@ -148,10 +147,7 @@ class TaskOrder(Base, mixins.TimestampsMixin): def total_obligated_funds(self): total = 0 for clin in self.clins: - if clin.obligated_amount is not None and clin.jedi_clin_type in [ - JEDICLINType.JEDI_CLIN_1, - JEDICLINType.JEDI_CLIN_3, - ]: + if clin.obligated_amount is not None: total += clin.obligated_amount return total @@ -159,8 +155,8 @@ class TaskOrder(Base, mixins.TimestampsMixin): def total_contract_amount(self): total = 0 for clin in self.clins: - if clin.obligated_amount is not None: - total += clin.obligated_amount + if clin.total_amount is not None: + total += clin.total_amount return total @property diff --git a/js/components/text_input.js b/js/components/text_input.js index 92c8c7e4..1e728ee7 100644 --- a/js/components/text_input.js +++ b/js/components/text_input.js @@ -159,7 +159,6 @@ export default { _isValid: function(value) { let valid = this._validate(value) - if (!this.modified && this.initialErrors && this.initialErrors.length) { valid = false } else if (this.optional && value === '') { From 47337e3e7093a1e04d8dd1f37043c005170fba5d Mon Sep 17 00:00:00 2001 From: graham-dds Date: Tue, 10 Sep 2019 14:48:03 -0400 Subject: [PATCH 05/24] obligated > total error text in translations --- translations.yaml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/translations.yaml b/translations.yaml index 5b5b9552..a47cb8ec 100644 --- a/translations.yaml +++ b/translations.yaml @@ -205,6 +205,7 @@ forms: start: PoP start date must be on or after {date}. scope_description: 'What do you plan to do on the cloud? Some examples might include migrating an existing application or creating a prototype. You don’t need to include a detailed plan of execution, but should list key requirements. This section will be reviewed by your contracting officer, but won’t be sent to the CCPO.

Not sure how to describe your scope? Read some examples to get some inspiration.

' scope_label: Cloud project scope + obligated_amount_error: Obligated amount must be less than or equal to total amount team_experience: built_1: Built, migrated, or consulted on 1-2 applications built_3: Built, migrated, or consulted on 3-5 applications @@ -395,7 +396,7 @@ task_orders: type: CLIN Type idiq_clin_description: Description (IDIQ CLIN) pop: PoP - amount: CLIN Value + total_amount: CLIN Value obligated: Amount Obligated form: add_clin: Add another CLIN @@ -406,7 +407,7 @@ task_orders: clin_description: "Refer to your task order to locate your Contract Line Item Numbers (CLINs)." clin_details: CLIN Details clin_funding: CLIN Funding - clin_number_label: CLIN Number + clin_number_label: CLIN clin_type_label: Corresponding IDIQ CLIN clin_remove_text: 'Do you want to remove ' clin_remove_confirm: Yes, remove CLIN @@ -415,6 +416,7 @@ task_orders: cloud_funding_text: Data must match with what is in your uploaded document. draft_alert_title: Your information has been saved draft_alert_message: You can return to the Task Order Builder to enter missing information. Once you are finished, you’ll be ready to submit this request. + total_funds_label: Total CLIN Value obligated_funds_label: Obligated Funds pop: Period of Performance pop_end: End Date From 3327405a26c31a06abffff634147dd690c316aa9 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Tue, 10 Sep 2019 14:49:25 -0400 Subject: [PATCH 06/24] Use actual total amount in CLIN summary table --- templates/fragments/task_order_review.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/fragments/task_order_review.html b/templates/fragments/task_order_review.html index bf76ba41..3956da3d 100644 --- a/templates/fragments/task_order_review.html +++ b/templates/fragments/task_order_review.html @@ -43,7 +43,7 @@ {{ "task_orders.review.clins.type" | translate }} {{ "task_orders.review.clins.idiq_clin_description" | translate }} {{ "task_orders.review.clins.pop" | translate }} - {{ "task_orders.review.clins.amount" | translate }} + {{ "task_orders.review.clins.total_amount" | translate }} {{ "task_orders.review.clins.obligated" | translate }} @@ -58,7 +58,7 @@ {{ clin.start_date | formattedDate }} - {{ clin.end_date | formattedDate }} {# TODO: Swap in total CLIN amount #} - $123,456,789.00 + {{ clin.total_amount | dollars }} {{ clin.obligated_amount | dollars }} {% endfor %} From aa613690f59d9dd8e578443a89c0176a1f0cec63 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Tue, 10 Sep 2019 14:50:09 -0400 Subject: [PATCH 07/24] Add sum of all CLIN total values to TO summary --- templates/portfolios/task_orders/index.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/portfolios/task_orders/index.html b/templates/portfolios/task_orders/index.html index 5371c95d..aca7b052 100644 --- a/templates/portfolios/task_orders/index.html +++ b/templates/portfolios/task_orders/index.html @@ -81,6 +81,9 @@

Task Order #{{ task_order.number }}

+
+ Total amount: {{ task_order.total_contract_amount | dollars }} +
Obligated amount: {{ task_order.total_obligated_funds | dollars }}
From 257fe648a8a9d2fc17469274e48a2b561497b9c2 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 11 Sep 2019 09:59:13 -0400 Subject: [PATCH 08/24] Add input validation for CLIN dollars But the validation message includes the valid number range --- js/lib/input_validations.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/js/lib/input_validations.js b/js/lib/input_validations.js index a873b3c0..af74f211 100644 --- a/js/lib/input_validations.js +++ b/js/lib/input_validations.js @@ -34,6 +34,13 @@ export default { unmask: ['$', ','], validationError: 'Please enter a dollar amount', }, + clinDollars: { + mask: createNumberMask({ prefix: '$', allowDecimal: true }), + match: /^-?\d+\.?\d*$/, + unmask: ['$', ','], + validationError: + 'Please enter a dollar amount between $0.00 and $1,000,000,000.00', + }, email: { mask: emailMask, match: /^.+@[^.].*\.[a-zA-Z]{2,10}$/, From bf19add617f0ef7cbd38d1e407ec295d8e60a11b Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 11 Sep 2019 10:19:01 -0400 Subject: [PATCH 09/24] Add funding validation to clinfields vue component --- js/components/clin_fields.js | 70 +++++++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 1 deletion(-) diff --git a/js/components/clin_fields.js b/js/components/clin_fields.js index eb958780..3edd7583 100644 --- a/js/components/clin_fields.js +++ b/js/components/clin_fields.js @@ -6,7 +6,10 @@ import { emitEvent } from '../lib/emitters' import Modal from '../mixins/modal' import optionsinput from './options_input' import textinput from './text_input' +import clindollaramount from './clin_dollar_amount' +const TOTAL_AMOUNT = 'total_amount' +const OBLIGATED_AMOUNT = 'obligated_amount' const START_DATE = 'start_date' const END_DATE = 'end_date' const POP = 'period_of_performance' @@ -19,12 +22,21 @@ export default { DateSelector, optionsinput, textinput, + clindollaramount, }, mixins: [Modal], props: { initialClinIndex: Number, + initialTotal: { + type: Number, + default: 0, + }, + initialObligated: { + type: Number, + default: 0, + }, initialStartDate: { type: String, default: null, @@ -54,6 +66,10 @@ export default { const end = !!this.initialEndDate ? new Date(this.initialEndDate) : undefined + const fundingValidation = + this.initialObligated && this.initialTotal + ? false + : this.initialObligated <= this.initialTotal const popValidation = !this.initialStartDate ? false : start < end const clinNumber = !!this.initialClinNumber ? this.initialClinNumber @@ -63,6 +79,7 @@ export default { return { clinIndex: this.initialClinIndex, + clinNumber: clinNumber, startDate: start, endDate: end, popValid: popValidation, @@ -93,6 +110,9 @@ export default { )}.`, }, ], + totalAmount: this.initialTotal || 0, + obligatedAmount: this.initialObligated || 0, + fundingValid: fundingValidation, } }, @@ -101,14 +121,37 @@ export default { }, created: function() { + emitEvent('clin-change', this, { + id: this._uid, + obligatedAmount: this.initialObligated, + totalAmount: this.initialTotal, + }) emitEvent('field-mount', this, { optional: false, name: 'clins-' + this.clinIndex + '-' + POP, valid: this.checkPopValid(), }) + emitEvent('field-mount', this, { + optional: false, + name: TOTAL_AMOUNT, + valid: this.checkFundingValid(), + }) + emitEvent('field-mount', this, { + optional: false, + name: OBLIGATED_AMOUNT, + valid: this.checkFundingValid(), + }) }, methods: { + clinChangeEvent: function() { + emitEvent('clin-change', this, { + id: this._uid, + obligatedAmount: this.initialObligated, + totalAmount: this.initialTotal, + }) + }, + checkPopValid: function() { return ( this.popDateOrder() && @@ -153,9 +196,30 @@ export default { return true }, + checkFundingValid: function() { + return this.obligatedAmount <= this.totalAmount + }, + + validateFunding: function() { + if (this.totalAmount && this.obligatedAmount) { + this.fundingValid = this.checkFundingValid() + } + + emitEvent('field-change', this, { + name: OBLIGATED_AMOUNT, + valid: this.checkFundingValid(), + }) + }, + handleFieldChange: function(event) { if (this._uid === event.parent_uid) { - if (event.name.includes(START_DATE)) { + if (event.name.includes(TOTAL_AMOUNT)) { + this.totalAmount = parseFloat(event.value) + this.validateFunding() + } else if (event.name.includes(OBLIGATED_AMOUNT)) { + this.obligatedAmount = parseFloat(event.value) + this.validateFunding() + } else if (event.name.includes(START_DATE)) { if (!!event.value) this.startDate = new Date(event.value) if (!!event.valid) this.startDateValid = event.valid this.validatePop() @@ -186,6 +250,10 @@ export default { return `CLIN` } }, + percentObligated: function() { + const percentage = (this.obligatedAmount / this.totalAmount) * 100 + return !!percentage ? `${percentage.toFixed(1)}%` : '0%' + }, removeModalId: function() { return `remove-clin-${this.clinIndex}` From cdbbc9f785ea4a5e0a0f1c581a98be22065f9002 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 11 Sep 2019 15:43:53 -0400 Subject: [PATCH 10/24] Add funding validation to clinfields vue component --- js/components/clin_fields.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/js/components/clin_fields.js b/js/components/clin_fields.js index 3edd7583..5d7469fb 100644 --- a/js/components/clin_fields.js +++ b/js/components/clin_fields.js @@ -252,7 +252,17 @@ export default { }, percentObligated: function() { const percentage = (this.obligatedAmount / this.totalAmount) * 100 - return !!percentage ? `${percentage.toFixed(1)}%` : '0%' + if (!!percentage) { + if (percentage > 0 && percentage < 1) { + return '<1%' + } else if (percentage > 99 && percentage < 100) { + return '>99%' + } else { + return `${percentage.toFixed(0)}%` + } + } else { + return '0%' + } }, removeModalId: function() { From 6224026d7280a856739c2faa240445e388105490 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 11 Sep 2019 15:44:43 -0400 Subject: [PATCH 11/24] Custom component for handling clin dollar input - Macro for CLIN dollar input HTML. - Custom Vue component to react to "fundingValid" validaiton --- js/components/clin_dollar_amount.js | 180 +++++++++++++++++++ templates/components/clin_dollar_amount.html | 70 ++++++++ templates/task_orders/step_3.html | 54 +----- 3 files changed, 257 insertions(+), 47 deletions(-) create mode 100644 js/components/clin_dollar_amount.js create mode 100644 templates/components/clin_dollar_amount.html diff --git a/js/components/clin_dollar_amount.js b/js/components/clin_dollar_amount.js new file mode 100644 index 00000000..5179a82a --- /dev/null +++ b/js/components/clin_dollar_amount.js @@ -0,0 +1,180 @@ +import MaskedInput, { conformToMask } from 'vue-text-mask' +import inputValidations from '../lib/input_validations' +import { formatDollars } from '../lib/dollars' +import { emitEvent } from '../lib/emitters' + +export default { + name: 'clindollaramount', + + components: { + MaskedInput, + }, + + props: { + name: String, + validation: { + type: String, + default: () => 'clinDollars', + }, + initialValue: { + type: String, + default: () => '', + }, + initialErrors: { + type: Array, + default: () => [], + }, + + optional: Boolean, + fundingValid: Boolean, + watch: { + type: Boolean, + default: false, + }, + }, + + data: function() { + return { + showErrorState: + (this.initialErrors && this.initialErrors.length) || false, + showValidationState: false, + mask: inputValidations[this.validation].mask, + pipe: inputValidations[this.validation].pipe || undefined, + keepCharPositions: + inputValidations[this.validation].keepCharPositions || false, + validationError: + this.initialErrors.join(' ') || + inputValidations[this.validation].validationError, + value: this.initialValue, + modified: false, + } + }, + + computed: { + rawValue: function() { + return this._rawValue(this.value) + }, + showError: function() { + return this.showErrorState || !this.fundingValid + }, + showValid: function() { + return this.showValidationState && this.fundingValid + }, + }, + + mounted: function() { + if (this.value) { + this._checkIfValid({ + value: this.value, + invalidate: true, + showValidationStateationIcon: false, + }) + + if (this.mask && this.validation !== 'email') { + const mask = + typeof this.mask.mask !== 'function' + ? this.mask + : mask.mask(this.value).filter(val => val !== '[]') + + this.value = conformToMask(this.value, mask).conformedValue + } + } + }, + + created: function() { + emitEvent('field-mount', this, { + optional: this.optional, + name: this.name, + valid: this._isValid(this.value), + }) + }, + + methods: { + // When user types a character + onInput: function(e) { + // When we use the native textarea element, we receive an event object + // When we use the masked-input component, we receive the value directly + const value = typeof e === 'object' ? e.target.value : e + this.value = value + this.modified = true + this._checkIfValid({ value }) + }, + + // When field is blurred (un-focused) + onChange: function(e) { + // Only invalidate the field when it blurs + this._checkIfValid({ value: e.target.value, invalidate: true }) + }, + + onBlur: function(e) { + if (!(this.optional && e.target.value === '')) { + this._checkIfValid({ value: e.target.value.trim(), invalidate: true }) + } else if (this.modified && !this.optional) { + this._checkIfValid({ value: e.target.value.trim(), invalidate: true }) + } + this.value = e.target.value.trim() + let value = Number.isNaN(e.target.value) ? '0' : e.target.value + this.value = formatDollars(this._rawValue(value)) + }, + + _checkIfValid: function({ + value, + invalidate = false, + showValidationStateationIcon = true, + }) { + const valid = this._isValid(value) + if (this.modified) { + this.validationError = inputValidations[this.validation].validationError + } + + // Show error messages or not + if (valid) { + this.showErrorState = false + } else if (invalidate) { + this.showErrorState = true + } + + if (showValidationStateationIcon) { + this.showValidationState = this.value != '' && valid + } + + // Emit a change event + emitEvent('field-change', this, { + value: this._rawValue(value), + valid: this._isValid(value), + name: this.name, + watch: this.watch, + }) + }, + + _rawValue: function(value) { + return inputValidations[this.validation].unmask.reduce( + (currentValue, character) => { + return currentValue.split(character).join('') + }, + value + ) + }, + + _validate: function(value) { + const rawValue = this._rawValue(value) + if (rawValue < 0 || rawValue > 1000000000 || !this.fundingValid) { + return false + } + return inputValidations[this.validation].match.test(rawValue) + }, + + _isValid: function(value) { + let valid = this._validate(value) + if (!this.modified && this.initialErrors && this.initialErrors.length) { + valid = false + } else if (this.optional && value === '') { + valid = true + } else if (!this.optional && value === '') { + valid = false + } + + return valid + }, + }, +} diff --git a/templates/components/clin_dollar_amount.html b/templates/components/clin_dollar_amount.html new file mode 100644 index 00000000..2263cbb7 --- /dev/null +++ b/templates/components/clin_dollar_amount.html @@ -0,0 +1,70 @@ +{% from 'components/icon.html' import Icon %} + +{% macro CLINDollarAmount(type, field=None, funding_validation=False) -%} +
+
+ +
+ {% if field %} +
+
+
+
+ {%- endmacro %} \ No newline at end of file diff --git a/templates/task_orders/step_3.html b/templates/task_orders/step_3.html index 1515e6b0..fabf706e 100644 --- a/templates/task_orders/step_3.html +++ b/templates/task_orders/step_3.html @@ -5,6 +5,7 @@ {% from 'components/icon.html' import Icon %} {% from 'components/options_input.html' import OptionsInput %} {% from 'components/text_input.html' import TextInput %} +{% from "components/clin_dollar_amount.html" import CLINDollarAmount %} {% from 'task_orders/form_header.html' import TOFormStepHeader %} {% set action = url_for("task_orders.submit_form_step_three_add_clins", task_order_id=task_order_id) %} @@ -117,56 +118,15 @@ {{ 'task_orders.form.clin_funding' | translate }} + {% if fields %} -
-
- {{ TextInput(fields.obligated_amount, validation='dollars', watch=True, optional=False) }} -
-
+ {{ CLINDollarAmount("total", field=fields.total_amount) }} + {{ CLINDollarAmount("obligated", field=fields.obligated_amount, funding_validation=True) }} {% else %} -
-
- -
- - - - - - - - - -
-
-
-
+ {{ CLINDollarAmount("total") }} + {{ CLINDollarAmount("obligated", funding_validation=True) }} {% endif %} +
From 33ed89df54aaac150a5427eb95fe86272b98c128 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 11 Sep 2019 10:19:15 -0400 Subject: [PATCH 12/24] Add percent obligated to step 3 form --- templates/task_orders/step_3.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/templates/task_orders/step_3.html b/templates/task_orders/step_3.html index fabf706e..87ce5daa 100644 --- a/templates/task_orders/step_3.html +++ b/templates/task_orders/step_3.html @@ -127,6 +127,9 @@ {{ CLINDollarAmount("obligated", funding_validation=True) }} {% endif %} +
Percent Obligated
+

+
From bdeb3dfa1acea9868af50e19f16e6260915d2fa0 Mon Sep 17 00:00:00 2001 From: graham-dds Date: Wed, 11 Sep 2019 10:24:37 -0400 Subject: [PATCH 13/24] use translations for clin validation message --- templates/components/clin_dollar_amount.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/components/clin_dollar_amount.html b/templates/components/clin_dollar_amount.html index 2263cbb7..21fe17f7 100644 --- a/templates/components/clin_dollar_amount.html +++ b/templates/components/clin_dollar_amount.html @@ -55,7 +55,7 @@