feat: initialize CCMA member administration

This commit is contained in:
Marcel Peterkau
2026-06-21 16:46:15 +02:00
parent 4c6a1191ee
commit dfd5b1192b
184 changed files with 5051 additions and 0 deletions
+169
View File
@@ -0,0 +1,169 @@
from __future__ import annotations
from dataclasses import dataclass, field
from datetime import date, datetime
from decimal import Decimal
from typing import Any
def _iso_now() -> str:
return datetime.now().astimezone().isoformat(timespec="seconds")
MEMBERSHIP_STATUS_LABELS = {
"application": "ANTRAG",
"accepted_pending_payment": "ANGENOMMEN / ZAHLUNG OFFEN",
"active": "AKTIV",
"suspended_contribution": "RUHEND / BEITRAG",
"resigned_end_of_year": "AUSTRITT ZUM JAHRESENDE",
"honorary": "EHRENMITGLIED",
"ended": "BEENDET",
}
@dataclass(slots=True)
class Member:
member_id: str
member_number: str
first_name: str
last_name: str
email: str = ""
birth_date: str = ""
status: str = "application"
accepted_at: str = ""
membership_started_at: str = ""
payment_frequency: str = "annual"
contribution_rule_id: str = "standard-2022"
honorary: bool = False
notes: str = ""
created_at: str = field(default_factory=_iso_now)
updated_at: str = field(default_factory=_iso_now)
schema_version: int = 1
@property
def display_name(self) -> str:
return " ".join(part for part in (self.first_name, self.last_name) if part).strip()
def to_dict(self) -> dict[str, Any]:
return {
"schema_version": self.schema_version,
"member_id": self.member_id,
"member_number": self.member_number,
"person": {
"first_name": self.first_name,
"last_name": self.last_name,
"birth_date": self.birth_date,
"email": self.email,
},
"membership": {
"status": self.status,
"accepted_at": self.accepted_at,
"started_at": self.membership_started_at,
"honorary": self.honorary,
},
"contribution_profile": {
"rule_id": self.contribution_rule_id,
"payment_frequency": self.payment_frequency,
},
"notes": self.notes,
"created_at": self.created_at,
"updated_at": self.updated_at,
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Member:
person = data.get("person") or {}
membership = data.get("membership") or {}
contribution = data.get("contribution_profile") or {}
return cls(
schema_version=int(data.get("schema_version", 1)),
member_id=str(data["member_id"]),
member_number=str(data.get("member_number", "")),
first_name=str(person.get("first_name", "")),
last_name=str(person.get("last_name", "")),
email=str(person.get("email", "")),
birth_date=str(person.get("birth_date", "")),
status=str(membership.get("status", "application")),
accepted_at=str(membership.get("accepted_at", "")),
membership_started_at=str(membership.get("started_at", "")),
honorary=bool(membership.get("honorary", False)),
contribution_rule_id=str(contribution.get("rule_id", "standard-2022")),
payment_frequency=str(contribution.get("payment_frequency", "annual")),
notes=str(data.get("notes", "")),
created_at=str(data.get("created_at", _iso_now())),
updated_at=str(data.get("updated_at", _iso_now())),
)
@dataclass(slots=True)
class Event:
event_id: str
timestamp: str
event_type: str
summary: str
actor_type: str = "system"
actor_name: str = "CCMA"
references: dict[str, str] = field(default_factory=dict)
data: dict[str, Any] = field(default_factory=dict)
def to_dict(self) -> dict[str, Any]:
return {
"schema_version": 1,
"event_id": self.event_id,
"timestamp": self.timestamp,
"type": self.event_type,
"actor": {"type": self.actor_type, "name": self.actor_name},
"summary": self.summary,
"references": self.references,
"data": self.data,
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> Event:
actor = data.get("actor") or {}
return cls(
event_id=str(data["event_id"]),
timestamp=str(data["timestamp"]),
event_type=str(data.get("type", "unknown")),
summary=str(data.get("summary", "")),
actor_type=str(actor.get("type", "system")),
actor_name=str(actor.get("name", "CCMA")),
references=dict(data.get("references") or {}),
data=dict(data.get("data") or {}),
)
@dataclass(slots=True)
class ContributionData:
claims: list[dict[str, Any]] = field(default_factory=list)
payments: list[dict[str, Any]] = field(default_factory=list)
schema_version: int = 1
def to_dict(self) -> dict[str, Any]:
return {
"schema_version": self.schema_version,
"claims": self.claims,
"payments": self.payments,
}
@classmethod
def from_dict(cls, data: dict[str, Any]) -> ContributionData:
return cls(
schema_version=int(data.get("schema_version", 1)),
claims=list(data.get("claims") or []),
payments=list(data.get("payments") or []),
)
@dataclass(frozen=True, slots=True)
class HousekeeperFinding:
severity: str
member_id: str
code: str
title: str
detail: str
due_date: date | None = None
def money(value: str | int | float | Decimal) -> Decimal:
return Decimal(str(value)).quantize(Decimal("0.01"))