From 54192c6c5d9428bbf6b0ab07e70a19c0c3da616c Mon Sep 17 00:00:00 2001 From: richard-dds Date: Mon, 15 Oct 2018 16:57:43 -0400 Subject: [PATCH] Implement SaveFinancialVerificationDraft --- atst/forms/financial.py | 7 +- .../routes/requests/financial_verification.py | 93 ++++++++++++++ tests/routes/test_financial_verification.py | 120 +++++++++++++----- 3 files changed, 187 insertions(+), 33 deletions(-) diff --git a/atst/forms/financial.py b/atst/forms/financial.py index d9d953de..61d334cd 100644 --- a/atst/forms/financial.py +++ b/atst/forms/financial.py @@ -2,7 +2,7 @@ import re import pendulum from wtforms.fields.html5 import DateField, EmailField from wtforms.fields import StringField, FileField -from wtforms.validators import InputRequired, Email, Regexp +from wtforms.validators import InputRequired, Email, Regexp, Optional from flask_wtf.file import FileAllowed from .fields import NewlineListField, SelectField, NumberStringField @@ -29,6 +29,11 @@ class BaseFinancialForm(ValidatedForm): """ self.uii_ids.process_data(self.uii_ids.data) + def validate_draft(self): + for field in self: + field.validators.insert(0, Optional()) + return self.validate() + @property def is_missing_task_order_number(self): return False diff --git a/atst/routes/requests/financial_verification.py b/atst/routes/requests/financial_verification.py index d2c6242e..47b7be2a 100644 --- a/atst/routes/requests/financial_verification.py +++ b/atst/routes/requests/financial_verification.py @@ -133,6 +133,73 @@ class UpdateFinancialVerification(object): return {"state": "pending", "request": updated_request} +class SaveFinancialVerificationDraft(object): + def __init__( + self, + pe_validator, + task_order_validator, + user, + request, + fv_data, + is_extended=False, + ): + self.pe_validator = pe_validator + self.task_order_validator = task_order_validator + self.user = user + self.request = request + self.fv_data = fv_data + self.is_extended = is_extended + + def _get_form(self): + data = self.fv_data + + existing_fv_data = self.request.body.get("financial_verification", {}) + data = {**data, **existing_fv_data} + + if self.request.task_order: + task_order_dict = self.request.task_order.to_dictionary() + task_order_dict.update({ + "task_order_number": self.request.task_order.number, + "funding_type": self.request.task_order.funding_type.value + }) + data = {**data, **task_order_dict} + + mdict = ImmutableMultiDict(data) + if self.is_extended: + return ExtendedFinancialForm(formdata=mdict) + else: + return FinancialForm(formdata=mdict) + + def execute(self): + form = self._get_form() + valid = True + + if not form.validate_draft(): + valid = False + + if valid and form.pe_id.data and not self.pe_validator.validate(self.request, form.pe_id.data): + suggestion = self.pe_validator.suggest_pe_id(form.pe_id.data) + error_str = ( + "We couldn't find that PE number. {}" + "If you have double checked it you can submit anyway. " + "Your request will need to go through a manual review." + ).format('Did you mean "{}"? '.format(suggestion) if suggestion else "") + form.pe_id.errors += (error_str,) + valid = False + + if valid and form.task_order_number.data and not self.task_order_validator.validate(form.task_order_number.data): + form.task_order_number.errors += ("Task Order number not found",) + valid = False + + if not valid: + form.reset() + raise FormValidationError(form) + else: + updated_request = Requests.update_financial_verification(self.request.id, form.data) + return {"request": updated_request} + + + @requests_bp.route("/requests/verify/", methods=["GET"]) def financial_verification(request_id): request = Requests.get(g.current_user, request_id) @@ -181,3 +248,29 @@ def update_financial_verification(request_id): ) elif response_context["state"] == "pending": return redirect(url_for("requests.requests_index", modal="pendingCCPOApproval")) + + +@requests_bp.route("/requests/verify//draft", methods=["POST"]) +def save_financial_verification_draft(request_id): + request = Requests.get(g.current_user, request_id) + fv_data = http_request.form + is_extended = http_request.args.get("extended") + + try: + response_context = SaveFinancialVerificationDraft( + PENumberValidator(), + TaskOrderNumberValidator(), + g.current_user, + request, + fv_data, + is_extended=is_extended, + ).execute() + except FormValidationError as e: + return render_template( + "requests/financial_verification.html", + jedi_request=request, + f=e.form, + extended=is_extended, + ) + + return redirect(url_for("requests.requests_index")) diff --git a/tests/routes/test_financial_verification.py b/tests/routes/test_financial_verification.py index 482b4eb8..a800869c 100644 --- a/tests/routes/test_financial_verification.py +++ b/tests/routes/test_financial_verification.py @@ -1,7 +1,8 @@ import pytest +from unittest.mock import MagicMock from atst.eda_client import MockEDAClient -from atst.routes.requests.financial_verification import UpdateFinancialVerification +from atst.routes.requests.financial_verification import UpdateFinancialVerification, SaveFinancialVerificationDraft from tests.mocks import MOCK_REQUEST, MOCK_USER, MOCK_VALID_PE_ID from tests.factories import ( @@ -17,23 +18,31 @@ from atst.domain.requests.financial_verification import ( TaskOrderNumberValidator, ) -required_data = { - "pe_id": "123", - "task_order_number": MockEDAClient.MOCK_CONTRACT_NUMBER, - "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", - "uii_ids": "1234", - "treasury_code": "00123456", - "ba_code": "02A", -} +@pytest.fixture +def fv_data(): + return { + "pe_id": "123", + "task_order_number": MockEDAClient.MOCK_CONTRACT_NUMBER, + "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", + "uii_ids": "1234", + "treasury_code": "00123456", + "ba_code": "02A", + } +TrueValidator = MagicMock() +TrueValidator.validate = MagicMock(return_value=True) + +FalseValidator = MagicMock() +FalseValidator.validate = MagicMock(return_value=False) + class MockPEValidator(object): def validate(self, request, field): return True @@ -44,14 +53,14 @@ class MockTaskOrderValidator(object): return True -def test_update(): +def test_update(fv_data): request = RequestFactory.create() user = UserFactory.create() - data = {**required_data, "pe_id": MOCK_VALID_PE_ID} + data = {**fv_data, "pe_id": MOCK_VALID_PE_ID} response_context = UpdateFinancialVerification( - MockPEValidator(), - MockTaskOrderValidator(), + TrueValidator, + TrueValidator, user, request, data, @@ -61,13 +70,13 @@ def test_update(): assert response_context.get("workspace") -def test_re_enter_pe_number(): +def test_re_enter_pe_number(fv_data): request = RequestFactory.create() user = UserFactory.create() - data = {**required_data, "pe_id": "0101228M"} + data = {**fv_data, "pe_id": "0101228M"} update_fv = UpdateFinancialVerification( PENumberValidator(), - MockTaskOrderValidator(), + TrueValidator, user, request, data, @@ -81,12 +90,12 @@ def test_re_enter_pe_number(): assert response_context.get("status", "submitted") -def test_invalid_task_order_number(): +def test_invalid_task_order_number(fv_data): request = RequestFactory.create() user = UserFactory.create() - data = {**required_data, "task_order_number": "DCA10096D0051"} + data = {**fv_data, "task_order_number": "DCA10096D0051"} update_fv = UpdateFinancialVerification( - MockPEValidator(), + TrueValidator, TaskOrderNumberValidator(), user, request, @@ -98,12 +107,12 @@ def test_invalid_task_order_number(): update_fv.execute() -def test_extended_fv_data(extended_financial_verification_data): +def test_extended_fv_data(fv_data, extended_financial_verification_data): request = RequestFactory.create() user = UserFactory.create() - data = {**required_data, **extended_financial_verification_data} + data = {**fv_data, **extended_financial_verification_data} update_fv = UpdateFinancialVerification( - MockPEValidator(), + TrueValidator, TaskOrderNumberValidator(), user, request, @@ -114,17 +123,64 @@ def test_extended_fv_data(extended_financial_verification_data): assert update_fv.execute() -def test_missing_extended_fv_data(): +def test_missing_extended_fv_data(fv_data): request = RequestFactory.create() user = UserFactory.create() update_fv = UpdateFinancialVerification( - MockPEValidator(), + TrueValidator, TaskOrderNumberValidator(), user, request, - required_data, + fv_data, is_extended=True, ) with pytest.raises(FormValidationError): update_fv.execute() + + +def test_save_empty_draft(): + request = RequestFactory.create() + user = UserFactory.create() + save_draft = SaveFinancialVerificationDraft( + TrueValidator, + TrueValidator, + user, + request, + {}, + is_extended=False, + ) + + assert save_draft.execute() + + +def test_save_draft_with_invalid_task_order(fv_data): + request = RequestFactory.create() + user = UserFactory.create() + save_draft = SaveFinancialVerificationDraft( + TrueValidator, + FalseValidator, + user, + request, + fv_data, + is_extended=False, + ) + + with pytest.raises(FormValidationError): + assert save_draft.execute() + + +def test_save_draft_with_invalid_pe_number(fv_data): + request = RequestFactory.create() + user = UserFactory.create() + save_draft = SaveFinancialVerificationDraft( + FalseValidator, + TrueValidator, + user, + request, + fv_data, + is_extended=False, + ) + + with pytest.raises(FormValidationError): + assert save_draft.execute()