Merge pull request #1059 from dod-ccpo/pop-configs

PoP validation & Contract date configs
This commit is contained in:
leigh-mil 2019-09-10 09:35:46 -04:00 committed by GitHub
commit aa45e6b2e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 167 additions and 28 deletions

View File

@ -9,11 +9,13 @@ from wtforms.fields import (
from wtforms.fields.html5 import DateField
from wtforms.validators import Required, Optional, Length
from flask_wtf import FlaskForm
from datetime import datetime
from .data import JEDI_CLIN_TYPES
from .fields import SelectField
from .forms import BaseForm
from atst.utils.localization import translate
from flask import current_app as app
def coerce_enum(enum_inst):
@ -52,16 +54,41 @@ class CLINForm(FlaskForm):
def validate(self, *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 (
self.start_date.data
and self.end_date.data
and self.start_date.data > self.end_date.data
):
self.start_date.errors.append(
translate("forms.task_order.start_date_error")
translate("forms.task_order.pop_errors.date_order")
)
return False
else:
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

View File

@ -22,6 +22,9 @@ def render_task_orders_edit(
):
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:
task_order = TaskOrders.get(task_order_id)
portfolio_id = task_order.portfolio_id

View File

@ -2,6 +2,8 @@
CAC_URL = http://localhost:8000/login-redirect
CA_CHAIN = ssl/server-certs/ca-chain.pem
CLASSIFIED = false
CONTRACT_START_DATE = 2019-09-14
CONTRACT_END_DATE = 2022-09-14
COOKIE_SECRET = some-secret-please-replace
DISABLE_CRL_CHECK = false
CRL_FAIL_OPEN = false

View File

@ -1,3 +1,6 @@
import * as R from 'ramda'
import { format } from 'date-fns'
import DateSelector from './date_selector'
import { emitEvent } from '../lib/emitters'
import Modal from '../mixins/modal'
@ -34,6 +37,14 @@ export default {
type: String,
default: null,
},
contractStart: {
type: String,
required: true,
},
contractEnd: {
type: String,
required: true,
},
},
data: function() {
@ -44,19 +55,44 @@ export default {
? new Date(this.initialEndDate)
: undefined
const popValidation = !this.initialStartDate ? false : start < end
const showPopValidation = !this.initialStartDate ? false : !popValidation
const clinNumber = !!this.initialClinNumber
? this.initialClinNumber
: undefined
const contractStartDate = new Date(this.contractStart)
const contractEndDate = new Date(this.contractEnd)
return {
clinIndex: this.initialClinIndex,
startDate: start,
endDate: end,
popValid: popValidation,
showPopError: showPopValidation,
startDateValid: false,
endDateValid: false,
contractStartDate: contractStartDate,
contractEndDate: contractEndDate,
clinNumber: clinNumber,
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: {
checkPopValid: function() {
return this.startDate < this.endDate
return (
this.popDateOrder() &&
this.popStartsAfterContract() &&
this.popEndsBeforeContract()
)
},
validatePop: function() {
if (!!this.startDate && !!this.endDate) {
// 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, {
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) {
if (this._uid === event.parent_uid) {
if (event.name.includes(START_DATE)) {
if (!!event.value) this.startDate = new Date(event.value)
if (!!event.valid) this.startDateValid = event.valid
this.validatePop()
} else if (event.name.includes(END_DATE)) {
if (!!event.value) this.endDate = new Date(event.value)
if (!!event.valid) this.endDateValid = event.valid
this.validatePop()
} else if (event.name.includes(NUMBER)) {
this.clinNumber = event.value

View File

@ -23,6 +23,8 @@
v-bind:initial-clin-index='clinIndex'
v-bind:initial-clin-type="'JEDI_CLIN_1'"
{% endif %}
v-bind:contract-start="'{{ contract_start | string }}'"
v-bind:contract-end="'{{ contract_end | string }}'"
inline-template>
<div class="clin-card" v-if="showClin">
<div class="card__title">
@ -169,6 +171,7 @@
{{ 'task_orders.form.pop' | translate }}
</div>
</div>
{% set contract_end_formatted = contract_end | dateFromString(formatter="%Y-%m-%d") | formattedDate(formatter="%B %d, %Y") %}
{% if fields %}
<div class="form-row">
<div class="form-col">
@ -178,7 +181,7 @@
<div class="form-row">
<div class="form-col">
{% 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 %}
</div>
</div>
@ -255,7 +258,7 @@
{{ 'task_orders.form.pop_end' | translate }}
</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'>
{{ 'task_orders.form.pop_example' | translate }}
@ -314,15 +317,9 @@
</div>
{% endif %}
<div class="form-row">
<p class="usa-input-error-message form-has-errors">
<template v-if='showPopError'>
{% if fields and fields.start_date.errors %}
{{ fields.start_date.errors[0] }}
{% else %}
{{ "forms.task_order.start_date_error" | translate }}
{% endif %}
</template>
</p>
<div class="usa-input-error-message form-has-errors">
<p v-for="error in popErrors" :key="error" v-html='error'></p>
</div>
</div>
</div>

View File

@ -1,4 +1,6 @@
import datetime
from dateutil.relativedelta import relativedelta
from flask import current_app as app
from atst.forms.task_order import CLINForm
from atst.models import JEDICLINType
@ -22,7 +24,10 @@ def test_clin_form_start_date_before_end_date():
)
clin_form = CLINForm(obj=invalid_clin)
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_end = datetime.date(2020, 12, 12)
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)
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()

View File

@ -197,9 +197,12 @@ forms:
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.
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 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
start_date_error: PoP start date must be before end date.
team_experience:
built_1: Built, migrated, or consulted on 1-2 applications
built_3: Built, migrated, or consulted on 3-5 applications
@ -382,7 +385,7 @@ task_orders:
obligated_funds_label: Obligated Funds
pop: Period of Performance
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_start: Start Date
review_button: Review task order