Merge pull request #1106 from dod-ccpo/clin-errors-contract-dates

PoP Range error message
This commit is contained in:
leigh-mil 2019-10-08 15:39:34 -04:00 committed by GitHub
commit bb6d656def
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 167 additions and 75 deletions

View File

@ -38,6 +38,22 @@ def validate_funding(form, field):
) )
def validate_date_in_range(form, field):
contract_start = app.config.get("CONTRACT_START_DATE")
contract_end = app.config.get("CONTRACT_END_DATE")
if field.data and (field.data < contract_start or field.data > contract_end):
raise ValidationError(
translate(
"forms.task_order.pop_errors.range",
{
"start": contract_start.strftime("%b %d, %Y"),
"end": contract_end.strftime("%b %d, %Y"),
},
)
)
class CLINForm(FlaskForm): class CLINForm(FlaskForm):
jedi_clin_type = SelectField( jedi_clin_type = SelectField(
translate("task_orders.form.clin_type_label"), translate("task_orders.form.clin_type_label"),
@ -52,13 +68,13 @@ class CLINForm(FlaskForm):
translate("task_orders.form.pop_start"), translate("task_orders.form.pop_start"),
description=translate("task_orders.form.pop_example"), description=translate("task_orders.form.pop_example"),
format="%m/%d/%Y", format="%m/%d/%Y",
validators=[Optional()], validators=[validate_date_in_range],
) )
end_date = DateField( end_date = DateField(
translate("task_orders.form.pop_end"), translate("task_orders.form.pop_end"),
description=translate("task_orders.form.pop_example"), description=translate("task_orders.form.pop_example"),
format="%m/%d/%Y", format="%m/%d/%Y",
validators=[Optional()], validators=[validate_date_in_range],
) )
total_amount = DecimalField( total_amount = DecimalField(
label=translate("task_orders.form.total_funds_label"), label=translate("task_orders.form.total_funds_label"),
@ -84,8 +100,6 @@ class CLINForm(FlaskForm):
def validate(self, *args, **kwargs): def validate(self, *args, **kwargs):
valid = super().validate(*args, **kwargs) valid = super().validate(*args, **kwargs)
contract_start = app.config.get("CONTRACT_START_DATE")
contract_end = app.config.get("CONTRACT_END_DATE")
if ( if (
self.start_date.data self.start_date.data
@ -97,24 +111,6 @@ class CLINForm(FlaskForm):
) )
valid = False valid = False
if self.start_date.data and self.start_date.data <= contract_start:
self.start_date.errors.append(
translate(
"forms.task_order.pop_errors.start",
{"date": contract_start.strftime("%b %d, %Y")},
)
)
valid = False
if self.end_date.data and self.end_date.data >= contract_end:
self.end_date.errors.append(
translate(
"forms.task_order.pop_errors.end",
{"date": contract_end.strftime("%b %d, %Y")},
)
)
valid = False
return valid return valid

View File

@ -250,4 +250,33 @@ describe('DateSelector', () => {
expect(component.maxError).toEqual(false) expect(component.maxError).toEqual(false)
}) })
}) })
describe('outsideRange', () => {
it('should return true if the date is before the minrange', () => {
component.minrange = '2020-01-01'
component.maxrange = '2025-01-01'
component.day = 1
component.month = 1
component.year = 2005
expect(component.outsideRange).toEqual(true)
})
it('should return true if the date is after the maxrange', () => {
component.minrange = '2020-01-01'
component.maxrange = '2025-01-01'
component.day = 1
component.month = 1
component.year = 2030
expect(component.outsideRange).toEqual(true)
})
it('should return false if the date is betwen minrange and maxrange', () => {
component.minrange = '2020-01-01'
component.maxrange = '2025-01-01'
component.day = 1
component.month = 1
component.year = 2022
expect(component.outsideRange).toEqual(false)
})
})
}) })

View File

@ -19,6 +19,8 @@ export default {
initialyear: { type: String }, initialyear: { type: String },
mindate: { type: String }, mindate: { type: String },
maxdate: { type: String }, maxdate: { type: String },
minrange: { type: String },
maxrange: { type: String },
nameTag: { type: String }, nameTag: { type: String },
optional: { optional: {
type: Boolean, type: Boolean,
@ -179,6 +181,15 @@ export default {
return false return false
}, },
outsideRange: function() {
if (!!this.maxrange && !!this.minrange && this.isDateComplete) {
return (
this.dateParsed < this.minRangeParsed ||
this.dateParsed > this.maxRangeParsed
)
}
},
maxDateParsed: function() { maxDateParsed: function() {
return new Date(this.maxdate) return new Date(this.maxdate)
}, },
@ -187,6 +198,14 @@ export default {
return new Date(this.mindate) return new Date(this.mindate)
}, },
maxRangeParsed: function() {
return new Date(this.maxrange)
},
minRangeParsed: function() {
return new Date(this.minrange)
},
dateParsed: function() { dateParsed: function() {
return new Date(this.formattedDate) return new Date(this.formattedDate)
}, },

View File

@ -332,6 +332,8 @@
<date-selector <date-selector
:mindate="initialMinStartDate" :mindate="initialMinStartDate"
:maxdate="maxStartProp" :maxdate="maxStartProp"
:minrange='initialMinStartDate'
:maxrange='initialMaxEndDate'
name-tag='start_date' name-tag='start_date'
initialmonth="" initialmonth=""
@ -352,10 +354,13 @@
For example: 07 04 1776 For example: 07 04 1776
</p> </p>
<div v-if='outsideRange && !minError' class="usa-input-error-message">
PoP start date must be before or on September 14, 2022.
</div>
<div v-if='minError' class="usa-input-error-message"> <div v-if='minError' class="usa-input-error-message">
PoP start date must be on or after September 14, 2019. PoP start date must be on or after September 14, 2019.
</div> </div>
<div v-if='maxError' class="usa-input-error-message"> <div v-if='maxError && !outsideRange' class="usa-input-error-message">
PoP start date must be before end date. PoP start date must be before end date.
</div> </div>
</legend> </legend>
@ -431,6 +436,8 @@
<date-selector <date-selector
:mindate="minEndProp" :mindate="minEndProp"
:maxdate="initialMaxEndDate" :maxdate="initialMaxEndDate"
:minrange='initialMinStartDate'
:maxrange='initialMaxEndDate'
name-tag='end_date' name-tag='end_date'
initialmonth="" initialmonth=""
@ -473,11 +480,14 @@
For example: 07 04 1776 For example: 07 04 1776
</p> </p>
<div v-if='minError' class="usa-input-error-message"> <div v-if='outsideRange && !maxError' class="usa-input-error-message">
PoP end date must be on or after September 14, 2019.
</div>
<div v-if='minError && !outsideRange' class="usa-input-error-message">
PoP end date must be after start date. PoP end date must be after start date.
</div> </div>
<div v-if='maxError' class="usa-input-error-message"> <div v-if='maxError' class="usa-input-error-message">
PoP end date must be on or after September 14, 2022. PoP end date must be before or on September 14, 2022.
</div> </div>
</legend> </legend>

View File

@ -19,6 +19,8 @@
<date-selector <date-selector
:mindate="initialMinStartDate" :mindate="initialMinStartDate"
:maxdate="maxStartProp" :maxdate="maxStartProp"
:minrange='initialMinStartDate'
:maxrange='initialMaxEndDate'
name-tag='start_date' name-tag='start_date'
initialmonth="" initialmonth=""
@ -39,10 +41,14 @@
For example: 07 04 1776 For example: 07 04 1776
</p> </p>
<div v-if='outsideRange && !minError' class="usa-input-error-message">
{{ ""}}
PoP start date must be before or on September 14, 2022.
</div>
<div v-if='minError' class="usa-input-error-message"> <div v-if='minError' class="usa-input-error-message">
PoP start date must be on or after September 14, 2019. PoP start date must be on or after September 14, 2019.
</div> </div>
<div v-if='maxError' class="usa-input-error-message"> <div v-if='maxError && !outsideRange' class="usa-input-error-message">
PoP start date must be before end date. PoP start date must be before end date.
</div> </div>
</legend> </legend>
@ -118,6 +124,8 @@
<date-selector <date-selector
:mindate="minEndProp" :mindate="minEndProp"
:maxdate="initialMaxEndDate" :maxdate="initialMaxEndDate"
:minrange='initialMinStartDate'
:maxrange='initialMaxEndDate'
name-tag='end_date' name-tag='end_date'
initialmonth="" initialmonth=""
@ -160,11 +168,14 @@
For example: 07 04 1776 For example: 07 04 1776
</p> </p>
<div v-if='minError' class="usa-input-error-message"> <div v-if='outsideRange && !maxError' class="usa-input-error-message">
PoP end date must be on or after September 14, 2019.
</div>
<div v-if='minError && !outsideRange' class="usa-input-error-message">
PoP end date must be after start date. PoP end date must be after start date.
</div> </div>
<div v-if='maxError' class="usa-input-error-message"> <div v-if='maxError' class="usa-input-error-message">
PoP end date must be on or after September 14, 2022. PoP end date must be before or on September 14, 2022.
</div> </div>
</legend> </legend>

View File

@ -203,7 +203,7 @@
<div> <div>
<div class="form-row"> <div class="form-row">
<div class="form-col"> <div class="form-col">
<date-selector :maxdate="maxStartProp" :mindate="initialMinStartDate" :name-tag="'clins-' + clinIndex + '-start_date'" :optional="false" inline-template="" v-on:date-change="handleDateChange"> <date-selector :maxdate="maxStartProp" :maxrange="initialMaxEndDate" :mindate="initialMinStartDate" :minrange="initialMinStartDate" :name-tag="'clins-' + clinIndex + '-start_date'" :optional="false" inline-template="" v-on:date-change="handleDateChange">
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid &amp;&amp; isDateComplete, 'usa-input--error': !isDateValid &amp;&amp; isDateComplete }"> <fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid &amp;&amp; isDateComplete, 'usa-input--error': !isDateValid &amp;&amp; isDateComplete }">
<legend> <legend>
<div class="usa-input__title"> <div class="usa-input__title">
@ -212,10 +212,13 @@
<p class="usa-input__help"> <p class="usa-input__help">
For example: 07 04 1776 For example: 07 04 1776
</p> </p>
<div class="usa-input-error-message" v-if="outsideRange &amp;&amp; !minError">
PoP start date must be before or on September 14, 2022.
</div>
<div class="usa-input-error-message" v-if="minError"> <div class="usa-input-error-message" v-if="minError">
PoP start date must be on or after September 14, 2019. PoP start date must be on or after September 14, 2019.
</div> </div>
<div class="usa-input-error-message" v-if="maxError"> <div class="usa-input-error-message" v-if="maxError &amp;&amp; !outsideRange">
PoP start date must be before end date. PoP start date must be before end date.
</div> </div>
</legend> </legend>
@ -251,7 +254,7 @@
</div> </div>
<div class="form-row"> <div class="form-row">
<div class="form-col"> <div class="form-col">
<date-selector :maxdate="initialMaxEndDate" :mindate="minEndProp" :name-tag="'clins-' + clinIndex + '-end_date'" :optional="false" inline-template="" v-on:date-change="handleDateChange"> <date-selector :maxdate="initialMaxEndDate" :maxrange="initialMaxEndDate" :mindate="minEndProp" :minrange="initialMinStartDate" :name-tag="'clins-' + clinIndex + '-end_date'" :optional="false" inline-template="" v-on:date-change="handleDateChange">
<fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid &amp;&amp; isDateComplete, 'usa-input--error': !isDateValid &amp;&amp; isDateComplete }"> <fieldset :name="name" class="usa-input date-picker" v-bind:class="{ 'usa-input--success': isDateValid &amp;&amp; isDateComplete, 'usa-input--error': !isDateValid &amp;&amp; isDateComplete }">
<legend> <legend>
<div class="usa-input__title"> <div class="usa-input__title">
@ -267,11 +270,14 @@
<p class="usa-input__help"> <p class="usa-input__help">
For example: 07 04 1776 For example: 07 04 1776
</p> </p>
<div class="usa-input-error-message" v-if="minError"> <div class="usa-input-error-message" v-if="outsideRange &amp;&amp; !maxError">
PoP end date must be on or after September 14, 2019.
</div>
<div class="usa-input-error-message" v-if="minError &amp;&amp; !outsideRange">
PoP end date must be after start date. PoP end date must be after start date.
</div> </div>
<div class="usa-input-error-message" v-if="maxError"> <div class="usa-input-error-message" v-if="maxError">
PoP end date must be on or after September 14, 2022. PoP end date must be before or on September 14, 2022.
</div> </div>
</legend> </legend>
<div class="date-picker-component"> <div class="date-picker-component">

