Merge pull request #1416 from dod-ccpo/reporting-integration

Bugfix: Accurately report Total Portfolio Value
This commit is contained in:
graham-dds 2020-02-11 14:53:16 -05:00 committed by GitHub
commit bad916dfb0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 115 additions and 4 deletions

View File

@ -100,6 +100,16 @@ class Portfolio(
(task_order.total_obligated_funds for task_order in self.active_task_orders)
)
@property
def upcoming_obligated_funds(self):
return sum(
(
task_order.total_obligated_funds
for task_order in self.task_orders
if task_order.is_upcoming
)
)
@property
def funding_duration(self):
"""

View File

@ -87,6 +87,10 @@ class TaskOrder(Base, mixins.TimestampsMixin):
def is_expired(self):
return self.status == Status.EXPIRED
@property
def is_upcoming(self):
return self.status == Status.UPCOMING
@property
def clins_are_completed(self):
return all([len(self.clins), (clin.is_completed for clin in self.clins)])

View File

@ -50,8 +50,10 @@ def reports(portfolio_id):
return render_template(
"portfolios/reports/index.html",
portfolio=portfolio,
# wrapped in str() because the sum of obligated funds returns a Decimal object
total_portfolio_value=str(portfolio.total_obligated_funds),
# wrapped in str() because this sum returns a Decimal object
total_portfolio_value=str(
portfolio.total_obligated_funds + portfolio.upcoming_obligated_funds
),
current_obligated_funds=current_obligated_funds,
expired_task_orders=Reports.expired_task_orders(portfolio),
retrieved=datetime.now(), # mocked datetime of reporting data retrival

View File

@ -195,7 +195,7 @@ def add_task_orders_to_portfolio(portfolio):
task_order=unsigned_to, start_date=(today - five_days), end_date=today
),
CLINFactory.build(
task_order=upcoming_to, start_date=future, end_date=(today + five_days)
task_order=upcoming_to, start_date=(today + five_days), end_date=future
),
CLINFactory.build(
task_order=expired_to, start_date=(today - five_days), end_date=yesterday

View File

@ -7,6 +7,51 @@ from tests.factories import (
random_past_date,
)
import datetime
import pendulum
from decimal import Decimal
import pytest
@pytest.fixture(scope="function")
def upcoming_task_order():
return dict(
signed_at=pendulum.today().subtract(days=3),
create_clins=[
dict(
start_date=pendulum.today().add(days=2),
end_date=pendulum.today().add(days=3),
obligated_amount=Decimal(700.0),
)
],
)
@pytest.fixture(scope="function")
def current_task_order():
return dict(
signed_at=pendulum.today().subtract(days=3),
create_clins=[
dict(
start_date=pendulum.today().subtract(days=1),
end_date=pendulum.today().add(days=1),
obligated_amount=Decimal(1000.0),
)
],
)
@pytest.fixture(scope="function")
def past_task_order():
return dict(
signed_at=pendulum.today().subtract(days=3),
create_clins=[
dict(
start_date=pendulum.today().subtract(days=3),
end_date=pendulum.today().subtract(days=2),
obligated_amount=Decimal(500.0),
)
],
)
def test_portfolio_applications_excludes_deleted():
@ -85,3 +130,53 @@ def test_active_task_orders(session):
portfolio=portfolio, signed_at=random_past_date(), clins=[CLINFactory.create()]
)
assert len(portfolio.active_task_orders) == 1
class TestCurrentObligatedFunds:
"""
Tests the current_obligated_funds property
"""
def test_no_task_orders(self):
portfolio = PortfolioFactory()
assert portfolio.total_obligated_funds == Decimal(0)
def test_with_current(self, current_task_order):
portfolio = PortfolioFactory(
task_orders=[current_task_order, current_task_order]
)
assert portfolio.total_obligated_funds == Decimal(2000.0)
def test_with_others(
self, past_task_order, current_task_order, upcoming_task_order
):
portfolio = PortfolioFactory(
task_orders=[past_task_order, current_task_order, upcoming_task_order,]
)
# Only sums the current task order
assert portfolio.total_obligated_funds == Decimal(1000.0)
class TestUpcomingObligatedFunds:
"""
Tests the upcoming_obligated_funds property
"""
def test_no_task_orders(self):
portfolio = PortfolioFactory()
assert portfolio.upcoming_obligated_funds == Decimal(0)
def test_with_upcoming(self, upcoming_task_order):
portfolio = PortfolioFactory(
task_orders=[upcoming_task_order, upcoming_task_order]
)
assert portfolio.upcoming_obligated_funds == Decimal(1400.0)
def test_with_others(
self, past_task_order, current_task_order, upcoming_task_order
):
portfolio = PortfolioFactory(
task_orders=[past_task_order, current_task_order, upcoming_task_order]
)
# Only sums the upcoming task order
assert portfolio.upcoming_obligated_funds == Decimal(700.0)

View File

@ -508,7 +508,7 @@ portfolios:
estimate_warning: Reports displayed in JEDI are estimates and not a system of record.
total_value:
header: Total Portfolio Value
tooltip: Total portfolio value is all obligated and projected funds for all task orders in this portfolio.
tooltip: Total portfolio value is all obligated funds for current and upcoming task orders in this portfolio.
task_orders:
add_new_button: Add New Task Order
review: