diff --git a/alembic/versions/edf509c974f6_extended_fv.py b/alembic/versions/edf509c974f6_extended_fv.py new file mode 100644 index 00000000..8877f659 --- /dev/null +++ b/alembic/versions/edf509c974f6_extended_fv.py @@ -0,0 +1,28 @@ +"""extended fv + +Revision ID: edf509c974f6 +Revises: 9c24c609878a +Create Date: 2018-10-19 14:06:45.396974 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'edf509c974f6' +down_revision = '9c24c609878a' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('request_revisions', sa.Column('extended', sa.Boolean(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('request_revisions', 'extended') + # ### end Alembic commands ### diff --git a/atst/domain/requests/requests.py b/atst/domain/requests/requests.py index be38880e..c5fc799c 100644 --- a/atst/domain/requests/requests.py +++ b/atst/domain/requests/requests.py @@ -161,7 +161,7 @@ class Requests(object): return Requests.status_count(RequestStatus.APPROVED) @classmethod - def update_financial_verification(cls, request_id, financial_data, task_order=None): + def update_financial_verification(cls, request_id, financial_data, extended=False, task_order=None): request = RequestsQuery.get_with_lock(request_id) # request_data = financial_data.copy() @@ -201,6 +201,7 @@ class Requests(object): ], financial_data, ) + delta = {**delta, "extended": extended} if task_order: request.task_order = task_order diff --git a/atst/forms/financial.py b/atst/forms/financial.py index 55abaf78..b1c4c6b9 100644 --- a/atst/forms/financial.py +++ b/atst/forms/financial.py @@ -182,4 +182,5 @@ class ExtendedFinancialForm(BaseFinancialForm): FileAllowed(["pdf"], "Only PDF documents can be uploaded."), InputRequired(), ], + render_kw={"required": False} ) diff --git a/atst/models/mixins/auditable.py b/atst/models/mixins/auditable.py index 2a47d1c9..1b6ae3d2 100644 --- a/atst/models/mixins/auditable.py +++ b/atst/models/mixins/auditable.py @@ -1,26 +1,14 @@ from sqlalchemy import event from flask import g -import re from atst.models.audit_event import AuditEvent +from atst.utils import camel_to_snake, getattr_path ACTION_CREATE = "create" ACTION_UPDATE = "update" ACTION_DELETE = "delete" -def getattr_path(obj, path, default=None): - _obj = obj - for item in path.split("."): - _obj = getattr(_obj, item, default) - return _obj - - -def camel_to_snake(camel_cased): - s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", camel_cased) - return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() - - class AuditableMixin(object): @staticmethod def create_audit_event(connection, resource, action): diff --git a/atst/models/request.py b/atst/models/request.py index 1e1abcac..056fbbfe 100644 --- a/atst/models/request.py +++ b/atst/models/request.py @@ -100,6 +100,7 @@ class Request(Base, mixins.TimestampsMixin, mixins.AuditableMixin): "uii_ids", "treasury_code", "ba_code", + "extended" ] @property @@ -135,7 +136,7 @@ class Request(Base, mixins.TimestampsMixin, mixins.AuditableMixin): @property def financial_verification(self): - return self.body.get("financial_verification") + return self.body.get("financial_verification", {}) @property def is_financially_verified(self): diff --git a/atst/models/request_revision.py b/atst/models/request_revision.py index 1d2da465..8f39d108 100644 --- a/atst/models/request_revision.py +++ b/atst/models/request_revision.py @@ -77,6 +77,7 @@ class RequestRevision(Base, mixins.TimestampsMixin, mixins.AuditableMixin): uii_ids = Column(ARRAY(String)) treasury_code = Column(String) ba_code = Column(String) + extended = Column(Boolean, default=False) def __repr__(self): return "".format( diff --git a/atst/routes/requests/financial_verification.py b/atst/routes/requests/financial_verification.py index dbafce03..889daf35 100644 --- a/atst/routes/requests/financial_verification.py +++ b/atst/routes/requests/financial_verification.py @@ -13,6 +13,11 @@ from atst.domain.requests.financial_verification import ( ) from atst.models.attachment import Attachment from atst.domain.task_orders import TaskOrders +from atst.utils import getattr_path + + +def fv_extended(_http_request): + return bool(_http_request.args.get("extended")) class FinancialVerificationBase(object): @@ -24,7 +29,7 @@ class FinancialVerificationBase(object): task_order_dict.update( { "task_order_number": request.task_order.number, - "funding_type": request.task_order.funding_type.value, + "funding_type": getattr_path(request, "task_order.funding_type.value") } ) existing_fv_data = {**existing_fv_data, **task_order_dict} @@ -51,7 +56,10 @@ class FinancialVerificationBase(object): form.task_order.data, "task_order", self.request.id ) elif isinstance(form.task_order.data, str): - attachment = Attachment.get_for_resource("task_order", self.request.id) + try: + attachment = Attachment.get_for_resource("task_order", self.request.id) + except NotFoundError: + pass if attachment: form.task_order.data = attachment.id @@ -139,7 +147,7 @@ class UpdateFinancialVerification(FinancialVerificationBase): if should_update: task_order = self._try_create_task_order(form, attachment) updated_request = Requests.update_financial_verification( - self.request.id, form.data, task_order=task_order + self.request.id, form.data, extended=self.is_extended, task_order=task_order ) if should_submit: return Requests.submit_financial_verification(updated_request) @@ -186,7 +194,7 @@ class SaveFinancialVerificationDraft(FinancialVerificationBase): attachment = self._process_attachment(self.is_extended, form) task_order = self._try_create_task_order(form, attachment) updated_request = Requests.update_financial_verification( - self.request.id, form.data, task_order=task_order + self.request.id, form.data, extended=self.is_extended, task_order=task_order ) if valid: @@ -198,7 +206,7 @@ class SaveFinancialVerificationDraft(FinancialVerificationBase): @requests_bp.route("/requests/verify/", methods=["GET"]) def financial_verification(request_id): request = Requests.get(g.current_user, request_id) - is_extended = http_request.args.get("extended") + is_extended = fv_extended(http_request) or request.financial_verification.get("extended", False) form = GetFinancialVerificationForm( g.current_user, request, is_extended=is_extended @@ -217,7 +225,7 @@ def financial_verification(request_id): def update_financial_verification(request_id): request = Requests.get(g.current_user, request_id) fv_data = {**http_request.form, **http_request.files} - is_extended = http_request.args.get("extended") + is_extended = fv_extended(http_request) try: updated_request = UpdateFinancialVerification( @@ -251,7 +259,7 @@ def update_financial_verification(request_id): def save_financial_verification_draft(request_id): request = Requests.get(g.current_user, request_id) fv_data = {**http_request.form, **http_request.files} - is_extended = http_request.args.get("extended") + is_extended = fv_extended(http_request) try: SaveFinancialVerificationDraft( diff --git a/atst/utils/__init__.py b/atst/utils/__init__.py index 3923a341..ff48d12b 100644 --- a/atst/utils/__init__.py +++ b/atst/utils/__init__.py @@ -1,3 +1,5 @@ +import re + def first_or_none(predicate, lst): return next((x for x in lst if predicate(x)), None) @@ -18,3 +20,15 @@ def deep_merge(source, destination: dict): return b return _deep_merge(source, dict(destination)) + + +def getattr_path(obj, path, default=None): + _obj = obj + for item in path.split("."): + _obj = getattr(_obj, item, default) + return _obj + + +def camel_to_snake(camel_cased): + s1 = re.sub("(.)([A-Z][a-z]+)", r"\1_\2", camel_cased) + return re.sub("([a-z0-9])([A-Z])", r"\1_\2", s1).lower() diff --git a/tests/domain/test_requests.py b/tests/domain/test_requests.py index fad94973..90e3655d 100644 --- a/tests/domain/test_requests.py +++ b/tests/domain/test_requests.py @@ -143,39 +143,6 @@ request_financial_data = { } -def test_update_financial_verification_without_task_order( - extended_financial_verification_data -): - request = RequestFactory.create() - financial_data = {**request_financial_data, **extended_financial_verification_data} - Requests.update_financial_verification(request.id, financial_data) - assert request.task_order - assert request.task_order.clin_0001 == int( - extended_financial_verification_data["clin_0001"] - ) - assert request.task_order.source == TaskOrderSource.MANUAL - assert request.task_order.pdf - - -def test_update_financial_verification_with_task_order(): - task_order = TaskOrderFactory.create(source=TaskOrderSource.EDA) - financial_data = {**request_financial_data, "task_order_number": task_order.number} - request = RequestFactory.create() - Requests.update_financial_verification(request.id, financial_data) - assert request.task_order == task_order - - -def test_update_financial_verification_with_invalid_task_order(): - request = RequestFactory.create() - Requests.update_financial_verification(request.id, request_financial_data) - assert not request.task_order - assert "task_order_number" in request.body.get("financial_verification") - assert ( - request.body["financial_verification"]["task_order_number"] - == request_financial_data["task_order_number"] - ) - - def test_set_status_sets_revision(): request = RequestFactory.create() Requests.set_status(request, RequestStatus.APPROVED)