feat: add status and payment capability contracts
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from datetime import datetime
|
||||
from typing import Protocol, runtime_checkable
|
||||
|
||||
|
||||
CAPABILITY_APPLICATION_STATUS_PROJECTION = "application_status.projection"
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class ApplicationStatusProjectionProvider(Protocol):
|
||||
"""Bounded applicant-status access without exposing the owning module's data."""
|
||||
|
||||
def tenant_id_for_tracking_id(
|
||||
self,
|
||||
session: object,
|
||||
*,
|
||||
tracking_id: str,
|
||||
) -> str | None:
|
||||
...
|
||||
|
||||
def public_access_challenge(
|
||||
self,
|
||||
session: object,
|
||||
*,
|
||||
tracking_id: str,
|
||||
) -> Mapping[str, object]:
|
||||
...
|
||||
|
||||
def get_authenticated_projection(
|
||||
self,
|
||||
session: object,
|
||||
principal: object,
|
||||
*,
|
||||
tracking_id: str,
|
||||
observed_at: datetime,
|
||||
) -> Mapping[str, object]:
|
||||
...
|
||||
|
||||
def get_public_projection(
|
||||
self,
|
||||
session: object,
|
||||
*,
|
||||
tracking_id: str,
|
||||
token: str | None,
|
||||
observed_at: datetime,
|
||||
) -> Mapping[str, object]:
|
||||
...
|
||||
|
||||
def request_email_link(
|
||||
self,
|
||||
session: object,
|
||||
*,
|
||||
tracking_id: str,
|
||||
email: str,
|
||||
requested_at: datetime,
|
||||
) -> bool:
|
||||
...
|
||||
|
||||
|
||||
def application_status_projection_provider(
|
||||
registry: object | None,
|
||||
) -> ApplicationStatusProjectionProvider | None:
|
||||
if registry is None or not hasattr(registry, "has_capability"):
|
||||
return None
|
||||
if not registry.has_capability(CAPABILITY_APPLICATION_STATUS_PROJECTION):
|
||||
return None
|
||||
capability = registry.capability(CAPABILITY_APPLICATION_STATUS_PROJECTION)
|
||||
return (
|
||||
capability
|
||||
if isinstance(capability, ApplicationStatusProjectionProvider)
|
||||
else None
|
||||
)
|
||||
|
||||
|
||||
__all__ = [
|
||||
"ApplicationStatusProjectionProvider",
|
||||
"CAPABILITY_APPLICATION_STATUS_PROJECTION",
|
||||
"application_status_projection_provider",
|
||||
]
|
||||
@@ -0,0 +1,87 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Protocol, runtime_checkable
|
||||
|
||||
from govoplan_core.core.institutional import EvidenceReference
|
||||
|
||||
|
||||
CAPABILITY_PAYMENT_REQUESTS = "payments.requests"
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class PaymentRequestCommand:
|
||||
tenant_id: str
|
||||
source_module: str
|
||||
source_resource_type: str
|
||||
source_resource_id: str
|
||||
amount_minor: int
|
||||
currency: str
|
||||
subject: str
|
||||
idempotency_key: str
|
||||
requested_at: datetime
|
||||
requested_by_ref: str
|
||||
due_at: datetime | None = None
|
||||
context_refs: Mapping[str, str] = field(default_factory=dict)
|
||||
metadata: Mapping[str, object] = field(default_factory=dict)
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class ManualPaymentReconciliationCommand:
|
||||
tenant_id: str
|
||||
payment_id: str
|
||||
amount_minor: int
|
||||
currency: str
|
||||
transaction_reference: str
|
||||
evidence_ref: EvidenceReference
|
||||
idempotency_key: str
|
||||
received_at: datetime
|
||||
recorded_at: datetime
|
||||
recorded_by_ref: str
|
||||
metadata: Mapping[str, object] = field(default_factory=dict)
|
||||
|
||||
|
||||
@runtime_checkable
|
||||
class PaymentRequestProvider(Protocol):
|
||||
def request_payment(
|
||||
self,
|
||||
session: object,
|
||||
command: PaymentRequestCommand,
|
||||
) -> Mapping[str, object]:
|
||||
...
|
||||
|
||||
def get_payment(
|
||||
self,
|
||||
session: object,
|
||||
*,
|
||||
tenant_id: str,
|
||||
payment_id: str,
|
||||
) -> Mapping[str, object] | None:
|
||||
...
|
||||
|
||||
def reconcile_manual_payment(
|
||||
self,
|
||||
session: object,
|
||||
command: ManualPaymentReconciliationCommand,
|
||||
) -> Mapping[str, object]:
|
||||
...
|
||||
|
||||
|
||||
def payment_request_provider(registry: object | None) -> PaymentRequestProvider | None:
|
||||
if registry is None or not hasattr(registry, "has_capability"):
|
||||
return None
|
||||
if not registry.has_capability(CAPABILITY_PAYMENT_REQUESTS):
|
||||
return None
|
||||
capability = registry.capability(CAPABILITY_PAYMENT_REQUESTS)
|
||||
return capability if isinstance(capability, PaymentRequestProvider) else None
|
||||
|
||||
|
||||
__all__ = [
|
||||
"CAPABILITY_PAYMENT_REQUESTS",
|
||||
"ManualPaymentReconciliationCommand",
|
||||
"PaymentRequestCommand",
|
||||
"PaymentRequestProvider",
|
||||
"payment_request_provider",
|
||||
]
|
||||
Reference in New Issue
Block a user