Implement new CLIN-based TO statuses

This commit is contained in:
richard-dds 2019-06-06 15:40:41 -04:00
parent 7b8ccbf145
commit 8ecf112c48
2 changed files with 90 additions and 20 deletions

View File

@ -1,5 +1,5 @@
from enum import Enum from enum import Enum
from datetime import date from datetime import date, datetime
from sqlalchemy import Column, DateTime, ForeignKey, String from sqlalchemy import Column, DateTime, ForeignKey, String
from sqlalchemy.ext.hybrid import hybrid_property from sqlalchemy.ext.hybrid import hybrid_property
@ -15,6 +15,9 @@ class Status(Enum):
PENDING = "Pending" PENDING = "Pending"
ACTIVE = "Active" ACTIVE = "Active"
EXPIRED = "Expired" EXPIRED = "Expired"
DRAFT = "Draft"
UPCOMING = "Upcoming"
UNSIGNED = "Unsigned"
class TaskOrder(Base, mixins.TimestampsMixin): class TaskOrder(Base, mixins.TimestampsMixin):
@ -62,19 +65,36 @@ class TaskOrder(Base, mixins.TimestampsMixin):
def is_expired(self): def is_expired(self):
return self.status == Status.EXPIRED return self.status == Status.EXPIRED
@property
def is_completed(self):
return True
@property
def is_signed(self):
return self.signed_at is not None
@property @property
def status(self): def status(self):
# TODO: fix task order -- implement correctly using CLINs today = date.today()
# Faked for display purposes
return Status.ACTIVE if not self.is_completed and not self.is_signed:
return Status.DRAFT
elif self.is_completed and not self.is_signed:
return Status.UNSIGNED
elif today < self.start_date:
return Status.UPCOMING
elif today >= self.end_date:
return Status.EXPIRED
elif self.start_date <= today < self.end_date:
return Status.ACTIVE
@property @property
def start_date(self): def start_date(self):
return min(c.start_date for c in self.clins) return min((c.start_date for c in self.clins), default=None)
@property @property
def end_date(self): def end_date(self):
return max(c.end_date for c in self.clins) return max((c.end_date for c in self.clins), default=None)
@property @property
def days_to_expiration(self): def days_to_expiration(self):

View File

@ -1,6 +1,8 @@
from werkzeug.datastructures import FileStorage from werkzeug.datastructures import FileStorage
import pytest import pytest
from datetime import date from datetime import date, datetime
from unittest.mock import Mock, patch, PropertyMock
import pendulum
from atst.models import * from atst.models import *
from atst.models.clin import JEDICLINType from atst.models.clin import JEDICLINType
@ -48,26 +50,74 @@ class TestPeriodOfPerformance:
class TestTaskOrderStatus: class TestTaskOrderStatus:
@pytest.mark.skip(reason="Reimplement after adding CLINs") @patch("atst.models.TaskOrder.is_completed", new_callable=PropertyMock)
def test_started_status(self): @patch("atst.models.TaskOrder.is_signed", new_callable=PropertyMock)
def test_draft_status(self, is_signed, is_completed):
# Given that I have a TO that is neither completed nor signed
to = TaskOrder() to = TaskOrder()
assert to.status == Status.STARTED is_signed.return_value = False
is_completed.return_value = False
@pytest.mark.skip(reason="See if still needed after implementing CLINs") assert to.status == Status.DRAFT
def test_pending_status(self):
to = TaskOrder(number="42")
assert to.status == Status.PENDING
@pytest.mark.skip(reason="See if still needed after implementing CLINs") @patch("atst.models.TaskOrder.end_date", new_callable=PropertyMock)
def test_active_status(self): @patch("atst.models.TaskOrder.start_date", new_callable=PropertyMock)
to = TaskOrder(number="42") @patch("atst.models.TaskOrder.is_completed", new_callable=PropertyMock)
@patch("atst.models.TaskOrder.is_signed", new_callable=PropertyMock)
def test_active_status(self, is_signed, is_completed, start_date, end_date):
# Given that I have a signed TO and today is within its start_date and end_date
today = pendulum.today().date()
to = TaskOrder()
start_date.return_value = today.subtract(days=1)
end_date.return_value = today.add(days=1)
is_signed.return_value = True
is_completed.return_value = True
# Its status should be active
assert to.status == Status.ACTIVE assert to.status == Status.ACTIVE
@pytest.mark.skip(reason="See if still needed after implementing CLINs") @patch("atst.models.TaskOrder.end_date", new_callable=PropertyMock)
def test_expired_status(self): @patch("atst.models.TaskOrder.start_date", new_callable=PropertyMock)
to = TaskOrder(number="42") @patch("atst.models.TaskOrder.is_completed", new_callable=PropertyMock)
@patch("atst.models.TaskOrder.is_signed", new_callable=PropertyMock)
def test_upcoming_status(self, is_signed, is_completed, start_date, end_date):
# Given that I have a signed TO and today is before its start_date
to = TaskOrder()
start_date.return_value = pendulum.today().add(days=1).date()
end_date.return_value = pendulum.today().add(days=2).date()
is_signed.return_value = True
is_completed.return_value = True
# Its status should be upcoming
assert to.status == Status.UPCOMING
@patch("atst.models.TaskOrder.start_date", new_callable=PropertyMock)
@patch("atst.models.TaskOrder.end_date", new_callable=PropertyMock)
@patch("atst.models.TaskOrder.is_completed", new_callable=PropertyMock)
@patch("atst.models.TaskOrder.is_signed", new_callable=PropertyMock)
def test_expired_status(self, is_signed, is_completed, end_date, start_date):
# Given that I have a signed TO and today is after its expiration date
to = TaskOrder()
end_date.return_value = pendulum.today().subtract(days=1).date()
start_date.return_value = pendulum.today().subtract(days=2).date()
is_signed.return_value = True
is_completed.return_value = True
# Its status should be expired
assert to.status == Status.EXPIRED assert to.status == Status.EXPIRED
@patch("atst.models.TaskOrder.is_completed", new_callable=PropertyMock)
@patch("atst.models.TaskOrder.is_signed", new_callable=PropertyMock)
def test_unsigned_status(self, is_signed, is_completed):
# Given that I have a TO that is completed but not signed
to = TaskOrder(signed_at=pendulum.now().subtract(days=1))
is_completed.return_value = True
is_signed.return_value = False
# Its status should be unsigned
assert to.status == Status.UNSIGNED
class TestBudget: class TestBudget:
def test_total_contract_amount(self): def test_total_contract_amount(self):