Merge branch 'master' into ui/request-approval-screen

This commit is contained in:
andrewdds 2018-07-26 12:28:50 -04:00 committed by GitHub
commit 9bbea99aa0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 425 additions and 96 deletions

View File

@ -9,6 +9,7 @@ from atst.handlers.root import Root
from atst.handlers.login_redirect import LoginRedirect from atst.handlers.login_redirect import LoginRedirect
from atst.handlers.workspace import Workspace from atst.handlers.workspace import Workspace
from atst.handlers.request import Request from atst.handlers.request import Request
from atst.handlers.request_financial_verification import RequestFinancialVerification
from atst.handlers.request_new import RequestNew from atst.handlers.request_new import RequestNew
from atst.handlers.request_submit import RequestsSubmit from atst.handlers.request_submit import RequestsSubmit
from atst.handlers.dev import Dev from atst.handlers.dev import Dev
@ -98,6 +99,22 @@ def make_app(config, deps, **kwargs):
{"page": "request_approval"}, {"page": "request_approval"},
name="request_approval" name="request_approval"
), ),
url(
r"/requests/verify/(\S+)",
RequestFinancialVerification,
{
"page": "financial_verification",
"requests_client": deps["requests_client"],
"fundz_client": deps["fundz_client"],
},
name="financial_verification",
),
url(
r"/requests/financial_verification_submitted",
Main,
{"page": "requests/financial_verification_submitted"},
name="financial_verification_submitted",
),
url(r"/users", Main, {"page": "users"}, name="users"), url(r"/users", Main, {"page": "users"}, name="users"),
url(r"/reports", Main, {"page": "reports"}, name="reports"), url(r"/reports", Main, {"page": "reports"}, name="reports"),
url(r"/calculator", Main, {"page": "calculator"}, name="calculator"), url(r"/calculator", Main, {"page": "calculator"}, name="calculator"),

View File

@ -0,0 +1,75 @@
import tornado
from atst.handler import BaseHandler
from atst.forms.financial import FinancialForm
class RequestFinancialVerification(BaseHandler):
def initialize(self, page, requests_client, fundz_client):
self.page = page
self.requests_client = requests_client
self.fundz_client = fundz_client
@tornado.gen.coroutine
def get_existing_request(self, request_id):
if request_id is None:
return {}
request = yield self.requests_client.get("/requests/{}".format(request_id))
return request.json
@tornado.web.authenticated
@tornado.gen.coroutine
def get(self, request_id=None):
existing_request = yield self.get_existing_request(request_id)
form = FinancialForm(data=existing_request['body'].get('financial_verification'))
self.render(
"requests/financial_verification.html.to",
page=self.page,
f=form,
request_id=request_id,
)
@tornado.gen.coroutine
def update_request(self, request_id, form_data):
request_data = {
"creator_id": self.current_user["id"],
"request": {"financial_verification": form_data},
}
response = yield self.requests_client.patch(
"/requests/{}".format(request_id), json=request_data
)
return response
@tornado.web.authenticated
@tornado.gen.coroutine
def post(self, request_id=None):
self.check_xsrf_cookie()
post_data = self.request.arguments
existing_request = yield self.get_existing_request(request_id)
form = FinancialForm(post_data)
rerender_args = dict(request_id=request_id, f=form)
if form.validate():
response = yield self.update_request(request_id, form.data)
if response.ok:
valid = yield form.perform_extra_validation(
existing_request.get('body', {}).get('financial_verification'),
self.fundz_client
)
if valid:
self.redirect(
self.application.default_router.reverse_url("financial_verification_submitted")
)
else:
self.render(
"requests/financial_verification.html.to",
**rerender_args
)
else:
self.set_status(response.code)
else:
self.render(
"requests/financial_verification.html.to",
**rerender_args
)

View File

@ -6,7 +6,6 @@ from atst.forms.request import RequestForm
from atst.forms.org import OrgForm from atst.forms.org import OrgForm
from atst.forms.poc import POCForm from atst.forms.poc import POCForm
from atst.forms.review import ReviewForm from atst.forms.review import ReviewForm
from atst.forms.financial import FinancialForm
class RequestNew(BaseHandler): class RequestNew(BaseHandler):
@ -221,12 +220,6 @@ class JEDIRequestFlow(object):
"form": ReviewForm, "form": ReviewForm,
"show":True, "show":True,
}, },
{
"title": "Financial Verification",
"section": "financial_verification",
"form": FinancialForm,
"show": self.request and self.request["status"] == "approved",
},
] ]
@tornado.gen.coroutine @tornado.gen.coroutine

