From 6ef3265cb5f0e1ba76c51afef50e6c36f93c289f Mon Sep 17 00:00:00 2001 From: leigh-mil Date: Wed, 12 Feb 2020 11:20:21 -0500 Subject: [PATCH] Create celery task for create_billing_instruction --- atst/domain/csp/cloud/models.py | 8 +++++ atst/jobs.py | 29 +++++++++++++++++ atst/models/clin.py | 6 +++- tests/test_jobs.py | 58 ++++++++++++++++++++++++++++++++- 4 files changed, 99 insertions(+), 2 deletions(-) diff --git a/atst/domain/csp/cloud/models.py b/atst/domain/csp/cloud/models.py index 27f2c9c7..5bb056d7 100644 --- a/atst/domain/csp/cloud/models.py +++ b/atst/domain/csp/cloud/models.py @@ -220,6 +220,14 @@ class BillingInstructionCSPPayload(BaseCSPPayload): billing_account_name: str billing_profile_name: str + class Config: + fields = { + "initial_clin_amount": "obligated_amount", + "initial_clin_start_date": "start_date", + "initial_clin_end_date": "end_date", + "initial_clin_type": "number", + } + class BillingInstructionCSPResult(AliasModel): reported_clin_name: str diff --git a/atst/jobs.py b/atst/jobs.py index c125bcb5..e5805875 100644 --- a/atst/jobs.py +++ b/atst/jobs.py @@ -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,31 @@ 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 + clin_data = clin.to_dictionary() + portfolio_data = portfolio.to_dictionary() + + 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"), + **clin_data, + **portfolio_data, + ) + + 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() diff --git a/atst/models/clin.py b/atst/models/clin.py index accab107..440ee0a0 100644 --- a/atst/models/clin.py +++ b/atst/models/clin.py @@ -66,11 +66,15 @@ 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"] } + data["start_date"] = str(data["start_date"]) + data["end_date"] = str(data["end_date"]) + + return data @property def is_active(self): diff --git a/tests/test_jobs.py b/tests/test_jobs.py index ff681c7e..a7c6aced 100644 --- a/tests/test_jobs.py +++ b/tests/test_jobs.py @@ -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, @@ -489,3 +491,57 @@ class TestSendTaskOrderFiles: # Check that pdf_last_sent_at has not been updated assert not task_order.pdf_last_sent_at + + +class TestCreateBillingInstructions: + def test_update_clin_last_sent_at(self, session): + # create portfolio with one active clin + 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}]}], + ) + unsent_clin = portfolio.task_orders[0].clins[0] + + assert not unsent_clin.last_sent_at + + # The session needs to be nested to prevent detached SQLAlchemy instance + session.begin_nested() + create_billing_instruction() + session.rollback() + + # check that last_sent_at has been updated + assert unsent_clin.last_sent_at + + def test_failure(self, monkeypatch, session): + 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, + ) + + # create portfolio with one active clin + 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}]}], + ) + unsent_clin = portfolio.task_orders[0].clins[0] + + # The session needs to be nested to prevent detached SQLAlchemy instance + session.begin_nested() + create_billing_instruction() + session.rollback() + + # check that last_sent_at has not been updated + assert not unsent_clin.last_sent_at