Merge pull request #1420 from dod-ccpo/update-clins

Update CLINs
This commit is contained in:
leigh-mil 2020-02-20 10:19:52 -05:00 committed by GitHub
commit 57ba7d5979
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 160 additions and 16 deletions

View File

@ -90,3 +90,13 @@ class TaskOrders(BaseDomainClass):
)
.all()
)
@classmethod
def get_clins_for_create_billing_instructions(cls):
return (
db.session.query(CLIN)
.filter(
CLIN.last_sent_at.is_(None), CLIN.start_date < pendulum.now(tz="UTC")
)
.all()
)

View File

@ -10,6 +10,7 @@ from atst.domain.csp.cloud import CloudProviderInterface
from atst.domain.csp.cloud.exceptions import GeneralCSPException
from atst.domain.csp.cloud.models import (
ApplicationCSPPayload,
BillingInstructionCSPPayload,
EnvironmentCSPPayload,
UserCSPPayload,
UserRoleCSPPayload,
@ -317,3 +318,32 @@ def send_task_order_files(self):
db.session.add(task_order)
db.session.commit()
@celery.task(bind=True)
def create_billing_instruction(self):
clins = TaskOrders.get_clins_for_create_billing_instructions()
for clin in clins:
portfolio = clin.task_order.portfolio
payload = BillingInstructionCSPPayload(
tenant_id=portfolio.csp_data.get("tenant_id"),
billing_account_name=portfolio.csp_data.get("billing_account_name"),
billing_profile_name=portfolio.csp_data.get("billing_profile_name"),
initial_clin_amount=clin.obligated_amount,
initial_clin_start_date=str(clin.start_date),
initial_clin_end_date=str(clin.end_date),
initial_clin_type=clin.number,
initial_task_order_id=str(clin.task_order_id),
)
try:
app.csp.cloud.create_billing_instruction(payload)
except (AzureError) as err:
app.logger.exception(err)
continue
clin.last_sent_at = pendulum.now(tz="UTC")
db.session.add(clin)
db.session.commit()

View File

@ -66,12 +66,14 @@ class CLIN(Base, mixins.TimestampsMixin):
)
def to_dictionary(self):
return {
data = {
c.name: getattr(self, c.name)
for c in self.__table__.columns
if c.name not in ["id"]
}
return data
@property
def is_active(self):
return (

View File

@ -31,6 +31,10 @@ def update_celery(celery, app):
"task": "atst.jobs.send_task_order_files",
"schedule": 60,
},
"beat-create_billing_instruction": {
"task": "atst.jobs.create_billing_instruction",
"schedule": 60,
},
}
class ContextTask(celery.Task):

View File

