From 2a9076ff1341c5b6d6e75f74b0573655ccc40c7a Mon Sep 17 00:00:00 2001 From: Mark Hobson Date: Thu, 23 Nov 2023 18:42:53 +0000 Subject: [PATCH] Extract date range domain value object --- schemes/schemes/domain.py | 34 ++--- schemes/schemes/services.py | 15 +-- schemes/schemes/views.py | 13 +- tests/integration/test_authorities.py | 7 +- tests/integration/test_scheme.py | 22 ++-- tests/unit/schemes/test_domain.py | 183 +++++++------------------- tests/unit/schemes/test_services.py | 57 ++++---- tests/unit/schemes/test_views.py | 41 +++--- 8 files changed, 132 insertions(+), 240 deletions(-) diff --git a/schemes/schemes/domain.py b/schemes/schemes/domain.py index 7bbf3a7c..35a4fd77 100644 --- a/schemes/schemes/domain.py +++ b/schemes/schemes/domain.py @@ -32,7 +32,7 @@ def current_milestone(self) -> Milestone | None: actual_milestones = [ revision.milestone for revision in self._milestone_revisions - if revision.observation_type == ObservationType.ACTUAL and revision.effective_date_to is None + if revision.observation_type == ObservationType.ACTUAL and revision.effective.date_to is None ] return sorted(actual_milestones)[-1] if actual_milestones else None @@ -48,7 +48,7 @@ def funding_allocation(self) -> Decimal | None: amounts = [ revision.amount for revision in self._financial_revisions - if revision.type == FinancialType.FUNDING_ALLOCATION and revision.effective_date_to is None + if revision.type == FinancialType.FUNDING_ALLOCATION and revision.effective.date_to is None ] return sum(amounts, Decimal(0)) if amounts else None @@ -57,7 +57,7 @@ def spend_to_date(self) -> Decimal | None: amounts = [ revision.amount for revision in self._financial_revisions - if revision.type == FinancialType.SPENT_TO_DATE and revision.effective_date_to is None + if revision.type == FinancialType.SPENT_TO_DATE and revision.effective.date_to is None ] return sum(amounts, Decimal(0)) if amounts else None @@ -66,7 +66,7 @@ def change_control_adjustment(self) -> Decimal | None: amounts = [ revision.amount for revision in self._financial_revisions - if revision.source == DataSource.CHANGE_CONTROL and revision.effective_date_to is None + if revision.source == DataSource.CHANGE_CONTROL and revision.effective.date_to is None ] return sum(amounts, Decimal(0)) if amounts else None @@ -96,18 +96,20 @@ class FundingProgramme(Enum): @dataclass(frozen=True) class MilestoneRevision: - effective_date_from: date - effective_date_to: date | None + effective: DateRange milestone: Milestone observation_type: ObservationType status_date: date + +@dataclass(frozen=True) +class DateRange: + date_from: date + date_to: date | None + def __post_init__(self) -> None: - if not (self.effective_date_to is None or self.effective_date_from <= self.effective_date_to): - raise ValueError( - f"Effective date from '{self.effective_date_from}' must not " - f"be after effective date to '{self.effective_date_to}'" - ) + if not (self.date_to is None or self.date_from <= self.date_to): + raise ValueError(f"From date '{self.date_from}' must not be after to date '{self.date_to}'") class Milestone(IntEnum): @@ -131,19 +133,11 @@ class ObservationType(Enum): @dataclass(frozen=True) class FinancialRevision: - effective_date_from: date - effective_date_to: date | None + effective: DateRange type: FinancialType amount: Decimal source: DataSource - def __post_init__(self) -> None: - if not (self.effective_date_to is None or self.effective_date_from <= self.effective_date_to): - raise ValueError( - f"Effective date from '{self.effective_date_from}' must not " - f"be after effective date to '{self.effective_date_to}'" - ) - class FinancialType(Enum): EXPECTED_COST = auto() diff --git a/schemes/schemes/services.py b/schemes/schemes/services.py index da26e760..a14f528a 100644 --- a/schemes/schemes/services.py +++ b/schemes/schemes/services.py @@ -19,6 +19,7 @@ from schemes.schemes.domain import ( DataSource, + DateRange, FinancialRevision, FinancialType, FundingProgramme, @@ -123,8 +124,8 @@ def add(self, *schemes: Scheme) -> None: connection.execute( insert(self._scheme_milestone_table).values( capital_scheme_id=scheme.id, - effective_date_from=milestone_revision.effective_date_from, - effective_date_to=milestone_revision.effective_date_to, + effective_date_from=milestone_revision.effective.date_from, + effective_date_to=milestone_revision.effective.date_to, milestone_id=MILESTONE_MAPPER.to_id(milestone_revision.milestone), observation_type_id=OBSERVATION_TYPE_MAPPER.to_id(milestone_revision.observation_type), status_date=milestone_revision.status_date, @@ -134,8 +135,8 @@ def add(self, *schemes: Scheme) -> None: connection.execute( insert(self._capital_scheme_financial_table).values( capital_scheme_id=scheme.id, - effective_date_from=financial_revision.effective_date_from, - effective_date_to=financial_revision.effective_date_to, + effective_date_from=financial_revision.effective.date_from, + effective_date_to=financial_revision.effective.date_to, financial_type_id=FINANCIAL_TYPE_MAPPER.to_id(financial_revision.type), amount=financial_revision.amount, data_source_id=DATA_SOURCE_MAPPER.to_id(financial_revision.source), @@ -231,8 +232,7 @@ def _capital_scheme_to_domain(row: Row[Any]) -> Scheme: @staticmethod def _scheme_milestone_to_domain(row: Row[Any]) -> MilestoneRevision: return MilestoneRevision( - effective_date_from=row.effective_date_from, - effective_date_to=row.effective_date_to, + effective=DateRange(row.effective_date_from, row.effective_date_to), milestone=MILESTONE_MAPPER.to_domain(row.milestone_id), observation_type=OBSERVATION_TYPE_MAPPER.to_domain(row.observation_type_id), status_date=row.status_date, @@ -241,8 +241,7 @@ def _scheme_milestone_to_domain(row: Row[Any]) -> MilestoneRevision: @staticmethod def _capital_scheme_financial_to_domain(row: Row[Any]) -> FinancialRevision: return FinancialRevision( - effective_date_from=row.effective_date_from, - effective_date_to=row.effective_date_to, + effective=DateRange(row.effective_date_from, row.effective_date_to), type=FINANCIAL_TYPE_MAPPER.to_domain(row.financial_type_id), amount=row.amount, source=DATA_SOURCE_MAPPER.to_domain(row.data_source_id), diff --git a/schemes/schemes/views.py b/schemes/schemes/views.py index 6edc36c9..0ed4cb90 100644 --- a/schemes/schemes/views.py +++ b/schemes/schemes/views.py @@ -13,6 +13,7 @@ from schemes.authorities.services import AuthorityRepository from schemes.schemes.domain import ( DataSource, + DateRange, FinancialRevision, FinancialType, FundingProgramme, @@ -238,8 +239,10 @@ class MilestoneRevisionRepr: def to_domain(self) -> MilestoneRevision: return MilestoneRevision( - effective_date_from=date.fromisoformat(self.effective_date_from), - effective_date_to=date.fromisoformat(self.effective_date_to) if self.effective_date_to else None, + effective=DateRange( + date_from=date.fromisoformat(self.effective_date_from), + date_to=date.fromisoformat(self.effective_date_to) if self.effective_date_to else None, + ), milestone=self._milestone_to_domain(self.milestone), observation_type=self._observation_type_to_domain(self.observation_type), status_date=date.fromisoformat(self.status_date), @@ -279,8 +282,10 @@ class FinancialRevisionRepr: def to_domain(self) -> FinancialRevision: return FinancialRevision( - effective_date_from=date.fromisoformat(self.effective_date_from), - effective_date_to=date.fromisoformat(self.effective_date_to) if self.effective_date_to else None, + effective=DateRange( + date_from=date.fromisoformat(self.effective_date_from), + date_to=date.fromisoformat(self.effective_date_to) if self.effective_date_to else None, + ), type=self._financial_type_to_domain(self.type), amount=Decimal(self.amount), source=self._data_source_to_domain(self.source), diff --git a/tests/integration/test_authorities.py b/tests/integration/test_authorities.py index 47d0e304..b93fcded 100644 --- a/tests/integration/test_authorities.py +++ b/tests/integration/test_authorities.py @@ -11,6 +11,7 @@ from schemes.authorities.services import AuthorityRepository from schemes.schemes.domain import ( DataSource, + DateRange, FinancialRevision, FinancialType, FundingProgramme, @@ -160,8 +161,7 @@ def test_add_schemes_milestone_revisions(self, schemes: SchemeRepository, client assert scheme1.id == 1 assert scheme1.milestone_revisions == [ MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -195,8 +195,7 @@ def test_add_schemes_financial_revisions(self, schemes: SchemeRepository, client assert scheme1.id == 1 assert scheme1.financial_revisions == [ FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, diff --git a/tests/integration/test_scheme.py b/tests/integration/test_scheme.py index 38b8cd17..e23296ba 100644 --- a/tests/integration/test_scheme.py +++ b/tests/integration/test_scheme.py @@ -10,6 +10,7 @@ from schemes.authorities.services import AuthorityRepository from schemes.schemes.domain import ( DataSource, + DateRange, FinancialRevision, FinancialType, FundingProgramme, @@ -83,8 +84,7 @@ def test_scheme_shows_overview(schemes: SchemeRepository, client: FlaskClient) - scheme.funding_programme = FundingProgramme.ATF4 scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -119,8 +119,7 @@ def test_scheme_shows_funding(schemes: SchemeRepository, client: FlaskClient) -> scheme = Scheme(id_=1, name="Wirral Package", authority_id=1) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -128,8 +127,7 @@ def test_scheme_shows_funding(schemes: SchemeRepository, client: FlaskClient) -> ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("50000"), source=DataSource.ATF4_BID, @@ -137,8 +135,7 @@ def test_scheme_shows_funding(schemes: SchemeRepository, client: FlaskClient) -> ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -160,8 +157,7 @@ def test_scheme_shows_zero_funding(schemes: SchemeRepository, client: FlaskClien scheme = Scheme(id_=1, name="Wirral Package", authority_id=1) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("0"), source=DataSource.ATF4_BID, @@ -169,8 +165,7 @@ def test_scheme_shows_zero_funding(schemes: SchemeRepository, client: FlaskClien ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("0"), source=DataSource.ATF4_BID, @@ -178,8 +173,7 @@ def test_scheme_shows_zero_funding(schemes: SchemeRepository, client: FlaskClien ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("0"), source=DataSource.CHANGE_CONTROL, diff --git a/tests/unit/schemes/test_domain.py b/tests/unit/schemes/test_domain.py index a8177558..343e0a2a 100644 --- a/tests/unit/schemes/test_domain.py +++ b/tests/unit/schemes/test_domain.py @@ -5,6 +5,7 @@ from schemes.schemes.domain import ( DataSource, + DateRange, FinancialRevision, FinancialType, Milestone, @@ -24,8 +25,7 @@ def test_get_milestone_revisions_is_copy(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -41,8 +41,7 @@ def test_update_milestone(self) -> None: scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -51,8 +50,7 @@ def test_update_milestone(self) -> None: assert scheme.milestone_revisions == [ MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -63,8 +61,7 @@ def test_get_current_milestone_selects_actual_observation_type(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.CONSTRUCTION_STARTED, observation_type=ObservationType.PLANNED, status_date=date(2020, 1, 1), @@ -72,8 +69,7 @@ def test_get_current_milestone_selects_actual_observation_type(self) -> None: ) scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -86,8 +82,7 @@ def test_get_current_milestone_selects_latest_milestone(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -95,8 +90,7 @@ def test_get_current_milestone_selects_latest_milestone(self) -> None: ) scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.CONSTRUCTION_STARTED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -109,8 +103,7 @@ def test_get_current_milestone_selects_latest_revision(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 2, 1), + effective=DateRange(date(2020, 1, 1), date(2020, 2, 1)), milestone=Milestone.CONSTRUCTION_STARTED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -128,8 +121,7 @@ def test_get_financial_revisions_is_copy(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -145,8 +137,7 @@ def test_update_financial(self) -> None: scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -155,8 +146,7 @@ def test_update_financial(self) -> None: assert scheme.financial_revisions == [ FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -167,8 +157,7 @@ def test_get_funding_allocation_sums_amounts(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -176,8 +165,7 @@ def test_get_funding_allocation_sums_amounts(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("200000"), source=DataSource.ATF4E_BID, @@ -190,8 +178,7 @@ def test_get_funding_allocation_selects_financial_type(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -199,8 +186,7 @@ def test_get_funding_allocation_selects_financial_type(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.EXPECTED_COST, amount=Decimal("200000"), source=DataSource.ATF4_BID, @@ -213,8 +199,7 @@ def test_get_funding_allocation_selects_latest_revision(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 1, 31), + effective=DateRange(date(2020, 1, 1), date(2020, 1, 31)), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -222,8 +207,7 @@ def test_get_funding_allocation_selects_latest_revision(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("200000"), source=DataSource.ATF4_BID, @@ -236,8 +220,7 @@ def test_get_funding_allocation_when_no_matching_revisions(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.EXPECTED_COST, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -255,8 +238,7 @@ def test_get_spend_to_date_sums_amounts(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -264,8 +246,7 @@ def test_get_spend_to_date_sums_amounts(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("200000"), source=DataSource.ATF4E_BID, @@ -278,8 +259,7 @@ def test_get_spend_to_date_selects_financial_type(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -287,8 +267,7 @@ def test_get_spend_to_date_selects_financial_type(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.EXPECTED_COST, amount=Decimal("200000"), source=DataSource.ATF4_BID, @@ -301,8 +280,7 @@ def test_get_spend_to_date_selects_latest_revision(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 1, 31), + effective=DateRange(date(2020, 1, 1), date(2020, 1, 31)), type=FinancialType.SPENT_TO_DATE, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -310,8 +288,7 @@ def test_get_spend_to_date_selects_latest_revision(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("200000"), source=DataSource.ATF4_BID, @@ -324,8 +301,7 @@ def test_get_spend_to_date_when_no_matching_revisions(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.EXPECTED_COST, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -343,8 +319,7 @@ def test_get_change_control_adjustment_sums_amounts(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -352,8 +327,7 @@ def test_get_change_control_adjustment_sums_amounts(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("20000"), source=DataSource.CHANGE_CONTROL, @@ -366,8 +340,7 @@ def test_get_change_control_adjustment_selects_source(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -375,8 +348,7 @@ def test_get_change_control_adjustment_selects_source(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("20000"), source=DataSource.ATF4_BID, @@ -389,8 +361,7 @@ def test_get_change_control_adjustment_selects_latest_revision(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 1, 31), + effective=DateRange(date(2020, 1, 1), date(2020, 1, 31)), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -398,8 +369,7 @@ def test_get_change_control_adjustment_selects_latest_revision(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("20000"), source=DataSource.CHANGE_CONTROL, @@ -412,8 +382,7 @@ def test_get_change_control_adjustment_when_no_matching_revisions(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("10000"), source=DataSource.ATF4_BID, @@ -431,8 +400,7 @@ def test_get_allocation_still_to_spend(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -440,8 +408,7 @@ def test_get_allocation_still_to_spend(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("50000"), source=DataSource.ATF4_BID, @@ -449,8 +416,7 @@ def test_get_allocation_still_to_spend(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -463,8 +429,7 @@ def test_get_allocation_still_to_spend_when_no_funding_allocation(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("50000"), source=DataSource.ATF4_BID, @@ -472,8 +437,7 @@ def test_get_allocation_still_to_spend_when_no_funding_allocation(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -486,8 +450,7 @@ def test_get_allocation_still_to_spend_when_no_spend_to_date(self) -> None: scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -495,8 +458,7 @@ def test_get_allocation_still_to_spend_when_no_spend_to_date(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -509,8 +471,7 @@ def test_get_allocation_still_to_spend_when_no_change_control_adjustment(self) - scheme = Scheme(id_=1, name="Wirral Package", authority_id=2) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -518,8 +479,7 @@ def test_get_allocation_still_to_spend_when_no_change_control_adjustment(self) - ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("50000"), source=DataSource.ATF4_BID, @@ -534,59 +494,14 @@ def test_get_allocation_still_to_spend_when_no_revisions(self) -> None: assert scheme.allocation_still_to_spend == Decimal("0") -class TestMilestoneRevision: +class TestDateRange: @pytest.mark.parametrize( - "effective_date_from, effective_date_to", + "date_from, date_to", [(date(2020, 1, 1), date(2020, 1, 31)), (date(2020, 1, 31), date(2020, 1, 31)), (date(2020, 1, 1), None)], ) - def test_effective_date_from_before_or_equal_to_effective_date_to( - self, effective_date_from: date, effective_date_to: date | None - ) -> None: - MilestoneRevision( - effective_date_from=effective_date_from, - effective_date_to=effective_date_to, - milestone=Milestone.DETAILED_DESIGN_COMPLETED, - observation_type=ObservationType.ACTUAL, - status_date=date(2020, 1, 1), - ) - - def test_effective_date_from_after_effective_date_to_errors(self) -> None: - with pytest.raises( - ValueError, match="Effective date from '2020-01-01' must not be after effective date to '2019-12-31'" - ): - MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2019, 12, 31), - milestone=Milestone.DETAILED_DESIGN_COMPLETED, - observation_type=ObservationType.ACTUAL, - status_date=date(2020, 1, 1), - ) - + def test_date_from_before_or_equal_to_date_to(self, date_from: date, date_to: date | None) -> None: + DateRange(date_from, date_to) -class TestFinancialRevision: - @pytest.mark.parametrize( - "effective_date_from, effective_date_to", - [(date(2020, 1, 1), date(2020, 1, 31)), (date(2020, 1, 31), date(2020, 1, 31)), (date(2020, 1, 1), None)], - ) - def test_effective_date_from_before_or_equal_to_effective_date_to( - self, effective_date_from: date, effective_date_to: date | None - ) -> None: - FinancialRevision( - effective_date_from=effective_date_from, - effective_date_to=effective_date_to, - type=FinancialType.FUNDING_ALLOCATION, - amount=Decimal("100000"), - source=DataSource.ATF4_BID, - ) - - def test_effective_date_from_after_effective_date_to_errors(self) -> None: - with pytest.raises( - ValueError, match="Effective date from '2020-01-01' must not be after effective date to '2019-12-31'" - ): - FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2019, 12, 31), - type=FinancialType.FUNDING_ALLOCATION, - amount=Decimal("100000"), - source=DataSource.ATF4_BID, - ) + def test_date_from_after_date_to_errors(self) -> None: + with pytest.raises(ValueError, match="From date '2020-01-01' must not be after to date '2019-12-31'"): + DateRange(date(2020, 1, 1), date(2019, 12, 31)) diff --git a/tests/unit/schemes/test_services.py b/tests/unit/schemes/test_services.py index 5140c4aa..8237687b 100644 --- a/tests/unit/schemes/test_services.py +++ b/tests/unit/schemes/test_services.py @@ -9,6 +9,7 @@ from schemes.authorities.services import add_tables as authorities_add_tables from schemes.schemes.domain import ( DataSource, + DateRange, FinancialRevision, FinancialType, FundingProgramme, @@ -91,8 +92,7 @@ def test_add_schemes_milestone_revisions( scheme1 = Scheme(id_=1, name="Wirral Package", authority_id=1) scheme1.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 1, 31), + effective=DateRange(date(2020, 1, 1), date(2020, 1, 31)), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.PLANNED, status_date=date(2020, 2, 1), @@ -100,8 +100,7 @@ def test_add_schemes_milestone_revisions( ) scheme1.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.PLANNED, status_date=date(2020, 3, 1), @@ -138,8 +137,7 @@ def test_add_schemes_financial_revisions( scheme1 = Scheme(id_=1, name="Wirral Package", authority_id=1) scheme1.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 1, 31), + effective=DateRange(date(2020, 1, 1), date(2020, 1, 31)), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -147,8 +145,7 @@ def test_add_schemes_financial_revisions( ) scheme1.update_financial( FinancialRevision( - effective_date_from=date(2020, 2, 1), - effective_date_to=None, + effective=DateRange(date(2020, 2, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("200000"), source=DataSource.ATF4_BID, @@ -243,15 +240,15 @@ def test_get_scheme_milestone_revisions( milestone_revision2: MilestoneRevision milestone_revision1, milestone_revision2 = actual.milestone_revisions assert ( - milestone_revision1.effective_date_from == date(2020, 1, 1) - and milestone_revision1.effective_date_to == date(2020, 1, 31) + milestone_revision1.effective.date_from == date(2020, 1, 1) + and milestone_revision1.effective.date_to == date(2020, 1, 31) and milestone_revision1.milestone == Milestone.DETAILED_DESIGN_COMPLETED and milestone_revision1.observation_type == ObservationType.PLANNED and milestone_revision1.status_date == date(2020, 2, 1) ) assert ( - milestone_revision2.effective_date_from == date(2020, 2, 1) - and milestone_revision2.effective_date_to is None + milestone_revision2.effective.date_from == date(2020, 2, 1) + and milestone_revision2.effective.date_to is None and milestone_revision2.milestone == Milestone.DETAILED_DESIGN_COMPLETED and milestone_revision2.observation_type == ObservationType.PLANNED and milestone_revision2.status_date == date(2020, 3, 1) @@ -296,15 +293,15 @@ def test_get_scheme_financial_revisions( financial_revision2: FinancialRevision financial_revision1, financial_revision2 = actual.financial_revisions assert ( - financial_revision1.effective_date_from == date(2020, 1, 1) - and financial_revision1.effective_date_to == date(2020, 1, 31) + financial_revision1.effective.date_from == date(2020, 1, 1) + and financial_revision1.effective.date_to == date(2020, 1, 31) and financial_revision1.type == FinancialType.FUNDING_ALLOCATION and financial_revision1.amount == Decimal("100000") and financial_revision1.source == DataSource.ATF4_BID ) assert ( - financial_revision2.effective_date_from == date(2020, 2, 1) - and financial_revision2.effective_date_to is None + financial_revision2.effective.date_from == date(2020, 2, 1) + and financial_revision2.effective.date_to is None and financial_revision2.type == FinancialType.FUNDING_ALLOCATION and financial_revision2.amount == Decimal("200000") and financial_revision2.source == DataSource.ATF4_BID @@ -420,8 +417,8 @@ def test_get_all_schemes_milestone_revisions_by_authority( milestone_revision1: MilestoneRevision (milestone_revision1,) = actual1.milestone_revisions assert ( - milestone_revision1.effective_date_from == date(2020, 1, 1) - and milestone_revision1.effective_date_to is None + milestone_revision1.effective.date_from == date(2020, 1, 1) + and milestone_revision1.effective.date_to is None and milestone_revision1.milestone == Milestone.DETAILED_DESIGN_COMPLETED and milestone_revision1.observation_type == ObservationType.PLANNED and milestone_revision1.status_date == date(2020, 2, 1) @@ -430,8 +427,8 @@ def test_get_all_schemes_milestone_revisions_by_authority( milestone_revision2: MilestoneRevision (milestone_revision2,) = actual2.milestone_revisions assert ( - milestone_revision2.effective_date_from == date(2020, 2, 1) - and milestone_revision2.effective_date_to is None + milestone_revision2.effective.date_from == date(2020, 2, 1) + and milestone_revision2.effective.date_to is None and milestone_revision2.milestone == Milestone.DETAILED_DESIGN_COMPLETED and milestone_revision2.observation_type == ObservationType.PLANNED and milestone_revision2.status_date == date(2020, 3, 1) @@ -479,8 +476,8 @@ def test_get_all_schemes_financial_revisions_by_authority( financial_revision1: FinancialRevision (financial_revision1,) = actual1.financial_revisions assert ( - financial_revision1.effective_date_from == date(2020, 1, 1) - and financial_revision1.effective_date_to is None + financial_revision1.effective.date_from == date(2020, 1, 1) + and financial_revision1.effective.date_to is None and financial_revision1.type == FinancialType.FUNDING_ALLOCATION and financial_revision1.amount == Decimal("100000") and financial_revision1.source == DataSource.ATF4_BID @@ -560,8 +557,8 @@ def test_get_all_schemes_milestone_revisions( milestone_revision1: MilestoneRevision (milestone_revision1,) = actual1.milestone_revisions assert ( - milestone_revision1.effective_date_from == date(2020, 1, 1) - and milestone_revision1.effective_date_to is None + milestone_revision1.effective.date_from == date(2020, 1, 1) + and milestone_revision1.effective.date_to is None and milestone_revision1.milestone == Milestone.DETAILED_DESIGN_COMPLETED and milestone_revision1.observation_type == ObservationType.PLANNED and milestone_revision1.status_date == date(2020, 2, 1) @@ -570,8 +567,8 @@ def test_get_all_schemes_milestone_revisions( milestone_revision2: MilestoneRevision (milestone_revision2,) = actual2.milestone_revisions assert ( - milestone_revision2.effective_date_from == date(2020, 2, 1) - and milestone_revision2.effective_date_to is None + milestone_revision2.effective.date_from == date(2020, 2, 1) + and milestone_revision2.effective.date_to is None and milestone_revision2.milestone == Milestone.DETAILED_DESIGN_COMPLETED and milestone_revision2.observation_type == ObservationType.PLANNED and milestone_revision2.status_date == date(2020, 3, 1) @@ -620,8 +617,8 @@ def test_get_all_schemes_financial_revisions( financial_revision1: FinancialRevision (financial_revision1,) = actual1.financial_revisions assert ( - financial_revision1.effective_date_from == date(2020, 1, 1) - and financial_revision1.effective_date_to is None + financial_revision1.effective.date_from == date(2020, 1, 1) + and financial_revision1.effective.date_to is None and financial_revision1.type == FinancialType.FUNDING_ALLOCATION and financial_revision1.amount == Decimal("100000") and financial_revision1.source == DataSource.ATF4_BID @@ -630,8 +627,8 @@ def test_get_all_schemes_financial_revisions( financial_revision2: FinancialRevision (financial_revision2,) = actual2.financial_revisions assert ( - financial_revision2.effective_date_from == date(2020, 2, 1) - and financial_revision2.effective_date_to is None + financial_revision2.effective.date_from == date(2020, 2, 1) + and financial_revision2.effective.date_to is None and financial_revision2.type == FinancialType.FUNDING_ALLOCATION and financial_revision2.amount == Decimal("200000") and financial_revision2.source == DataSource.ATF4_BID diff --git a/tests/unit/schemes/test_views.py b/tests/unit/schemes/test_views.py index 449fff90..81a1ffce 100644 --- a/tests/unit/schemes/test_views.py +++ b/tests/unit/schemes/test_views.py @@ -6,6 +6,7 @@ from schemes.authorities.domain import Authority from schemes.schemes.domain import ( DataSource, + DateRange, FinancialRevision, FinancialType, FundingProgramme, @@ -123,8 +124,7 @@ def test_set_current_milestone(self, milestone: Milestone, expected_milestone: s scheme = Scheme(id_=0, name="", authority_id=0) scheme.update_milestone( MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=milestone, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -169,8 +169,7 @@ def test_set_funding_allocation(self) -> None: scheme = Scheme(id_=0, name="", authority_id=0) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -185,8 +184,7 @@ def test_set_spend_to_date(self) -> None: scheme = Scheme(id_=0, name="", authority_id=0) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("50000"), source=DataSource.ATF4_BID, @@ -201,8 +199,7 @@ def test_set_change_control_adjustment(self) -> None: scheme = Scheme(id_=0, name="", authority_id=0) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.CHANGE_CONTROL_FUNDING_REALLOCATION, amount=Decimal("10000"), source=DataSource.CHANGE_CONTROL, @@ -217,8 +214,7 @@ def test_set_allocation_still_to_spend(self) -> None: scheme = Scheme(id_=0, name="", authority_id=0) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("110000"), source=DataSource.ATF4_BID, @@ -226,8 +222,7 @@ def test_set_allocation_still_to_spend(self) -> None: ) scheme.update_financial( FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.SPENT_TO_DATE, amount=Decimal("50000"), source=DataSource.ATF4_BID, @@ -307,15 +302,13 @@ def test_set_milestone_revisions(self) -> None: assert scheme.milestone_revisions == [ MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), ), MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), milestone=Milestone.CONSTRUCTION_STARTED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 2, 1), @@ -348,15 +341,13 @@ def test_set_financial_revisions(self) -> None: assert scheme.financial_revisions == [ FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, ), FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=None, + effective=DateRange(date(2020, 1, 1), None), type=FinancialType.EXPECTED_COST, amount=Decimal("200000"), source=DataSource.PULSE_6, @@ -377,8 +368,7 @@ def test_create_domain(self) -> None: milestone_revision = milestone_revision_repr.to_domain() assert milestone_revision == MilestoneRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 1, 31), + effective=DateRange(date(2020, 1, 1), date(2020, 1, 31)), milestone=Milestone.DETAILED_DESIGN_COMPLETED, observation_type=ObservationType.ACTUAL, status_date=date(2020, 1, 1), @@ -395,7 +385,7 @@ def test_set_effective_date_to_when_missing(self) -> None: milestone_revision = milestone_revision_repr.to_domain() - assert milestone_revision.effective_date_to is None + assert milestone_revision.effective.date_to is None @pytest.mark.parametrize( "milestone, expected_milestone", @@ -460,8 +450,7 @@ def test_create_domain(self) -> None: financial_revision = financial_revision_repr.to_domain() assert financial_revision == FinancialRevision( - effective_date_from=date(2020, 1, 1), - effective_date_to=date(2020, 1, 31), + effective=DateRange(date(2020, 1, 1), date(2020, 1, 31)), type=FinancialType.FUNDING_ALLOCATION, amount=Decimal("100000"), source=DataSource.ATF4_BID, @@ -478,7 +467,7 @@ def test_set_effective_date_to_when_missing(self) -> None: financial_revision = financial_revision_repr.to_domain() - assert financial_revision.effective_date_to is None + assert financial_revision.effective.date_to is None @pytest.mark.parametrize( "financial_type, expected_financial_type",