diff --git a/alembic/versions/4f4defb7b440_add_expiration_date_to_task_order.py b/alembic/versions/4f4defb7b440_add_expiration_date_to_task_order.py
new file mode 100644
index 00000000..86f3e5d2
--- /dev/null
+++ b/alembic/versions/4f4defb7b440_add_expiration_date_to_task_order.py
@@ -0,0 +1,28 @@
+"""add expiration date to task order
+
+Revision ID: 4f4defb7b440
+Revises: 2572be7fb7fc
+Create Date: 2018-09-17 15:22:33.240310
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+
+# revision identifiers, used by Alembic.
+revision = '4f4defb7b440'
+down_revision = '2572be7fb7fc'
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.add_column('task_order', sa.Column('expiration_date', sa.Date(), nullable=True))
+ # ### end Alembic commands ###
+
+
+def downgrade():
+ # ### commands auto generated by Alembic - please adjust! ###
+ op.drop_column('task_order', 'expiration_date')
+ # ### end Alembic commands ###
diff --git a/atst/forms/financial.py b/atst/forms/financial.py
index 337ea10f..4238df60 100644
--- a/atst/forms/financial.py
+++ b/atst/forms/financial.py
@@ -1,5 +1,6 @@
import re
-from wtforms.fields.html5 import EmailField
+import pendulum
+from wtforms.fields.html5 import DateField, EmailField
from wtforms.fields import StringField, FileField
from wtforms.validators import InputRequired, Required, Email, Regexp
from flask_wtf.file import FileAllowed
@@ -11,6 +12,7 @@ from atst.domain.task_orders import TaskOrders
from .fields import NewlineListField, SelectField
from .forms import ValidatedForm
from .data import FUNDING_TYPES
+from .validators import DateRange
PE_REGEX = re.compile(
@@ -166,6 +168,20 @@ class ExtendedFinancialForm(BaseFinancialForm):
funding_type_other = StringField("If other, please specify")
+ expiration_date = DateField(
+ "Task Order Expiration Date",
+ description="Please enter the expiration date for the Task Order",
+ validators=[
+ Required(),
+ DateRange(
+ lower_bound=pendulum.duration(days=0),
+ upper_bound=pendulum.duration(years=100),
+ message="Must be a date in the future.",
+ ),
+ ],
+ format="%m/%d/%Y",
+ )
+
clin_0001 = StringField(
"
- CLIN 0001
- - Unclassified IaaS and PaaS Amount
",
validators=[InputRequired()],
diff --git a/atst/models/task_order.py b/atst/models/task_order.py
index 46e08b18..9c30cca1 100644
--- a/atst/models/task_order.py
+++ b/atst/models/task_order.py
@@ -1,6 +1,6 @@
from enum import Enum
-from sqlalchemy import Column, Integer, String, ForeignKey, Enum as SQLAEnum
+from sqlalchemy import Column, Integer, String, ForeignKey, Enum as SQLAEnum, Date
from sqlalchemy.orm import relationship
from atst.models import Base
@@ -32,6 +32,7 @@ class TaskOrder(Base):
clin_1003 = Column(Integer)
clin_2001 = Column(Integer)
clin_2003 = Column(Integer)
+ expiration_date = Column(Date())
attachment_id = Column(ForeignKey("attachments.id"))
pdf = relationship("Attachment")
diff --git a/atst/routes/requests/requests_form.py b/atst/routes/requests/requests_form.py
index b0252b86..925a561c 100644
--- a/atst/routes/requests/requests_form.py
+++ b/atst/routes/requests/requests_form.py
@@ -146,5 +146,6 @@ def view_request_details(request_id=None):
"requests/details.html",
data=data,
request=request,
+ financial_review=financial_review,
requires_fv_action=requires_fv_action,
)
diff --git a/templates/requests/_review.html b/templates/requests/_review.html
index a01272b7..f070edd4 100644
--- a/templates/requests/_review.html
+++ b/templates/requests/_review.html
@@ -155,6 +155,8 @@
{{ DefinitionReviewField("If other, please specify", "task_order", "funding_type_other") }}
{% endif %}
+ {{ DefinitionReviewField("Task Order Expiration Date", "task_order", "expiration_date") }}
+
{{ DefinitionReviewField("- CLIN 0001
- - Unclassified IaaS and PaaS Amount
", "task_order", "clin_0001", filter="dollars") }}
{{ DefinitionReviewField("- CLIN 0003
- - Unclassified Cloud Support Package
", "task_order", "clin_0003", filter="dollars") }}
diff --git a/templates/requests/financial_verification.html b/templates/requests/financial_verification.html
index 4ae287af..4fdf6479 100644
--- a/templates/requests/financial_verification.html
+++ b/templates/requests/financial_verification.html
@@ -3,6 +3,7 @@
{% from "components/alert.html" import Alert %}
{% from "components/text_input.html" import TextInput %}
{% from "components/options_input.html" import OptionsInput %}
+{% from "components/date_input.html" import DateInput %}
{% block content %}
@@ -72,6 +73,8 @@
{{ TextInput(f.funding_type_other) }}
+ {{ DateInput(f.expiration_date, placeholder='MM / DD / YYYY', validation='date', tooltip='Please enter the expiration date for the task order only and do not include options that you may choose to exercise in the future.') }}
+
{{ TextInput(
f.clin_0001,
validation='dollars'
diff --git a/tests/conftest.py b/tests/conftest.py
index aa4558c1..14539df6 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,4 +1,5 @@
import os
+import datetime
import pytest
import alembic.config
import alembic.command
@@ -124,6 +125,7 @@ def extended_financial_verification_data(pdf_upload):
return {
"funding_type": "RDTE",
"funding_type_other": "other",
+ "expiration_date": "1/1/{}".format(datetime.date.today().year + 1),
"clin_0001": "50000",
"clin_0003": "13000",
"clin_1001": "30000",
diff --git a/tests/factories.py b/tests/factories.py
index ea15a0f6..e3019b88 100644
--- a/tests/factories.py
+++ b/tests/factories.py
@@ -170,6 +170,11 @@ class TaskOrderFactory(Base):
funding_type = FundingType.PROC
funding_type_other = None
number = "toABC123"
+ expiration_date = factory.LazyFunction(
+ lambda: datetime.date(
+ datetime.date.today().year + random.randrange(1, 15), 1, 1
+ )
+ )
clin_0001 = random.randrange(100, 100000)
clin_0003 = random.randrange(100, 100000)
clin_1001 = random.randrange(100, 100000)