Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 31 additions & 6 deletions apps/sponsors/use_cases.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
"""Use case classes orchestrating sponsorship business logic with notifications."""
"""Use case classes orchestrating sponsorship business logic with notifications.

Use cases (UCs) are an indirection layer that triggers sponsorship business rules,
keeping core logic and state management out of the views.
"""

from abc import ABC, abstractmethod

from django.db import transaction

Expand All @@ -14,8 +20,11 @@
)


class BaseUseCaseWithNotifications:
"""Base class providing notification dispatch for use case implementations."""
class BaseUseCaseWithNotifications(ABC):
"""Abstract base class for use cases.

Holds the list of notifications the use case dispatches when needed.
"""

notifications = []

Expand All @@ -30,9 +39,16 @@ def notify(self, **kwargs):

@classmethod
def build(cls):
"""Construct the use case with its default notification list."""
"""Construct the use case with its default notification list.

Factory method; override to handle complex setup or dependency injection.
"""
return cls(cls.notifications)

@abstractmethod
def execute(self, *args, **kwargs):
"""Run the use case's business rules."""


class CreateSponsorshipApplicationUseCase(BaseUseCaseWithNotifications):
"""Create a new sponsorship application and notify stakeholders."""
Expand Down Expand Up @@ -118,7 +134,12 @@ def execute(self, contract, **kwargs):


class ExecuteExistingContractUseCase(BaseUseCaseWithNotifications):
"""Execute a contract with an already-signed document file."""
"""Execute a contract using a document the sponsor signed outside the app.

Backs the admin "approve signed sponsorship" action: the contract is created on
approval and executed immediately, bypassing the usual status checks. Any other
active sponsorship of the same sponsor is marked as overlapped by this one.
"""

notifications = [
notifications.ExecutedExistingContractLogger(),
Expand Down Expand Up @@ -146,7 +167,11 @@ def execute(self, contract, contract_file, **kwargs):


class ExecuteContractUseCase(ExecuteExistingContractUseCase):
"""Execute a contract that was previously sent for signature."""
"""Execute a contract that was previously sent for signature.

PSF staff upload the signed contract; this flags the contract as executed
and the corresponding sponsorship as finalized.
"""

notifications = [
notifications.ExecutedContractLogger(),
Expand Down
7 changes: 7 additions & 0 deletions docs/source/administration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,13 @@ they represent:
:contract.py: The `Contract` model which is used to generate the final contract document and other
support models;

Regular users interact with the sponsors app in two places: the application flow
(benefit selection at ``/sponsors/application/``, then the form at
``/sponsors/application/new/``) and the sponsorship dashboard at ``/users/sponsorships/``,
where sponsors view their sponsorships, update sponsor information, and upload required
assets. Everything else is done through the admin action buttons on the ``Sponsorship``
and ``Contract`` models.

Agreements
----------

Expand Down
Loading