diff --git a/atst/forms/financial.py b/atst/forms/financial.py index 05390b8e..cfc86a20 100644 --- a/atst/forms/financial.py +++ b/atst/forms/financial.py @@ -31,24 +31,7 @@ def coerce_choice(val): return val.value -class DraftValidateMixin(object): - def validate_draft(self): - """ - Make all fields optional before validation, and then return them to - their previous state. - """ - for field in self: - field.validators.insert(0, Optional()) - - valid = self.validate() - - for field in self: - field.validators.pop(0) - - return valid - - -class TaskOrderForm(ValidatedForm, DraftValidateMixin): +class TaskOrderForm(ValidatedForm): def do_validate_number(self): for field in self: if field.name != "task_order-number": @@ -144,7 +127,7 @@ class TaskOrderForm(ValidatedForm, DraftValidateMixin): ) -class RequestFinancialVerificationForm(ValidatedForm, DraftValidateMixin): +class RequestFinancialVerificationForm(ValidatedForm): uii_ids = NewlineListField( "Unique Item Identifier (UII)s related to your application(s) if you already have them.", description="If you have more than one UII, place each one on a new line.", @@ -224,9 +207,6 @@ class FinancialVerificationForm(ValidatedForm): task_order_valid = self.task_order.do_validate_number() return request_valid and task_order_valid - def validate_draft(self): - return self.task_order.validate_draft() and self.request.validate_draft() - def reset(self): self.request.reset() diff --git a/atst/models/request.py b/atst/models/request.py index 101c1969..2a4cef41 100644 --- a/atst/models/request.py +++ b/atst/models/request.py @@ -238,6 +238,13 @@ class Request(Base, mixins.TimestampsMixin, mixins.AuditableMixin): else None ) + @property + def last_finver_draft_saved_at(self): + if self.latest_revision.any_finver_fields_saved: + return self.latest_revision.time_updated + else: + return None + def __repr__(self): return "".format( self.status_displayname, diff --git a/atst/models/request_revision.py b/atst/models/request_revision.py index 89c4987a..17ba19c5 100644 --- a/atst/models/request_revision.py +++ b/atst/models/request_revision.py @@ -83,3 +83,24 @@ class RequestRevision(Base, mixins.TimestampsMixin, mixins.AuditableMixin): return "".format( self.request_id, self.id ) + + @property + def any_finver_fields_saved(self): + return any( + getattr(self, n, None) + for n in [ + "pe_id", + "task_order_number", + "fname_co", + "lname_co", + "email_co", + "office_co", + "fname_cor", + "lname_cor", + "email_cor", + "office_cor", + "uii_ids", + "treasury_code", + "ba_code", + ] + ) diff --git a/atst/routes/requests/financial_verification.py b/atst/routes/requests/financial_verification.py index 47235db5..320db9c6 100644 --- a/atst/routes/requests/financial_verification.py +++ b/atst/routes/requests/financial_verification.py @@ -169,35 +169,20 @@ class SaveFinancialVerificationDraft(FinancialVerificationBase): def execute(self): form = self._get_form(self.request, self.is_extended, self.fv_data) - valid = True - - if not form.validate_draft(): - self._raise(form) - - if form.pe_id.data and not self.pe_validator.validate(self.request, form.pe_id): - valid = False - - if form.task_order.number.data and not self.task_order_validator.validate( - form.task_order.number - ): - valid = False - attachment = self._process_attachment(self.is_extended, form) task_order = self._try_create_task_order(form, attachment, self.is_extended) updated_request = Requests.update_financial_verification( self.request.id, form.request.data, task_order=task_order ) - if valid: - return updated_request - else: - self._raise(form) + return updated_request @requests_bp.route("/requests/verify/", methods=["GET"]) def financial_verification(request_id): request = Requests.get(g.current_user, request_id) is_extended = fv_extended(http_request) + saved_draft = http_request.args.get("saved_draft", False) should_be_extended = not is_extended and request.has_manual_task_order if should_be_extended: @@ -215,6 +200,7 @@ def financial_verification(request_id): jedi_request=request, review_comment=request.review_comment, extended=is_extended, + saved_draft=saved_draft, ) @@ -254,15 +240,16 @@ def update_financial_verification(request_id): @requests_bp.route("/requests/verify//draft", methods=["POST"]) def save_financial_verification_draft(request_id): - request = Requests.get(g.current_user, request_id) + user = g.current_user + request = Requests.get(user, request_id) fv_data = {**http_request.form, **http_request.files} is_extended = fv_extended(http_request) try: - SaveFinancialVerificationDraft( + updated_request = SaveFinancialVerificationDraft( PENumberValidator(), TaskOrderNumberValidator(), - g.current_user, + user, request, fv_data, is_extended=is_extended, @@ -275,4 +262,11 @@ def save_financial_verification_draft(request_id): extended=is_extended, ) - return redirect(url_for("requests.requests_index")) + return redirect( + url_for( + "requests.financial_verification", + request_id=updated_request.id, + is_extended=is_extended, + saved_draft=True, + ) + ) diff --git a/js/components/forms/financial.js b/js/components/forms/financial.js index 7c270081..c3adeaad 100644 --- a/js/components/forms/financial.js +++ b/js/components/forms/financial.js @@ -1,6 +1,7 @@ import FormMixin from '../../mixins/form' import optionsinput from '../options_input' import textinput from '../text_input' +import localdatetime from '../local_datetime' export default { name: 'financial', @@ -10,6 +11,7 @@ export default { components: { optionsinput, textinput, + localdatetime, }, props: { diff --git a/templates/requests/financial_verification.html b/templates/requests/financial_verification.html index 827ff9f5..c8d06002 100644 --- a/templates/requests/financial_verification.html +++ b/templates/requests/financial_verification.html @@ -9,6 +9,12 @@ {% include 'requests/review_menu.html' %} +{% if saved_draft %} + {% call Alert('Draft saved', level='success') %} + {% endcall %} +{% endif %} + + {% if jedi_request.is_pending_financial_verification and not f.errors and not extended %} {{ Alert('Pending Financial Verification', fragment="fragments/pending_financial_verification.html") }} {% endif %} @@ -19,7 +25,6 @@
- {% if extended %} {{ Alert('Manually enter Task Order information', message="Additional fields are displayed below, where you can manually enter financial information as documented in your Task Order.", @@ -184,6 +189,9 @@
+ {% if jedi_request.last_finver_draft_saved_at %} + Draft saved at + {% endif %}
{% endblock %} diff --git a/tests/models/test_requests.py b/tests/models/test_requests.py index 3d5bcf72..f2a82528 100644 --- a/tests/models/test_requests.py +++ b/tests/models/test_requests.py @@ -3,6 +3,7 @@ from tests.factories import ( UserFactory, RequestStatusEventFactory, RequestReviewFactory, + RequestRevisionFactory, ) from atst.domain.requests import Requests from atst.models.request_status_event import RequestStatus @@ -113,3 +114,9 @@ def test_review_comment(): ) assert not request.review_comment + + +def test_finver_last_saved_at(): + request = RequestFactory.create() + RequestRevisionFactory.create(fname_co="Amanda", request=request) + assert request.last_finver_draft_saved_at diff --git a/tests/routes/test_financial_verification.py b/tests/routes/test_financial_verification.py index 427b05a9..1578fe4a 100644 --- a/tests/routes/test_financial_verification.py +++ b/tests/routes/test_financial_verification.py @@ -17,7 +17,6 @@ from atst.domain.requests.financial_verification import ( PENumberValidator, TaskOrderNumberValidator, ) -from atst.utils import pick from atst.models.request_status_event import RequestStatus from atst.domain.requests.query import RequestsQuery @@ -193,39 +192,23 @@ def test_save_draft_with_ba_code(): assert save_draft.execute() -def test_save_draft_with_invalid_task_order(fv_data): +def test_save_draft_allows_invalid_data(): request = RequestFactory.create() user = UserFactory.create() - save_draft = SaveFinancialVerificationDraft( - TrueValidator, FalseValidator, user, request, fv_data, is_extended=False - ) + data = { + "task_order-number": MANUAL_TO_NUMBER, + "request-pe_id": "123", + "request-ba_code": "a", + } - 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() - - -def test_save_draft_re_enter_pe_number(fv_data): - request = RequestFactory.create() - user = UserFactory.create() - data = {**fv_data, "pe_id": "0101228M"} - save_fv = SaveFinancialVerificationDraft( - PENumberValidator(), TrueValidator, user, request, data, is_extended=False - ) - - with pytest.raises(FormValidationError): - save_fv.execute() - save_fv.execute() + assert SaveFinancialVerificationDraft( + PENumberValidator(), + TaskOrderNumberValidator(), + user, + request, + data, + is_extended=True, + ).execute() def test_save_draft_and_then_submit(): @@ -288,17 +271,6 @@ def test_update_ignores_empty_values(fv_data, e_fv_data): ).execute() -def test_simple_form_does_not_generate_task_order(fv_data): - request = RequestFactory.create() - user = UserFactory.create() - data = pick(["uii_ids"], fv_data) - updated_request = SaveFinancialVerificationDraft( - TrueValidator, TrueValidator, user, request, data, is_extended=False - ).execute() - - assert updated_request.task_order is None - - def test_can_save_draft_with_funding_type(fv_data, e_fv_data): request = RequestFactory.create() user = UserFactory.create() @@ -333,7 +305,7 @@ def test_save_fv_draft_route(client, user_session, fv_data): response = client.post( url_for("requests.save_financial_verification_draft", request_id=request.id), data=fv_data, - follow_redirects=False, + follow_redirects=True, ) assert response.status_code == 200