Merge pull request #593 from dod-ccpo/to-funding-status-alerts
TO Statuses on Portfolio Funding Page
This commit is contained in:
commit
cd13a01af8
@ -1,4 +1,5 @@
|
|||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
import pendulum
|
import pendulum
|
||||||
from sqlalchemy import Column, Numeric, String, ForeignKey, Date, Integer
|
from sqlalchemy import Column, Numeric, String, ForeignKey, Date, Integer
|
||||||
@ -117,6 +118,11 @@ class TaskOrder(Base, mixins.TimestampsMixin):
|
|||||||
def display_status(self):
|
def display_status(self):
|
||||||
return self.status.value
|
return self.status.value
|
||||||
|
|
||||||
|
@property
|
||||||
|
def days_to_expiration(self):
|
||||||
|
if self.end_date:
|
||||||
|
return (self.end_date - date.today()).days
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def budget(self):
|
def budget(self):
|
||||||
return sum(
|
return sum(
|
||||||
|
@ -26,6 +26,7 @@ def portfolio_funding(portfolio_id):
|
|||||||
"start_date",
|
"start_date",
|
||||||
"end_date",
|
"end_date",
|
||||||
"display_status",
|
"display_status",
|
||||||
|
"days_to_expiration",
|
||||||
"balance",
|
"balance",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -45,6 +46,7 @@ def portfolio_funding(portfolio_id):
|
|||||||
if active_task_orders
|
if active_task_orders
|
||||||
else None
|
else None
|
||||||
)
|
)
|
||||||
|
funded = len(active_task_orders) > 1
|
||||||
total_balance = sum([task_order["balance"] for task_order in active_task_orders])
|
total_balance = sum([task_order["balance"] for task_order in active_task_orders])
|
||||||
|
|
||||||
return render_template(
|
return render_template(
|
||||||
@ -54,6 +56,7 @@ def portfolio_funding(portfolio_id):
|
|||||||
active_task_orders=active_task_orders,
|
active_task_orders=active_task_orders,
|
||||||
expired_task_orders=task_orders_by_status.get(TaskOrderStatus.EXPIRED, []),
|
expired_task_orders=task_orders_by_status.get(TaskOrderStatus.EXPIRED, []),
|
||||||
funding_end_date=funding_end_date,
|
funding_end_date=funding_end_date,
|
||||||
|
funded=funded,
|
||||||
total_balance=total_balance,
|
total_balance=total_balance,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
from flask.json import JSONEncoder
|
from flask.json import JSONEncoder
|
||||||
|
from datetime import date
|
||||||
from atst.models.attachment import Attachment
|
from atst.models.attachment import Attachment
|
||||||
|
|
||||||
|
|
||||||
@ -6,4 +7,6 @@ class CustomJSONEncoder(JSONEncoder):
|
|||||||
def default(self, obj):
|
def default(self, obj):
|
||||||
if isinstance(obj, Attachment):
|
if isinstance(obj, Attachment):
|
||||||
return obj.filename
|
return obj.filename
|
||||||
|
if isinstance(obj, date):
|
||||||
|
return obj.strftime("%Y-%m-%d")
|
||||||
return JSONEncoder.default(self, obj)
|
return JSONEncoder.default(self, obj)
|
||||||
|
@ -21,6 +21,7 @@ export default {
|
|||||||
props: {
|
props: {
|
||||||
data: Array,
|
data: Array,
|
||||||
expired: Boolean,
|
expired: Boolean,
|
||||||
|
funded: Boolean,
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
@ -47,6 +48,7 @@ export default {
|
|||||||
attr: 'start_date',
|
attr: 'start_date',
|
||||||
sortFunc: numericSort,
|
sortFunc: numericSort,
|
||||||
width: '50%',
|
width: '50%',
|
||||||
|
class: 'period-of-performance',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
displayName: 'Initial Value',
|
displayName: 'Initial Value',
|
||||||
@ -72,6 +74,7 @@ export default {
|
|||||||
isAscending: false,
|
isAscending: false,
|
||||||
columns: indexBy(prop('displayName'), columns),
|
columns: indexBy(prop('displayName'), columns),
|
||||||
},
|
},
|
||||||
|
days_to_exp_alert_limit: 30,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# Add root application dir to the python path
|
# Add root application dir to the python path
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from datetime import datetime, timedelta, date
|
||||||
|
|
||||||
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
parent_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
sys.path.append(parent_dir)
|
sys.path.append(parent_dir)
|
||||||
@ -94,9 +95,12 @@ def seed_db():
|
|||||||
|
|
||||||
users.append(user)
|
users.append(user)
|
||||||
|
|
||||||
|
amanda = Users.get_by_dod_id("2345678901")
|
||||||
|
|
||||||
|
# create Portfolios for all users that have funding and are not expiring soon
|
||||||
for user in users:
|
for user in users:
|
||||||
portfolio = Portfolios.create(
|
portfolio = Portfolios.create(
|
||||||
user, name="{}'s portfolio".format(user.first_name)
|
user, name="{}'s portfolio (not expiring)".format(user.first_name)
|
||||||
)
|
)
|
||||||
for portfolio_role in PORTFOLIO_USERS:
|
for portfolio_role in PORTFOLIO_USERS:
|
||||||
ws_role = Portfolios.create_member(user, portfolio, portfolio_role)
|
ws_role = Portfolios.create_member(user, portfolio, portfolio_role)
|
||||||
@ -161,6 +165,92 @@ def seed_db():
|
|||||||
environment_names=["dev", "staging", "prod"],
|
environment_names=["dev", "staging", "prod"],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Create Portfolio for Amanda with TO that is expiring soon and does not have another TO
|
||||||
|
unfunded_portfolio = Portfolios.create(
|
||||||
|
amanda, name="{}'s portfolio (expiring and unfunded)".format(amanda.first_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
[past_date_1, past_date_2, past_date_3, future_date] = sorted(
|
||||||
|
[
|
||||||
|
random_past_date(year_max=3, year_min=2),
|
||||||
|
random_past_date(year_max=2, year_min=1),
|
||||||
|
random_past_date(year_max=1, year_min=1),
|
||||||
|
(date.today() + timedelta(days=20)),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
date_ranges = [
|
||||||
|
(past_date_1, past_date_2),
|
||||||
|
(past_date_2, past_date_3),
|
||||||
|
(past_date_3, future_date),
|
||||||
|
]
|
||||||
|
for (start_date, end_date) in date_ranges:
|
||||||
|
task_order = TaskOrderFactory.build(
|
||||||
|
start_date=start_date,
|
||||||
|
end_date=end_date,
|
||||||
|
number=random_task_order_number(),
|
||||||
|
portfolio=unfunded_portfolio,
|
||||||
|
)
|
||||||
|
db.session.add(task_order)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
Applications.create(
|
||||||
|
amanda,
|
||||||
|
portfolio=unfunded_portfolio,
|
||||||
|
name="First Application",
|
||||||
|
description="This is our first application.",
|
||||||
|
environment_names=["dev", "staging", "prod"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create Portfolio for Amanda with TO that is expiring soon and has another TO
|
||||||
|
funded_portfolio = Portfolios.create(
|
||||||
|
amanda, name="{}'s portfolio (expiring and funded)".format(amanda.first_name)
|
||||||
|
)
|
||||||
|
|
||||||
|
[
|
||||||
|
past_date_1,
|
||||||
|
past_date_2,
|
||||||
|
past_date_3,
|
||||||
|
past_date_4,
|
||||||
|
future_date_1,
|
||||||
|
future_date_2,
|
||||||
|
] = sorted(
|
||||||
|
[
|
||||||
|
random_past_date(year_max=3, year_min=2),
|
||||||
|
random_past_date(year_max=2, year_min=1),
|
||||||
|
random_past_date(year_max=1, year_min=1),
|
||||||
|
random_past_date(year_max=1, year_min=1),
|
||||||
|
(date.today() + timedelta(days=20)),
|
||||||
|
random_future_date(year_min=0, year_max=1),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
date_ranges = [
|
||||||
|
(past_date_1, past_date_2),
|
||||||
|
(past_date_2, past_date_3),
|
||||||
|
(past_date_3, future_date_1),
|
||||||
|
(past_date_4, future_date_2),
|
||||||
|
]
|
||||||
|
for (start_date, end_date) in date_ranges:
|
||||||
|
task_order = TaskOrderFactory.build(
|
||||||
|
start_date=start_date,
|
||||||
|
end_date=end_date,
|
||||||
|
number=random_task_order_number(),
|
||||||
|
portfolio=funded_portfolio,
|
||||||
|
)
|
||||||
|
db.session.add(task_order)
|
||||||
|
|
||||||
|
db.session.commit()
|
||||||
|
|
||||||
|
Applications.create(
|
||||||
|
amanda,
|
||||||
|
portfolio=funded_portfolio,
|
||||||
|
name="First Application",
|
||||||
|
description="This is our first application.",
|
||||||
|
environment_names=["dev", "staging", "prod"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
config = make_config({"DISABLE_CRL_CHECK": True})
|
config = make_config({"DISABLE_CRL_CHECK": True})
|
||||||
|
@ -50,6 +50,13 @@
|
|||||||
@include icon-color($color-green);
|
@include icon-color($color-green);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.unfunded {
|
||||||
|
color: $color-red;
|
||||||
|
.icon {
|
||||||
|
@include icon-color($color-red);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.pending-task-order {
|
.pending-task-order {
|
||||||
@ -82,10 +89,17 @@
|
|||||||
|
|
||||||
.view-task-order-link {
|
.view-task-order-link {
|
||||||
margin-left: $gap * 2;
|
margin-left: $gap * 2;
|
||||||
|
|
||||||
|
.icon--tiny {
|
||||||
|
@include icon-size(10);
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.portfolio-total-balance {
|
.portfolio-total-balance {
|
||||||
margin-top: -$gap;
|
margin-top: -$gap;
|
||||||
|
margin-bottom: 3rem;
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
flex-direction: row-reverse;
|
flex-direction: row-reverse;
|
||||||
margin: 2 * $gap 0;
|
margin: 2 * $gap 0;
|
||||||
@ -98,8 +112,60 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
table {
|
table {
|
||||||
|
th{
|
||||||
|
.icon {
|
||||||
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.period-of-performance {
|
||||||
|
color: $color-blue;
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
@include icon-color($color-primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
td.unused-balance {
|
td.unused-balance {
|
||||||
color: $color-red;
|
color: $color-red;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.label--expired {
|
||||||
|
background-color: $color-gray-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.to-performance-period {
|
||||||
|
&.to-expiring-soon {
|
||||||
|
|
||||||
|
.to-expiration-alert {
|
||||||
|
font-weight: $font-bold;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
margin-left: $gap;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.funded .to-expiration-alert {
|
||||||
|
color: $color-blue;
|
||||||
|
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
@include icon-color($color-blue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.unfunded {
|
||||||
|
.to-expiration-alert {
|
||||||
|
color: $color-red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
@include icon-color($color-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.to-end-date {
|
||||||
|
color: $color-red;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,15 +8,16 @@
|
|||||||
{% macro ViewLink(task_order) %}
|
{% macro ViewLink(task_order) %}
|
||||||
<a href="{{ url_for('portfolios.view_task_order', portfolio_id=portfolio.id, task_order_id=task_order.id) }}" class="icon-link view-task-order-link">
|
<a href="{{ url_for('portfolios.view_task_order', portfolio_id=portfolio.id, task_order_id=task_order.id) }}" class="icon-link view-task-order-link">
|
||||||
<span>View</span>
|
<span>View</span>
|
||||||
{{ Icon("caret_right") }}
|
{{ Icon("caret_right", classes="icon--tiny") }}
|
||||||
</a>
|
</a>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro TaskOrderList(task_orders, label='success', expired=False) %}
|
{% macro TaskOrderList(task_orders, label='success', expired=False, funded=False) %}
|
||||||
<task-order-list
|
<task-order-list
|
||||||
inline-template
|
inline-template
|
||||||
v-bind:data='{{ task_orders | tojson }}'
|
v-bind:data='{{ task_orders | tojson }}'
|
||||||
v-bind:expired='{{ 'true' if expired else 'false' }}'
|
v-bind:expired='{{ 'true' if expired else 'false' }}'
|
||||||
|
v-bind:funded='{{'true' if funded else 'false' }}'
|
||||||
v-cloak
|
v-cloak
|
||||||
>
|
>
|
||||||
<div class='responsive-table-wrapper'>
|
<div class='responsive-table-wrapper'>
|
||||||
@ -27,10 +28,10 @@
|
|||||||
!{ col.displayName }
|
!{ col.displayName }
|
||||||
<template v-if="col.sortFunc">
|
<template v-if="col.sortFunc">
|
||||||
<span class="sorting-direction" v-if="col.displayName === sortInfo.columnName && sortInfo.isAscending">
|
<span class="sorting-direction" v-if="col.displayName === sortInfo.columnName && sortInfo.isAscending">
|
||||||
{{ Icon("caret_down") }}
|
{{ Icon("caret_down", classes="icon--tiny") }}
|
||||||
</span>
|
</span>
|
||||||
<span class="sorting-direction" v-if="col.displayName === sortInfo.columnName && !sortInfo.isAscending">
|
<span class="sorting-direction" v-if="col.displayName === sortInfo.columnName && !sortInfo.isAscending">
|
||||||
{{ Icon("caret_up") }}
|
{{ Icon("caret_up", classes="icon--tiny") }}
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</th>
|
</th>
|
||||||
@ -43,16 +44,29 @@
|
|||||||
<span class='label label--{{ label }}'>!{ taskOrder.display_status }</span>
|
<span class='label label--{{ label }}'>!{ taskOrder.display_status }</span>
|
||||||
</td>
|
</td>
|
||||||
<td class='table-cell--grow'>
|
<td class='table-cell--grow'>
|
||||||
<span>
|
<span :class="{ 'to-performance-period': true, 'to-expiring-soon': (taskOrder.days_to_expiration > 0 && taskOrder.days_to_expiration <= days_to_exp_alert_limit), 'funded': funded && taskOrder.display_status === 'Active', 'unfunded': !funded && taskOrder.display_status === 'Active' }">
|
||||||
<local-datetime
|
<local-datetime
|
||||||
v-bind:timestamp="taskOrder.start_date"
|
v-bind:timestamp="taskOrder.start_date"
|
||||||
format="M/D/YYYY">
|
format="M/D/YYYY">
|
||||||
</local-datetime>
|
</local-datetime>
|
||||||
-
|
-
|
||||||
<local-datetime
|
<local-datetime
|
||||||
v-bind:timestamp="taskOrder.end_date"
|
v-bind:timestamp="taskOrder.end_date"
|
||||||
format="M/D/YYYY">
|
format="M/D/YYYY"
|
||||||
|
class="to-end-date"
|
||||||
|
>
|
||||||
</local-datetime>
|
</local-datetime>
|
||||||
|
<span
|
||||||
|
v-if="taskOrder.days_to_expiration > 0 && taskOrder.days_to_expiration <= days_to_exp_alert_limit && funded"
|
||||||
|
class="to-expiration-alert">
|
||||||
|
{{ Icon('ok') }} Period ending in !{ taskOrder.days_to_expiration } days, but new period funded
|
||||||
|
</span>
|
||||||
|
<span
|
||||||
|
v-if="taskOrder.days_to_expiration > 0 && taskOrder.days_to_expiration <= days_to_exp_alert_limit && !funded"
|
||||||
|
class="to-expiration-alert">
|
||||||
|
{{ Icon('alert') }} Period ends in !{ taskOrder.days_to_expiration } days, submit a new task order<br>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
</td>
|
</td>
|
||||||
<td class="table-cell--align-right">
|
<td class="table-cell--align-right">
|
||||||
<span v-html='formatDollars(taskOrder.budget)'></span>
|
<span v-html='formatDollars(taskOrder.budget)'></span>
|
||||||
@ -63,7 +77,7 @@
|
|||||||
<td>
|
<td>
|
||||||
<a v-bind:href="taskOrder.url" class="icon-link view-task-order-link">
|
<a v-bind:href="taskOrder.url" class="icon-link view-task-order-link">
|
||||||
<span>View</span>
|
<span>View</span>
|
||||||
{{ Icon("caret_right") }}
|
{{ Icon("caret_right", classes="icon--tiny") }}
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@ -78,15 +92,22 @@
|
|||||||
<div class='panel'>
|
<div class='panel'>
|
||||||
<div class='panel__content portfolio-funding__header row'>
|
<div class='panel__content portfolio-funding__header row'>
|
||||||
<h3>Portfolio Funding</h3>
|
<h3>Portfolio Funding</h3>
|
||||||
<div class='portfolio-funding__header--funded-through {{ "funded" if funding_end_date is not none }}'>
|
<div class='portfolio-funding__header--funded-through {{ "funded" if funding_end_date is not none and funded else "unfunded"}}'>
|
||||||
{% if funding_end_date %}
|
{% if funding_end_date and funded %}
|
||||||
{{ Icon('ok') }}
|
{{ Icon('ok') }}
|
||||||
Funded through
|
Funded through
|
||||||
<local-datetime
|
<local-datetime
|
||||||
timestamp="{{ funding_end_date }}"
|
timestamp='{{ funding_end_date }}'
|
||||||
format="M/D/YYYY">
|
format="M/D/YYYY">
|
||||||
</local-datetime>
|
</local-datetime>
|
||||||
{% endif %}
|
{% elif funding_end_date and not funded %}
|
||||||
|
{{ Icon('alert') }}
|
||||||
|
Funded period ends
|
||||||
|
<local-datetime
|
||||||
|
timestamp='{{ funding_end_date }}'
|
||||||
|
format="M/D/YYYY">
|
||||||
|
</local-datetime>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<a href="{{ url_for("task_orders.new", screen=1, portfolio_id=portfolio.id) }}" class="usa-button">Start a New Task Order</a>
|
<a href="{{ url_for("task_orders.new", screen=1, portfolio_id=portfolio.id) }}" class="usa-button">Start a New Task Order</a>
|
||||||
</div>
|
</div>
|
||||||
@ -125,7 +146,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if active_task_orders %}
|
{% if active_task_orders %}
|
||||||
{{ TaskOrderList(active_task_orders, label='success') }}
|
{{ TaskOrderList(active_task_orders, label='success', funded=funded) }}
|
||||||
<div class='panel portfolio-total-balance'>
|
<div class='panel portfolio-total-balance'>
|
||||||
<div class='panel__content row'>
|
<div class='panel__content row'>
|
||||||
<span>{{ total_balance | dollars }}</span>
|
<span>{{ total_balance | dollars }}</span>
|
||||||
@ -135,7 +156,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if expired_task_orders %}
|
{% if expired_task_orders %}
|
||||||
{{ TaskOrderList(expired_task_orders, label='', expired=True) }}
|
{{ TaskOrderList(expired_task_orders, label='expired', expired=True) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
from flask import url_for
|
from flask import url_for
|
||||||
import pytest
|
import pytest
|
||||||
|
from datetime import timedelta, date
|
||||||
|
|
||||||
from atst.domain.roles import Roles
|
from atst.domain.roles import Roles
|
||||||
from atst.domain.task_orders import TaskOrders
|
from atst.domain.task_orders import TaskOrders
|
||||||
@ -17,7 +18,7 @@ from tests.utils import captured_templates
|
|||||||
|
|
||||||
|
|
||||||
class TestPortfolioFunding:
|
class TestPortfolioFunding:
|
||||||
def test_unfunded_portfolio(self, app, user_session):
|
def test_portfolio_with_no_task_orders(self, app, user_session):
|
||||||
portfolio = PortfolioFactory.create()
|
portfolio = PortfolioFactory.create()
|
||||||
user_session(portfolio.owner)
|
user_session(portfolio.owner)
|
||||||
|
|
||||||
@ -67,6 +68,54 @@ class TestPortfolioFunding:
|
|||||||
assert context["funding_end_date"] is end_date
|
assert context["funding_end_date"] is end_date
|
||||||
assert context["total_balance"] == active_to1.budget + active_to2.budget
|
assert context["total_balance"] == active_to1.budget + active_to2.budget
|
||||||
|
|
||||||
|
def test_expiring_and_funded_portfolio(self, app, user_session):
|
||||||
|
portfolio = PortfolioFactory.create()
|
||||||
|
user_session(portfolio.owner)
|
||||||
|
|
||||||
|
expiring_to = TaskOrderFactory.create(
|
||||||
|
portfolio=portfolio,
|
||||||
|
start_date=random_past_date(),
|
||||||
|
end_date=(date.today() + timedelta(days=10)),
|
||||||
|
number="42",
|
||||||
|
)
|
||||||
|
active_to = TaskOrderFactory.create(
|
||||||
|
portfolio=portfolio,
|
||||||
|
start_date=random_past_date(),
|
||||||
|
end_date=random_future_date(year_min=1, year_max=2),
|
||||||
|
number="43",
|
||||||
|
)
|
||||||
|
|
||||||
|
with captured_templates(app) as templates:
|
||||||
|
response = app.test_client().get(
|
||||||
|
url_for("portfolios.portfolio_funding", portfolio_id=portfolio.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
_, context = templates[0]
|
||||||
|
assert context["funding_end_date"] is active_to.end_date
|
||||||
|
assert context["funded"] == True
|
||||||
|
|
||||||
|
def test_expiring_and_unfunded_portfolio(self, app, user_session):
|
||||||
|
portfolio = PortfolioFactory.create()
|
||||||
|
user_session(portfolio.owner)
|
||||||
|
|
||||||
|
expiring_to = TaskOrderFactory.create(
|
||||||
|
portfolio=portfolio,
|
||||||
|
start_date=random_past_date(),
|
||||||
|
end_date=(date.today() + timedelta(days=10)),
|
||||||
|
number="42",
|
||||||
|
)
|
||||||
|
|
||||||
|
with captured_templates(app) as templates:
|
||||||
|
response = app.test_client().get(
|
||||||
|
url_for("portfolios.portfolio_funding", portfolio_id=portfolio.id)
|
||||||
|
)
|
||||||
|
|
||||||
|
assert response.status_code == 200
|
||||||
|
_, context = templates[0]
|
||||||
|
assert context["funding_end_date"] is expiring_to.end_date
|
||||||
|
assert context["funded"] == False
|
||||||
|
|
||||||
|
|
||||||
class TestTaskOrderInvitations:
|
class TestTaskOrderInvitations:
|
||||||
def setup(self):
|
def setup(self):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user