Merge pull request #1059 from dod-ccpo/pop-configs
PoP validation & Contract date configs
This commit is contained in:
commit
aa45e6b2e1
@ -9,11 +9,13 @@ from wtforms.fields import (
|
|||||||
from wtforms.fields.html5 import DateField
|
from wtforms.fields.html5 import DateField
|
||||||
from wtforms.validators import Required, Optional, Length
|
from wtforms.validators import Required, Optional, Length
|
||||||
from flask_wtf import FlaskForm
|
from flask_wtf import FlaskForm
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
from .data import JEDI_CLIN_TYPES
|
from .data import JEDI_CLIN_TYPES
|
||||||
from .fields import SelectField
|
from .fields import SelectField
|
||||||
from .forms import BaseForm
|
from .forms import BaseForm
|
||||||
from atst.utils.localization import translate
|
from atst.utils.localization import translate
|
||||||
|
from flask import current_app as app
|
||||||
|
|
||||||
|
|
||||||
def coerce_enum(enum_inst):
|
def coerce_enum(enum_inst):
|
||||||
@ -52,17 +54,42 @@ 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 = datetime.strptime(
|
||||||
|
app.config.get("CONTRACT_START_DATE"), "%Y-%m-%d"
|
||||||
|
).date()
|
||||||
|
contract_end = datetime.strptime(
|
||||||
|
app.config.get("CONTRACT_END_DATE"), "%Y-%m-%d"
|
||||||
|
).date()
|
||||||
|
|
||||||
if (
|
if (
|
||||||
self.start_date.data
|
self.start_date.data
|
||||||
and self.end_date.data
|
and self.end_date.data
|
||||||
and self.start_date.data > self.end_date.data
|
and self.start_date.data > self.end_date.data
|
||||||
):
|
):
|
||||||
self.start_date.errors.append(
|
self.start_date.errors.append(
|
||||||
translate("forms.task_order.start_date_error")
|
translate("forms.task_order.pop_errors.date_order")
|
||||||
)
|
)
|
||||||
return False
|
valid = False
|
||||||
else:
|
|
||||||
return valid
|
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
|
||||||
|
|
||||||
|
|
||||||
class AttachmentForm(BaseForm):
|
class AttachmentForm(BaseForm):
|
||||||
|
@ -22,6 +22,9 @@ def render_task_orders_edit(
|
|||||||
):
|
):
|
||||||
render_args = extra_args or {}
|
render_args = extra_args or {}
|
||||||
|
|
||||||
|
render_args["contract_start"] = app.config.get("CONTRACT_START_DATE")
|
||||||
|
render_args["contract_end"] = app.config.get("CONTRACT_END_DATE")
|
||||||
|
|
||||||
if task_order_id:
|
if task_order_id:
|
||||||
task_order = TaskOrders.get(task_order_id)
|
task_order = TaskOrders.get(task_order_id)
|
||||||
portfolio_id = task_order.portfolio_id
|
portfolio_id = task_order.portfolio_id
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
CAC_URL = http://localhost:8000/login-redirect
|
CAC_URL = http://localhost:8000/login-redirect
|
||||||
CA_CHAIN = ssl/server-certs/ca-chain.pem
|
CA_CHAIN = ssl/server-certs/ca-chain.pem
|
||||||
CLASSIFIED = false
|
CLASSIFIED = false
|
||||||
|
CONTRACT_START_DATE = 2019-09-14
|
||||||
|
CONTRACT_END_DATE = 2022-09-14
|
||||||
COOKIE_SECRET = some-secret-please-replace
|
COOKIE_SECRET = some-secret-please-replace
|
||||||
DISABLE_CRL_CHECK = false
|
DISABLE_CRL_CHECK = false
|
||||||
CRL_FAIL_OPEN = false
|
CRL_FAIL_OPEN = false
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import * as R from 'ramda'
|
||||||
|
import { format } from 'date-fns'
|
||||||
|
|
||||||
import DateSelector from './date_selector'
|
import DateSelector from './date_selector'
|
||||||
import { emitEvent } from '../lib/emitters'
|
import { emitEvent } from '../lib/emitters'
|
||||||
import Modal from '../mixins/modal'
|
import Modal from '../mixins/modal'
|
||||||
@ -34,6 +37,14 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
|
contractStart: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
contractEnd: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
data: function() {
|
data: function() {
|
||||||
@ -44,19 +55,44 @@ export default {
|
|||||||
? new Date(this.initialEndDate)
|
? new Date(this.initialEndDate)
|
||||||
: undefined
|
: undefined
|
||||||
const popValidation = !this.initialStartDate ? false : start < end
|
const popValidation = !this.initialStartDate ? false : start < end
|
||||||
const showPopValidation = !this.initialStartDate ? false : !popValidation
|
|
||||||
const clinNumber = !!this.initialClinNumber
|
const clinNumber = !!this.initialClinNumber
|
||||||
? this.initialClinNumber
|
? this.initialClinNumber
|
||||||
: undefined
|
: undefined
|
||||||
|
const contractStartDate = new Date(this.contractStart)
|
||||||
|
const contractEndDate = new Date(this.contractEnd)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
clinIndex: this.initialClinIndex,
|
clinIndex: this.initialClinIndex,
|
||||||
startDate: start,
|
startDate: start,
|
||||||
endDate: end,
|
endDate: end,
|
||||||
popValid: popValidation,
|
popValid: popValidation,
|
||||||
showPopError: showPopValidation,
|
startDateValid: false,
|
||||||
|
endDateValid: false,
|
||||||
|
contractStartDate: contractStartDate,
|
||||||
|
contractEndDate: contractEndDate,
|
||||||
clinNumber: clinNumber,
|
clinNumber: clinNumber,
|
||||||
showClin: true,
|
showClin: true,
|
||||||
|
popErrors: [],
|
||||||
|
validations: [
|
||||||
|
{
|
||||||
|
func: this.popDateOrder,
|
||||||
|
message: 'PoP start date must be before end date.',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
func: this.popStartsAfterContract,
|
||||||
|
message: `PoP start date must be on or after ${format(
|
||||||
|
contractStartDate,
|
||||||
|
'MMM D, YYYY'
|
||||||
|
)}.`,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
func: this.popEndsBeforeContract,
|
||||||
|
message: `PoP end date must be before or on ${format(
|
||||||
|
contractEndDate,
|
||||||
|
'MMM D, YYYY'
|
||||||
|
)}.`,
|
||||||
|
},
|
||||||
|
],
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -74,29 +110,58 @@ export default {
|
|||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
checkPopValid: function() {
|
checkPopValid: function() {
|
||||||
return this.startDate < this.endDate
|
return (
|
||||||
|
this.popDateOrder() &&
|
||||||
|
this.popStartsAfterContract() &&
|
||||||
|
this.popEndsBeforeContract()
|
||||||
|
)
|
||||||
},
|
},
|
||||||
|
|
||||||
validatePop: function() {
|
validatePop: function() {
|
||||||
if (!!this.startDate && !!this.endDate) {
|
this.popValid = this.checkPopValid()
|
||||||
// only want to update popValid and showPopError if both dates are filled in
|
|
||||||
this.popValid = this.checkPopValid()
|
|
||||||
this.showPopError = !this.popValid
|
|
||||||
}
|
|
||||||
|
|
||||||
emitEvent('field-change', this, {
|
emitEvent('field-change', this, {
|
||||||
name: 'clins-' + this.clinIndex + '-' + POP,
|
name: 'clins-' + this.clinIndex + '-' + POP,
|
||||||
valid: this.checkPopValid(),
|
valid: this.popValid,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.popErrors = R.pipe(
|
||||||
|
R.map(validation =>
|
||||||
|
!validation.func() ? validation.message : undefined
|
||||||
|
),
|
||||||
|
R.filter(Boolean)
|
||||||
|
)(this.validations)
|
||||||
|
},
|
||||||
|
|
||||||
|
popStartsAfterContract: function() {
|
||||||
|
if (this.startDateValid) {
|
||||||
|
return this.startDate >= this.contractStartDate
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
popEndsBeforeContract: function() {
|
||||||
|
if (this.endDateValid) {
|
||||||
|
return this.endDate <= this.contractEndDate
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
|
||||||
|
popDateOrder: function() {
|
||||||
|
if (!!this.startDate && !!this.endDate) {
|
||||||
|
return this.startDate < this.endDate
|
||||||
|
}
|
||||||
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
handleFieldChange: function(event) {
|
handleFieldChange: function(event) {
|
||||||
if (this._uid === event.parent_uid) {
|
if (this._uid === event.parent_uid) {
|
||||||
if (event.name.includes(START_DATE)) {
|
if (event.name.includes(START_DATE)) {
|
||||||
if (!!event.value) this.startDate = new Date(event.value)
|
if (!!event.value) this.startDate = new Date(event.value)
|
||||||
|
if (!!event.valid) this.startDateValid = event.valid
|
||||||
this.validatePop()
|
this.validatePop()
|
||||||
} else if (event.name.includes(END_DATE)) {
|
} else if (event.name.includes(END_DATE)) {
|
||||||
if (!!event.value) this.endDate = new Date(event.value)
|
if (!!event.value) this.endDate = new Date(event.value)
|
||||||
|
if (!!event.valid) this.endDateValid = event.valid
|
||||||
this.validatePop()
|
this.validatePop()
|
||||||
} else if (event.name.includes(NUMBER)) {
|
} else if (event.name.includes(NUMBER)) {
|
||||||
this.clinNumber = event.value
|
this.clinNumber = event.value
|
||||||
|
@ -23,6 +23,8 @@
|
|||||||
v-bind:initial-clin-index='clinIndex'
|
v-bind:initial-clin-index='clinIndex'
|
||||||
v-bind:initial-clin-type="'JEDI_CLIN_1'"
|
v-bind:initial-clin-type="'JEDI_CLIN_1'"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
v-bind:contract-start="'{{ contract_start | string }}'"
|
||||||
|
v-bind:contract-end="'{{ contract_end | string }}'"
|
||||||
inline-template>
|
inline-template>
|
||||||
<div class="clin-card" v-if="showClin">
|
<div class="clin-card" v-if="showClin">
|
||||||
<div class="card__title">
|
<div class="card__title">
|
||||||
@ -169,6 +171,7 @@
|
|||||||
{{ 'task_orders.form.pop' | translate }}
|
{{ 'task_orders.form.pop' | translate }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% set contract_end_formatted = contract_end | dateFromString(formatter="%Y-%m-%d") | formattedDate(formatter="%B %d, %Y") %}
|
||||||
{% if fields %}
|
{% if fields %}
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-col">
|
<div class="form-col">
|
||||||
@ -178,7 +181,7 @@
|
|||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-col">
|
<div class="form-col">
|
||||||
{% call DatePicker(fields.end_date, watch=True, optional=False) %}
|
{% call DatePicker(fields.end_date, watch=True, optional=False) %}
|
||||||
{{ Alert(message="task_orders.form.pop_end_alert" | translate) }}
|
{{ Alert(message="task_orders.form.pop_end_alert" | translate({'end_date': contract_end_formatted})) }}
|
||||||
{% endcall %}
|
{% endcall %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -255,7 +258,7 @@
|
|||||||
{{ 'task_orders.form.pop_end' | translate }}
|
{{ 'task_orders.form.pop_end' | translate }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{ Alert(message="A CLIN's period of performance must end before September 14, 2022.") }}
|
{{ Alert(message="task_orders.form.pop_end_alert" | translate({'end_date': contract_end_formatted})) }}
|
||||||
|
|
||||||
<p class='usa-input__help'>
|
<p class='usa-input__help'>
|
||||||
{{ 'task_orders.form.pop_example' | translate }}
|
{{ 'task_orders.form.pop_example' | translate }}
|
||||||
@ -314,15 +317,9 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<p class="usa-input-error-message form-has-errors">
|
<div class="usa-input-error-message form-has-errors">
|
||||||
<template v-if='showPopError'>
|
<p v-for="error in popErrors" :key="error" v-html='error'></p>
|
||||||
{% if fields and fields.start_date.errors %}
|
</div>
|
||||||
{{ fields.start_date.errors[0] }}
|
|
||||||
{% else %}
|
|
||||||
{{ "forms.task_order.start_date_error" | translate }}
|
|
||||||
{% endif %}
|
|
||||||
</template>
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
|
from dateutil.relativedelta import relativedelta
|
||||||
|
from flask import current_app as app
|
||||||
|
|
||||||
from atst.forms.task_order import CLINForm
|
from atst.forms.task_order import CLINForm
|
||||||
from atst.models import JEDICLINType
|
from atst.models import JEDICLINType
|
||||||
@ -22,7 +24,10 @@ def test_clin_form_start_date_before_end_date():
|
|||||||
)
|
)
|
||||||
clin_form = CLINForm(obj=invalid_clin)
|
clin_form = CLINForm(obj=invalid_clin)
|
||||||
assert not clin_form.validate()
|
assert not clin_form.validate()
|
||||||
assert translate("forms.task_order.start_date_error") in clin_form.start_date.errors
|
assert (
|
||||||
|
translate("forms.task_order.pop_errors.date_order")
|
||||||
|
in clin_form.start_date.errors
|
||||||
|
)
|
||||||
valid_start = datetime.date(2020, 1, 1)
|
valid_start = datetime.date(2020, 1, 1)
|
||||||
valid_end = datetime.date(2020, 12, 12)
|
valid_end = datetime.date(2020, 12, 12)
|
||||||
valid_clin = factories.CLINFactory.create(
|
valid_clin = factories.CLINFactory.create(
|
||||||
@ -30,3 +35,40 @@ def test_clin_form_start_date_before_end_date():
|
|||||||
)
|
)
|
||||||
valid_clin_form = CLINForm(obj=valid_clin)
|
valid_clin_form = CLINForm(obj=valid_clin)
|
||||||
assert valid_clin_form.validate()
|
assert valid_clin_form.validate()
|
||||||
|
|
||||||
|
|
||||||
|
def test_clin_form_pop_dates_within_contract_dates():
|
||||||
|
CONTRACT_START_DATE = datetime.datetime.strptime(
|
||||||
|
app.config.get("CONTRACT_START_DATE"), "%Y-%m-%d"
|
||||||
|
).date()
|
||||||
|
CONTRACT_END_DATE = datetime.datetime.strptime(
|
||||||
|
app.config.get("CONTRACT_END_DATE"), "%Y-%m-%d"
|
||||||
|
).date()
|
||||||
|
|
||||||
|
invalid_start = CONTRACT_START_DATE - relativedelta(months=1)
|
||||||
|
invalid_end = CONTRACT_END_DATE + relativedelta(months=1)
|
||||||
|
invalid_clin = factories.CLINFactory.create(
|
||||||
|
start_date=invalid_start, end_date=invalid_end
|
||||||
|
)
|
||||||
|
clin_form = CLINForm(obj=invalid_clin)
|
||||||
|
assert not clin_form.validate()
|
||||||
|
assert (
|
||||||
|
translate(
|
||||||
|
"forms.task_order.pop_errors.start",
|
||||||
|
{"date": CONTRACT_START_DATE.strftime("%b %d, %Y")},
|
||||||
|
)
|
||||||
|
) in clin_form.start_date.errors
|
||||||
|
assert (
|
||||||
|
translate(
|
||||||
|
"forms.task_order.pop_errors.end",
|
||||||
|
{"date": CONTRACT_END_DATE.strftime("%b %d, %Y")},
|
||||||
|
)
|
||||||
|
) in clin_form.end_date.errors
|
||||||
|
|
||||||
|
valid_start = CONTRACT_START_DATE + relativedelta(months=1)
|
||||||
|
valid_end = CONTRACT_END_DATE - relativedelta(months=1)
|
||||||
|
valid_clin = factories.CLINFactory.create(
|
||||||
|
start_date=valid_start, end_date=valid_end
|
||||||
|
)
|
||||||
|
valid_clin_form = CLINForm(obj=valid_clin)
|
||||||
|
assert valid_clin_form.validate()
|
||||||
|
@ -197,9 +197,12 @@ forms:
|
|||||||
not_sure: 'Not sure, unsure if planning to develop natively in the cloud'
|
not_sure: 'Not sure, unsure if planning to develop natively in the cloud'
|
||||||
not_sure_help: Not sure? Talk to your technical lead about where and how they plan on developing your application.
|
not_sure_help: Not sure? Talk to your technical lead about where and how they plan on developing your application.
|
||||||
number_description: Task order number (13 digits)
|
number_description: Task order number (13 digits)
|
||||||
|
pop_errors:
|
||||||
|
date_order: PoP start date must be before end date.
|
||||||
|
end: PoP end date must be before or on {date}.
|
||||||
|
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. <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 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. <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
|
||||||
start_date_error: PoP start date must be before end date.
|
|
||||||
team_experience:
|
team_experience:
|
||||||
built_1: Built, migrated, or consulted on 1-2 applications
|
built_1: Built, migrated, or consulted on 1-2 applications
|
||||||
built_3: Built, migrated, or consulted on 3-5 applications
|
built_3: Built, migrated, or consulted on 3-5 applications
|
||||||
@ -382,7 +385,7 @@ task_orders:
|
|||||||
obligated_funds_label: Obligated Funds
|
obligated_funds_label: Obligated Funds
|
||||||
pop: Period of Performance
|
pop: Period of Performance
|
||||||
pop_end: End Date
|
pop_end: End Date
|
||||||
pop_end_alert: "A CLIN's period of performance must end before September 14, 2022."
|
pop_end_alert: "A CLIN's period of performance must end before {end_date}."
|
||||||
pop_example: "For example: 07 04 1776"
|
pop_example: "For example: 07 04 1776"
|
||||||
pop_start: Start Date
|
pop_start: Start Date
|
||||||
review_button: Review task order
|
review_button: Review task order
|
||||||
|
Loading…
x
Reference in New Issue
Block a user