From a0a8c8e1f190449016412aabff2c2580228c93af Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 4 Jun 2019 11:00:48 -0400 Subject: [PATCH 1/5] Use task order in TOSidebar macro --- templates/components/to_sidebar.html | 4 ++-- templates/portfolios/task_orders/review.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/templates/components/to_sidebar.html b/templates/components/to_sidebar.html index 1b495b7b..c7109a9f 100644 --- a/templates/components/to_sidebar.html +++ b/templates/components/to_sidebar.html @@ -1,4 +1,4 @@ -{% macro TOSidebar() -%} +{% macro TOSidebar(task_order) -%}
Total obligated funds
@@ -8,7 +8,7 @@
Total contract amount
-
$800,000
+
{{ task_order.budget | dollars }}
This is the value of all funds obligated for this contract, including -- but not limited to -- funds obligated for the cloud.
diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index 58d2767a..cd29ebb3 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -83,7 +83,7 @@
{{ Icon('ok',classes="icon-validation") }}document
- {{ TOSidebar() }} + {{ TOSidebar(task_order=task_order) }} From b365c4bde454c9722609e695d4b2e298e81bf66c Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 4 Jun 2019 11:05:19 -0400 Subject: [PATCH 2/5] Change name of TOSidebar to TotalsBox --- styles/sections/_task_order.scss | 2 +- templates/components/{to_sidebar.html => totals_box.html} | 4 ++-- templates/portfolios/task_orders/review.html | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) rename templates/components/{to_sidebar.html => totals_box.html} (88%) diff --git a/styles/sections/_task_order.scss b/styles/sections/_task_order.scss index 0b887352..bdcaf40e 100644 --- a/styles/sections/_task_order.scss +++ b/styles/sections/_task_order.scss @@ -93,7 +93,7 @@ margin-top: $gap * 2; } - .to-sidebar { + .totals-box { padding: $gap * 4; padding-top: $gap * 2; flex-grow: unset; diff --git a/templates/components/to_sidebar.html b/templates/components/totals_box.html similarity index 88% rename from templates/components/to_sidebar.html rename to templates/components/totals_box.html index c7109a9f..08d47d63 100644 --- a/templates/components/to_sidebar.html +++ b/templates/components/totals_box.html @@ -1,6 +1,6 @@ -{% macro TOSidebar(task_order) -%} +{% macro TotalsBox(task_order) -%} -
+
Total obligated funds
$500,000
This is the funding allocated to cloud services. It may be 100% or a portion of the total task order budget.
diff --git a/templates/portfolios/task_orders/review.html b/templates/portfolios/task_orders/review.html index cd29ebb3..21d32dd6 100644 --- a/templates/portfolios/task_orders/review.html +++ b/templates/portfolios/task_orders/review.html @@ -1,7 +1,7 @@ {% extends 'portfolios/base.html' %} {% from "components/icon.html" import Icon %} -{% from "components/to_sidebar.html" import TOSidebar %} +{% from "components/totals_box.html" import TotalsBox %} {% block content %} @@ -83,7 +83,7 @@
{{ Icon('ok',classes="icon-validation") }}document
- {{ TOSidebar(task_order=task_order) }} + {{ TotalsBox(task_order=task_order) }}
From 08c1a967ba0e8ad98dad92d4d9536442432bcf33 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 4 Jun 2019 16:05:31 -0400 Subject: [PATCH 3/5] Add `jedi clin type` column to clin model --- ...29cca1_add_jedi_clin_type_to_clin_table.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 alembic/versions/c19d6129cca1_add_jedi_clin_type_to_clin_table.py diff --git a/alembic/versions/c19d6129cca1_add_jedi_clin_type_to_clin_table.py b/alembic/versions/c19d6129cca1_add_jedi_clin_type_to_clin_table.py new file mode 100644 index 00000000..4b916ea5 --- /dev/null +++ b/alembic/versions/c19d6129cca1_add_jedi_clin_type_to_clin_table.py @@ -0,0 +1,28 @@ +"""add jedi clin type to clin table + +Revision ID: c19d6129cca1 +Revises: 988f8b23fbf6 +Create Date: 2019-06-04 11:30:25.283028 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = 'c19d6129cca1' +down_revision = '988f8b23fbf6' +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('clins', sa.Column('jedi_clin_type', sa.Enum('JEDI_CLIN_1', 'JEDI_CLIN_2', 'JEDI_CLIN_3', 'JEDI_CLIN_4', name='jediclintype', native_enum=False), nullable=False)) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('clins', 'jedi_clin_type') + # ### end Alembic commands ### From e4cbe892fc57a6a9b5c8b0c08bb5279ebdb034af Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 4 Jun 2019 16:15:24 -0400 Subject: [PATCH 4/5] Use clin values to render budget information - creates a relationship `clins` on task order model - two properties on task order model to calculate budget amounts --- atst/models/task_order.py | 26 +++++++++++++++++++++++--- templates/components/totals_box.html | 4 ++-- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/atst/models/task_order.py b/atst/models/task_order.py index 525c71ed..049b7d85 100644 --- a/atst/models/task_order.py +++ b/atst/models/task_order.py @@ -6,7 +6,8 @@ from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.orm import relationship from werkzeug.datastructures import FileStorage -from atst.models import Attachment, Base, types, mixins +from atst.models import Attachment, Base, mixins, types +from atst.models.clin import JEDICLINType class Status(Enum): @@ -33,6 +34,8 @@ class TaskOrder(Base, mixins.TimestampsMixin): signer_dod_id = Column(String) signed_at = Column(DateTime) + clins = relationship("CLIN") + @hybrid_property def pdf(self): return self._pdf @@ -83,9 +86,26 @@ class TaskOrder(Base, mixins.TimestampsMixin): return (self.end_date - date.today()).days @property + def total_obligated_funds(self): + total = 0 + for clin in self.clins: + if clin.jedi_clin_type in [ + JEDICLINType.JEDI_CLIN_1, + JEDICLINType.JEDI_CLIN_3, + ]: + total += clin.obligated_amount + return total + + @property + def total_contract_amount(self): + total = 0 + for clin in self.clins: + total += clin.obligated_amount + return total + + @property + # TODO delete when we delete task_order_review flow def budget(self): - # TODO: fix task order -- reimplement using CLINs - # Faked for display purposes return 100000 @property diff --git a/templates/components/totals_box.html b/templates/components/totals_box.html index 08d47d63..18afeedf 100644 --- a/templates/components/totals_box.html +++ b/templates/components/totals_box.html @@ -2,13 +2,13 @@
Total obligated funds
-
$500,000
+
{{ task_order.total_obligated_funds | dollars }}
This is the funding allocated to cloud services. It may be 100% or a portion of the total task order budget.

Total contract amount
-
{{ task_order.budget | dollars }}
+
{{ task_order.total_contract_amount | dollars }}
This is the value of all funds obligated for this contract, including -- but not limited to -- funds obligated for the cloud.
From f462c3bd5e6c03c1ad29efa039fb780932e6a3b7 Mon Sep 17 00:00:00 2001 From: Montana Date: Wed, 5 Jun 2019 09:40:33 -0400 Subject: [PATCH 5/5] Add unit tests --- tests/factories.py | 12 +++++++++++ tests/models/test_task_order.py | 37 +++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/tests/factories.py b/tests/factories.py index 341887c0..e91ec26b 100644 --- a/tests/factories.py +++ b/tests/factories.py @@ -269,6 +269,18 @@ class TaskOrderFactory(Base): number = factory.LazyFunction(random_task_order_number) +class CLINFactory(Base): + class Meta: + model = CLIN + + task_order = factory.SubFactory(TaskOrderFactory) + number = factory.LazyFunction(random_task_order_number) + start_date = datetime.date.today() + end_date = factory.LazyFunction(random_future_date) + obligated_amount = random.randint(100, 999999) + jedi_clin_type = random.choice([e.value for e in clin.JEDICLINType]) + + class NotificationRecipientFactory(Base): class Meta: model = NotificationRecipient diff --git a/tests/models/test_task_order.py b/tests/models/test_task_order.py index 3a25781a..d6ae9e1c 100644 --- a/tests/models/test_task_order.py +++ b/tests/models/test_task_order.py @@ -1,10 +1,16 @@ from werkzeug.datastructures import FileStorage import pytest, datetime -from atst.models.attachment import Attachment +from atst.models import * +from atst.models.clin import JEDICLINType from atst.models.task_order import TaskOrder, Status -from tests.factories import random_future_date, random_past_date +from tests.factories import ( + CLINFactory, + random_future_date, + random_past_date, + TaskOrderFactory, +) from tests.mocks import PDF_FILENAME @@ -30,6 +36,33 @@ class TestTaskOrderStatus: assert to.status == Status.EXPIRED +class TestBudget: + def test_total_contract_amount(self): + to = TaskOrder() + assert to.total_contract_amount == 0 + + clin1 = CLINFactory(task_order=to, jedi_clin_type=JEDICLINType.JEDI_CLIN_1) + clin2 = CLINFactory(task_order=to, jedi_clin_type=JEDICLINType.JEDI_CLIN_2) + clin3 = CLINFactory(task_order=to, jedi_clin_type=JEDICLINType.JEDI_CLIN_3) + + assert ( + to.total_contract_amount + == clin1.obligated_amount + clin2.obligated_amount + clin3.obligated_amount + ) + + def test_total_obligated_funds(self): + to = TaskOrder() + clin4 = CLINFactory(task_order=to, jedi_clin_type=JEDICLINType.JEDI_CLIN_4) + assert to.total_obligated_funds == 0 + + clin1 = CLINFactory(task_order=to, jedi_clin_type=JEDICLINType.JEDI_CLIN_1) + clin2 = CLINFactory(task_order=to, jedi_clin_type=JEDICLINType.JEDI_CLIN_2) + clin3 = CLINFactory(task_order=to, jedi_clin_type=JEDICLINType.JEDI_CLIN_3) + assert ( + to.total_obligated_funds == clin1.obligated_amount + clin3.obligated_amount + ) + + class TestPDF: def test_setting_pdf_with_attachment(self): to = TaskOrder()