View File

@ -2,6 +2,8 @@
{% from 'components/icon.html' import Icon %} {% from 'components/icon.html' import Icon %}
{% macro PopDateRange(start_field=None, end_field=None, mindate=mindate, maxdate=maxdate, optional=True, index=None) %} {% macro PopDateRange(start_field=None, end_field=None, mindate=mindate, maxdate=maxdate, optional=True, index=None) %}
{% set formatted_end = maxdate | formattedDate(formatter="%B %d, %Y") %}
{% set formatted_start = mindate | formattedDate(formatter="%B %d, %Y") %}
<pop-date-range <pop-date-range
initial-min-start-date="{{ mindate }}" initial-min-start-date="{{ mindate }}"
initial-max-end-date="{{ maxdate }}" initial-max-end-date="{{ maxdate }}"
@ -24,6 +26,8 @@
<date-selector <date-selector
:mindate="initialMinStartDate" :mindate="initialMinStartDate"
:maxdate="maxStartProp" :maxdate="maxStartProp"
:minrange='initialMinStartDate'
:maxrange='initialMaxEndDate'
{% if start_field %} {% if start_field %}
name-tag='{{ start_field.name }}' name-tag='{{ start_field.name }}'
initialmonth="{{ start_field.data.month }}" initialmonth="{{ start_field.data.month }}"
@ -46,11 +50,14 @@
{{ "task_orders.form.pop_example" | translate | safe }} {{ "task_orders.form.pop_example" | translate | safe }}
</p> </p>
<div v-if='minError' class="usa-input-error-message"> <div v-if='outsideRange && !minError' class="usa-input-error-message">
PoP start date must be on or after {{ mindate | formattedDate(formatter="%B %d, %Y") }}. {{ "forms.task_order.pop_errors.start_past_contract" | translate({"date": formatted_end}) }}
</div> </div>
<div v-if='maxError' class="usa-input-error-message"> <div v-if='minError' class="usa-input-error-message">
PoP start date must be before end date. {{ "forms.task_order.pop_errors.start_pre_contract" | translate({"date": formatted_start}) }}
</div>
<div v-if='maxError && !outsideRange' class="usa-input-error-message">
{{ "forms.task_order.pop_errors.date_order" | translate }}
</div> </div>
</legend> </legend>
@ -118,6 +125,8 @@
<date-selector <date-selector
:mindate="minEndProp" :mindate="minEndProp"
:maxdate="initialMaxEndDate" :maxdate="initialMaxEndDate"
:minrange='initialMinStartDate'
:maxrange='initialMaxEndDate'
{% if end_field %} {% if end_field %}
name-tag='{{ end_field.name }}' name-tag='{{ end_field.name }}'
initialmonth="{{ end_field.data.month }}" initialmonth="{{ end_field.data.month }}"
@ -135,18 +144,20 @@
<div class="usa-input__title"> <div class="usa-input__title">
{{ 'task_orders.form.pop_end' | translate }} {{ 'task_orders.form.pop_end' | translate }}
</div> </div>
{% set formatted_end_date = maxdate | formattedDate(formatter="%B %d, %Y") %} {{ Alert(message="task_orders.form.pop_end_alert" | translate({'end_date': formatted_end })) }}
{{ Alert(message="task_orders.form.pop_end_alert" | translate({'end_date': formatted_end_date })) }}
<p class='usa-input__help'> <p class='usa-input__help'>
{{ 'task_orders.form.pop_example' | translate }} {{ 'task_orders.form.pop_example' | translate }}
</p> </p>
<div v-if='minError' class="usa-input-error-message"> <div v-if='outsideRange && !maxError' class="usa-input-error-message">
PoP end date must be after start date. {{ "forms.task_order.pop_errors.end_pre_contract" | translate({"date": formatted_start}) }}
</div>
<div v-if='minError && !outsideRange' class="usa-input-error-message">
{{ "forms.task_order.pop_errors.date_order" | translate }}
</div> </div>
<div v-if='maxError' class="usa-input-error-message"> <div v-if='maxError' class="usa-input-error-message">
PoP end date must be on or after {{ formatted_end_date }}. {{ "forms.task_order.pop_errors.end_past_contract" | translate({"date": formatted_end}) }}
</div> </div>
</legend> </legend>