@ -9,6 +9,27 @@ from atst.models.task_order import TaskOrder, SORT_ORDERING, Status
from tests.factories import TaskOrderFactory, CLINFactory, PortfolioFactory
@pytest.fixture
def new_task_order():
return TaskOrderFactory.create(create_clins=[{}])
@pytest.fixture
def updated_task_order():
return TaskOrderFactory.create(
create_clins=[{"last_sent_at": pendulum.date(2020, 2, 1)}],
pdf_last_sent_at=pendulum.date(2020, 1, 1),
)
@pytest.fixture
def sent_task_order():
return TaskOrderFactory.create(
create_clins=[{"last_sent_at": pendulum.date(2020, 1, 1)}],
pdf_last_sent_at=pendulum.date(2020, 1, 1),
)
def test_create_adds_clins():
portfolio = PortfolioFactory.create()
clins = [
@ -181,19 +202,18 @@ def test_allows_alphanumeric_number():
assert TaskOrders.create(portfolio.id, number, [], None)
def test_get_for_send_task_order_files():
new_to = TaskOrderFactory.create(create_clins=[{}])
updated_to = TaskOrderFactory.create(
create_clins=[{"last_sent_at": pendulum.datetime(2020, 2, 1)}],
pdf_last_sent_at=pendulum.datetime(2020, 1, 1),
)
sent_to = TaskOrderFactory.create(
create_clins=[{"last_sent_at": pendulum.datetime(2020, 1, 1)}],
pdf_last_sent_at=pendulum.datetime(2020, 1, 1),
)
def test_get_for_send_task_order_files(
new_task_order, updated_task_order, sent_task_order
):
updated_and_new_task_orders = TaskOrders.get_for_send_task_order_files()
assert len(updated_and_new_task_orders) == 2
assert sent_to not in updated_and_new_task_orders
assert updated_to in updated_and_new_task_orders
assert new_to in updated_and_new_task_orders
assert sent_task_order not in updated_and_new_task_orders
assert updated_task_order in updated_and_new_task_orders
assert new_task_order in updated_and_new_task_orders
def test_get_clins_for_create_billing_instructions(new_task_order, sent_task_order):
new_clins = TaskOrders.get_clins_for_create_billing_instructions()
assert len(new_clins) == 1
assert new_task_order.clins[0] in new_clins
assert sent_task_order.clins[0] not in new_clins

View File

@ -6,7 +6,8 @@ from smtplib import SMTPException
from azure.core.exceptions import AzureError
from atst.domain.csp.cloud import MockCloudProvider
from atst.domain.csp.cloud.models import UserRoleCSPResult
from atst.domain.csp.cloud.models import BillingInstructionCSPPayload, UserRoleCSPResult
from atst.domain.portfolios import Portfolios
from atst.models import ApplicationRoleStatus, Portfolio, FSMStates
from atst.jobs import (
@ -16,6 +17,7 @@ from atst.jobs import (
dispatch_create_user,
dispatch_create_environment_role,
dispatch_provision_portfolio,
create_billing_instruction,
create_environment,
do_create_user,
do_provision_portfolio,
@ -28,6 +30,7 @@ from atst.jobs import (
from tests.factories import (
ApplicationFactory,
ApplicationRoleFactory,
CLINFactory,
EnvironmentFactory,
EnvironmentRoleFactory,
PortfolioFactory,
@ -489,3 +492,78 @@ class TestSendTaskOrderFiles:
# Check that pdf_last_sent_at has not been updated
assert not task_order.pdf_last_sent_at
class TestCreateBillingInstructions:
@pytest.fixture
def unsent_clin(self):
start_date = pendulum.now().subtract(days=1)
portfolio = PortfolioFactory.create(
csp_data={
"tenant_id": str(uuid4()),
"billing_account_name": "fake",
"billing_profile_name": "fake",
},
task_orders=[{"create_clins": [{"start_date": start_date}]}],
)
return portfolio.task_orders[0].clins[0]
def test_update_clin_last_sent_at(self, session, unsent_clin):
assert not unsent_clin.last_sent_at
# The session needs to be nested to prevent detached SQLAlchemy instance
session.begin_nested()
create_billing_instruction()
# check that last_sent_at has been updated
assert unsent_clin.last_sent_at
session.rollback()
def test_failure(self, monkeypatch, session, unsent_clin):
def _create_billing_instruction(MockCloudProvider, object_name):
raise AzureError("something went wrong")
monkeypatch.setattr(
"atst.domain.csp.cloud.MockCloudProvider.create_billing_instruction",
_create_billing_instruction,
)
# The session needs to be nested to prevent detached SQLAlchemy instance
session.begin_nested()
create_billing_instruction()
# check that last_sent_at has not been updated
assert not unsent_clin.last_sent_at
session.rollback()
def test_task_order_with_multiple_clins(self, session):
start_date = pendulum.now(tz="UTC").subtract(days=1)
portfolio = PortfolioFactory.create(
csp_data={
"tenant_id": str(uuid4()),
"billing_account_name": "fake",
"billing_profile_name": "fake",
},
task_orders=[
{
"create_clins": [
{"start_date": start_date, "last_sent_at": start_date}
]
}
],
)
task_order = portfolio.task_orders[0]
sent_clin = task_order.clins[0]
# Add new CLIN to the Task Order
new_clin = CLINFactory.create(task_order=task_order)
assert not new_clin.last_sent_at
session.begin_nested()
create_billing_instruction()
session.add(sent_clin)
# check that last_sent_at has been update for the new clin only
assert new_clin.last_sent_at
assert sent_clin.last_sent_at != new_clin.last_sent_at
session.rollback()