View File

@ -39,7 +39,8 @@
<tbody> <tbody>
{% for r in requests %} {% for r in requests %}
<tr> <tr>
<th scope="row"><a href="{{ reverse_url('request_form_update', 1, r['order_id']) }}">{{ r['order_id'] }}</a> <th scope="row">
<a href="{{ reverse_url('request_form_update', 1, r['order_id']) if r["status"] != "approved" else reverse_url('financial_verification', r['order_id']) }}">{{ r['order_id'] }}</a>
{% if r['is_new'] %}<span class="usa-label">New</span> {% if r['is_new'] %}<span class="usa-label">New</span>
</th> </th>
{% end %} {% end %}

View File

@ -0,0 +1,219 @@
{% extends "../base.html.to" %}
{% block content %}
<div class="col">
<div class="panel">
<div class="panel__content">
<div class="panel__heading">
<h1>Order #{{ request_id }}</h1>
<h2 id="financial-verification">Financial Verification</h2>
</div>
{% block form_action %}
<form method='POST' action="{{ reverse_url('financial_verification', request_id) }}" autocomplete="off">
{% end %}
{% module xsrf_form_html() %}
{% block form %}
{% autoescape None %}
{% if f.errors %}
<b class="usa-input-error-message">There were some errors, see below.</b>
{% end %}
<p class="usa-font-lead">In order to get you access to the JEDI Cloud, we will need you to enter the details below that will help us verify and account for your Task Order.</p>
{{ f.task_order_id.label }}
{{ f.task_order_id(placeholder="Example: 1234567899C0001") }}
{% for e in f.task_order_id.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.uii_ids.label }}
{{ f.uii_ids(placeholder="Example: \nDI 0CVA5786950 \nUN1945326361234786950") }}
{% for e in f.uii_ids.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.pe_id.label }}
{{ f.pe_id(placeholder="Example: 0203752A") }}
{% for e in f.pe_id.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.treasury_code.label }}
{{ f.treasury_code(placeholder="Example: 1200") }}
{% for e in f.treasury_code.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.ba_code.label }}
{{ f.ba_code(placeholder="Example: 02") }}
{% for e in f.ba_code.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
<!-- KO Information -->
<h3>Contracting Officer (KO) Information</h3>
{{ f.fname_co.label }}
{{ f.fname_co(placeholder="Contracting Officer first name") }}
{% for e in f.fname_co.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.lname_co.label }}
{{ f.lname_co(placeholder="Contracting Officer last name") }}
{% for e in f.lname_co.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.email_co.label }}
{{ f.email_co(placeholder="jane@mail.mil") }}
{% for e in f.email_co.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.office_co.label }}
{{ f.office_co(placeholder="Example: WHS") }}
{% for e in f.office_co.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
<!-- COR Information -->
<h3>Contracting Officer Representative (COR) Information</h3>
{{ f.fname_cor.label }}
{{ f.fname_cor(placeholder="Contracting Officer Representative first name") }}
{% for e in f.fname_cor.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.lname_cor.label }}
{{ f.lname_cor(placeholder="Contracting Officer Representative last name") }}
{% for e in f.lname_cor.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.email_cor.label }}
{{ f.email_cor(placeholder="jane@mail.mil") }}
{% for e in f.email_cor.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.office_cor.label }}
{{ f.office_cor(placeholder="Example: WHS") }}
{% for e in f.office_cor.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
<br><hr>
<em>&darr; FIELDS NEEDED FOR MANUAL ENTRY OF TASK ORDER INFORMATION (only necessary if EDA info not available)</em>
{{ f.funding_type.label }}
{{ f.funding_type }}
{% for e in f.funding_type.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.funding_type_other.label }}
{{ f.funding_type_other(placeholder="") }}
{% for e in f.funding_type_other.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.clin_0001.label }}
{{ f.clin_0001(placeholder="50,000") }}
{% for e in f.clin_0001.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.clin_0003.label }}
{{ f.clin_0003(placeholder="13,000") }}
{% for e in f.clin_0003.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.clin_1001.label }}
{{ f.clin_1001(placeholder="30,000") }}
{% for e in f.clin_1001.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.clin_1003.label }}
{{ f.clin_1003(placeholder="7,000") }}
{% for e in f.clin_1003.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.clin_2001.label }}
{{ f.clin_2001(placeholder="30,000") }}
{% for e in f.clin_2001.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{{ f.clin_2003.label }}
{{ f.clin_2003(placeholder="7,000") }}
{% for e in f.clin_2003.errors %}
<div class="usa-input-error-message">
{{ e }}
</div>
{% end %}
{% end %}
{% block next %}
<input type='submit' class='usa-button usa-button-primary' value='Save & Continue' />
{% end %}
</form>
</div>
</div>
</div>
{% end %}

View File

@ -0,0 +1,18 @@
{% extends "../base.html.to" %}
{% block content %}
<div class="col">
<div class="panel">
<div class="panel__content">
<div class="panel__heading">
<h2 id="financial-verification">Submitted</h2>
</div>
</div>
</div>
</div>
{% end %}

View File

@ -1,4 +1,4 @@
<div class="progress-menu progress-menu--five"> <div class="progress-menu progress-menu--four">
<ul> <ul>
{% for i,s in enumerate(screens) %} {% for i,s in enumerate(screens) %}
<li class="progress-menu__item"> <li class="progress-menu__item">

View File

@ -0,0 +1,84 @@
import re
import pytest
import tornado
import urllib
from tests.mocks import MOCK_REQUEST, MOCK_USER, MOCK_VALID_PE_ID
class TestPENumberInForm:
required_data = {
"pe_id": "123",
"task_order_id": "1234567899C0001",
"fname_co": "Contracting",
"lname_co": "Officer",
"email_co": "jane@mail.mil",
"office_co": "WHS",
"fname_cor": "Officer",
"lname_cor": "Representative",
"email_cor": "jane@mail.mil",
"office_cor": "WHS",
"funding_type": "RDTE",
"funding_type_other": "other",
"clin_0001": "50,000",
"clin_0003": "13,000",
"clin_1001": "30,000",
"clin_1003": "7,000",
"clin_2001": "30,000",
"clin_2003": "7,000",
}
def _set_monkeypatches(self, monkeypatch):
monkeypatch.setattr(
"atst.handlers.request_financial_verification.RequestFinancialVerification.get_current_user", lambda s: MOCK_USER
)
monkeypatch.setattr(
"atst.handlers.request_financial_verification.RequestFinancialVerification.check_xsrf_cookie", lambda s: True
)
monkeypatch.setattr("atst.forms.request.RequestForm.validate", lambda s: True)
@tornado.gen.coroutine
def submit_data(self, http_client, base_url, data):
response = yield http_client.fetch(
base_url + "/requests/verify/{}".format(MOCK_REQUEST["id"]),
method="POST",
headers={"Content-Type": "application/x-www-form-urlencoded"},
body=urllib.parse.urlencode(data),
follow_redirects=False,
raise_error=False,
)
return response
@pytest.mark.gen_test
def test_submit_request_form_with_invalid_pe_id(self, monkeypatch, http_client, base_url):
self._set_monkeypatches(monkeypatch)
response = yield self.submit_data(http_client, base_url, self.required_data)
assert "We couldn\'t find that PE number" in response.body.decode()
assert response.code == 200
assert "/requests/verify" in response.effective_url
@pytest.mark.gen_test
def test_submit_request_form_with_unchanged_pe_id(self, monkeypatch, http_client, base_url):
self._set_monkeypatches(monkeypatch)
data = dict(self.required_data)
data['pe_id'] = MOCK_REQUEST['body']['financial_verification']['pe_id']
response = yield self.submit_data(http_client, base_url, data)
assert response.code == 302
assert response.headers.get("Location") == "/requests/financial_verification_submitted"
@pytest.mark.gen_test
def test_submit_request_form_with_new_valid_pe_id(self, monkeypatch, http_client, base_url):
self._set_monkeypatches(monkeypatch)
data = dict(self.required_data)
data['pe_id'] = MOCK_VALID_PE_ID
response = yield self.submit_data(http_client, base_url, data)
assert response.code == 302
assert response.headers.get("Location") == "/requests/financial_verification_submitted"

View File

@ -2,16 +2,9 @@ import re
import pytest import pytest
import tornado import tornado
import urllib import urllib
from tests.mocks import MOCK_REQUEST, MOCK_VALID_PE_ID from tests.mocks import MOCK_USER
ERROR_CLASS = "usa-input-error-message" ERROR_CLASS = "usa-input-error-message"
MOCK_USER = {
"id": "9cb348f0-8102-4962-88c4-dac8180c904c",
"email": "fake.user@mail.com",
"first_name": "Fake",
"last_name": "User",
}
@pytest.mark.gen_test @pytest.mark.gen_test
def test_submit_invalid_request_form(monkeypatch, http_client, base_url): def test_submit_invalid_request_form(monkeypatch, http_client, base_url):
@ -50,82 +43,3 @@ def test_submit_valid_request_form(monkeypatch, http_client, base_url):
body="meaning=42", body="meaning=42",
) )
assert "/requests/new/2" in response.effective_url assert "/requests/new/2" in response.effective_url
class TestPENumberInForm:
required_data = {
"pe_id": "123",
"task_order_id": "1234567899C0001",
"fname_co": "Contracting",
"lname_co": "Officer",
"email_co": "jane@mail.mil",
"office_co": "WHS",
"fname_cor": "Officer",
"lname_cor": "Representative",
"email_cor": "jane@mail.mil",
"office_cor": "WHS",
"funding_type": "RDTE",
"funding_type_other": "other",
"clin_0001": "50,000",
"clin_0003": "13,000",
"clin_1001": "30,000",
"clin_1003": "7,000",
"clin_2001": "30,000",
"clin_2003": "7,000",
}
def _set_monkeypatches(self, monkeypatch):
monkeypatch.setattr(
"atst.handlers.request_new.RequestNew.get_current_user", lambda s: MOCK_USER
)
monkeypatch.setattr(
"atst.handlers.request_new.RequestNew.check_xsrf_cookie", lambda s: True
)
monkeypatch.setattr("atst.forms.request.RequestForm.validate", lambda s: True)
@tornado.gen.coroutine
def submit_data(self, http_client, base_url, data):
response = yield http_client.fetch(
base_url + "/requests/new/5/{}".format(MOCK_REQUEST["id"]),
method="POST",
headers={"Content-Type": "application/x-www-form-urlencoded"},
body=urllib.parse.urlencode(data),
follow_redirects=False,
raise_error=False,
)
return response
@pytest.mark.gen_test
def test_submit_request_form_with_invalid_pe_id(self, monkeypatch, http_client, base_url):
self._set_monkeypatches(monkeypatch)
response = yield self.submit_data(http_client, base_url, self.required_data)
assert "We couldn\'t find that PE number" in response.body.decode()
assert response.code == 200
assert "/requests/new/5" in response.effective_url
@pytest.mark.gen_test
def test_submit_request_form_with_unchanged_pe_id(self, monkeypatch, http_client, base_url):
self._set_monkeypatches(monkeypatch)
data = dict(self.required_data)
data['pe_id'] = MOCK_REQUEST['body']['financial_verification']['pe_id']
response = yield self.submit_data(http_client, base_url, data)
assert response.code == 302
assert response.headers.get("Location") == "/requests"
@pytest.mark.gen_test
def test_submit_request_form_with_new_valid_pe_id(self, monkeypatch, http_client, base_url):
self._set_monkeypatches(monkeypatch)
data = dict(self.required_data)
data['pe_id'] = MOCK_VALID_PE_ID
response = yield self.submit_data(http_client, base_url, data)
assert response.code == 302
assert response.headers.get("Location") == "/requests"

View File

@ -4,6 +4,14 @@ from tornado.httpclient import HTTPRequest, HTTPResponse
from atst.api_client import ApiClient from atst.api_client import ApiClient
MOCK_USER = {
"id": "9cb348f0-8102-4962-88c4-dac8180c904c",
"email": "fake.user@mail.com",
"first_name": "Fake",
"last_name": "User",
}
class MockApiClient(ApiClient): class MockApiClient(ApiClient):
def __init__(self, service): def __init__(self, service):