View File

@ -47,17 +47,24 @@ def test_clin_form_pop_dates_within_contract_dates():
start_date=invalid_start, end_date=invalid_end start_date=invalid_start, end_date=invalid_end
) )
clin_form = CLINForm(obj=invalid_clin) clin_form = CLINForm(obj=invalid_clin)
assert not clin_form.validate() assert not clin_form.validate()
assert ( assert (
translate( translate(
"forms.task_order.pop_errors.start", "forms.task_order.pop_errors.range",
{"date": CONTRACT_START_DATE.strftime("%b %d, %Y")}, {
"start": CONTRACT_START_DATE.strftime("%b %d, %Y"),
"end": CONTRACT_END_DATE.strftime("%b %d, %Y"),
},
) )
) in clin_form.start_date.errors ) in clin_form.start_date.errors
assert ( assert (
translate( translate(
"forms.task_order.pop_errors.end", "forms.task_order.pop_errors.range",
{"date": CONTRACT_END_DATE.strftime("%b %d, %Y")}, {
"start": CONTRACT_START_DATE.strftime("%b %d, %Y"),
"end": CONTRACT_END_DATE.strftime("%b %d, %Y"),
},
) )
) in clin_form.end_date.errors ) in clin_form.end_date.errors

View File

@ -211,8 +211,11 @@ forms:
number_description: Task order number (13 digits) number_description: Task order number (13 digits)
pop_errors: pop_errors:
date_order: PoP start date must be before end date. date_order: PoP start date must be before end date.
end: PoP end date must be before or on {date}. range: Date must be between {start} and {end}.
start: PoP start date must be on or after {date}. end_past_contract: PoP end date must be before or on {date}.
end_pre_contract: PoP end date must be after or on {date}.
start_past_contract: PoP start date must be before or on {date}.
start_pre_contract: 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 dont need to include a detailed plan of execution, but should list key requirements. This section will be reviewed by your contracting officer, but wont be sent to the CCPO. <p>Not sure how to describe your scope? <a href="#">Read some examples</a> to get some inspiration.</p>' scope_description: 'What do you plan to do on the cloud? Some examples might include migrating an existing application or creating a prototype. You dont need to include a detailed plan of execution, but should list key requirements. This section will be reviewed by your contracting officer, but wont be sent to the CCPO. <p>Not sure how to describe your scope? <a href="#">Read some examples</a> to get some inspiration.</p>'
scope_label: Cloud project scope scope_label: Cloud project scope
clin_funding_errors: clin_funding_errors: