mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-08-24 22:45:18 +02:00
Merge branch 'dev' into feature/payment-frequency-per-member
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
"version": "0.2.0",
|
||||
"date": "upcoming",
|
||||
"changes": [
|
||||
"Mitgliedsanträge und Mitgliederakten enthalten jetzt ein separates Feld für das Antragsdatum.",
|
||||
"Eigentum und aktueller Gewahrsam von Assets werden getrennt dokumentiert; neben Vereinseigentum kann nun auch Eigentum von Mitgliedern oder externen Personen zentral erfasst werden.",
|
||||
"Mitgliedseigentum wird per UUID mit der Mitgliedsakte verknüpft und erscheint dort gemeinsam mit den vom Verein erhaltenen Assets in getrennt verständlichen Ansichten.",
|
||||
"Standort, Übergabedatum, Zustand und Wert eines Assets können für Übergabe- und Versicherungsnachweise festgehalten werden; Eigentumsänderungen erscheinen zusätzlich in der Chronik.",
|
||||
|
||||
@@ -95,6 +95,7 @@ class Member:
|
||||
mandate_active: bool = False
|
||||
mandate_revoked_at: str = ""
|
||||
status: str = "application"
|
||||
application_date: str = ""
|
||||
accepted_at: str = ""
|
||||
membership_started_at: str = ""
|
||||
membership_ended_at: str = ""
|
||||
@@ -141,6 +142,7 @@ class Member:
|
||||
},
|
||||
"membership": {
|
||||
"status": self.status,
|
||||
"application_date": self.application_date,
|
||||
"accepted_at": self.accepted_at,
|
||||
"started_at": self.membership_started_at,
|
||||
"ended_at": self.membership_ended_at,
|
||||
@@ -185,6 +187,7 @@ class Member:
|
||||
mandate_active=bool(banking.get("mandate_active", False)),
|
||||
mandate_revoked_at=str(banking.get("mandate_revoked_at", "")),
|
||||
status=str(membership.get("status", "application")),
|
||||
application_date=str(membership.get("application_date", "")),
|
||||
accepted_at=str(membership.get("accepted_at", "")),
|
||||
membership_started_at=str(membership.get("started_at", "")),
|
||||
membership_ended_at=str(membership.get("ended_at", "")),
|
||||
|
||||
@@ -295,6 +295,7 @@ class MemberRepository:
|
||||
email: str = "",
|
||||
phone: str = "",
|
||||
birth_date: str = "",
|
||||
application_date: str = "",
|
||||
member_number: str = "",
|
||||
) -> Member:
|
||||
with self._member_number_lock():
|
||||
@@ -305,6 +306,7 @@ class MemberRepository:
|
||||
email=email,
|
||||
phone=phone,
|
||||
birth_date=birth_date,
|
||||
application_date=application_date,
|
||||
member_number=member_number,
|
||||
)
|
||||
|
||||
@@ -317,12 +319,14 @@ class MemberRepository:
|
||||
email: str,
|
||||
phone: str,
|
||||
birth_date: str,
|
||||
application_date: str,
|
||||
member_number: str,
|
||||
) -> Member:
|
||||
if not first_name.strip() or not last_name.strip():
|
||||
raise RepositoryError("Vorname und Nachname sind erforderlich.")
|
||||
try:
|
||||
birth_date = normalize_date_input(birth_date, "Geburtsdatum")
|
||||
application_date = normalize_date_input(application_date, "Antragsdatum")
|
||||
validate_member_dates(birth_date=birth_date)
|
||||
except DateValidationError as exc:
|
||||
raise RepositoryError(str(exc)) from exc
|
||||
@@ -349,6 +353,7 @@ class MemberRepository:
|
||||
email=email.strip(),
|
||||
phone=phone.strip(),
|
||||
birth_date=birth_date,
|
||||
application_date=application_date,
|
||||
)
|
||||
write_json_atomic(directory / "member.json", member.to_dict())
|
||||
write_json_atomic(directory / "contributions.json", ContributionData().to_dict())
|
||||
@@ -365,6 +370,7 @@ class MemberRepository:
|
||||
existing = self.get_member(member.member_id)
|
||||
try:
|
||||
member.birth_date = normalize_date_input(member.birth_date, "Geburtsdatum")
|
||||
member.application_date = normalize_date_input(member.application_date, "Antragsdatum")
|
||||
member.accepted_at = normalize_date_input(member.accepted_at, "Aufnahmebeschluss")
|
||||
member.membership_started_at = normalize_date_input(member.membership_started_at, "Mitglied seit")
|
||||
member.membership_ended_at = normalize_date_input(member.membership_ended_at, "Austrittsdatum")
|
||||
@@ -1892,6 +1898,7 @@ class MemberRepository:
|
||||
"email": "E-Mail-Adresse",
|
||||
"phone": "Telefonnummer",
|
||||
"birth_date": "Geburtsdatum",
|
||||
"application_date": "Antragsdatum",
|
||||
"membership_ended_at": "Austrittsdatum",
|
||||
"status": "Status",
|
||||
"payment_frequency": "Zahlungsweise",
|
||||
|
||||
@@ -41,6 +41,7 @@ class NewMemberDialog(tk.Toplevel):
|
||||
"email",
|
||||
"phone",
|
||||
"birth_date",
|
||||
"application_date",
|
||||
"member_number",
|
||||
)
|
||||
}
|
||||
@@ -63,6 +64,7 @@ class NewMemberDialog(tk.Toplevel):
|
||||
("E-Mail-Adresse", "email"),
|
||||
("Telefonnummer", "phone"),
|
||||
(f"Geburtsdatum ({date_input_hint()})", "birth_date"),
|
||||
(f"Antragsdatum ({date_input_hint()})", "application_date"),
|
||||
]
|
||||
if self.number_policy["mode"] == "manual":
|
||||
fields.append(("Mitgliedsnummer *", "member_number"))
|
||||
|
||||
@@ -170,6 +170,7 @@ class MemberTab(ttk.Frame):
|
||||
("E-Mail-Adresse", "email"),
|
||||
("Telefonnummer", "phone"),
|
||||
(f"Geburtsdatum ({date_input_hint()})", "birth_date"),
|
||||
(f"Antragsdatum ({date_input_hint()})", "application_date"),
|
||||
(f"Aufnahmebeschluss ({date_input_hint()})", "accepted_at"),
|
||||
(f"Mitglied seit ({date_input_hint()})", "membership_started_at"),
|
||||
(f"Austrittsdatum ({date_input_hint()})", "membership_ended_at"),
|
||||
@@ -475,6 +476,7 @@ class MemberTab(ttk.Frame):
|
||||
)
|
||||
date_fields = {
|
||||
"birth_date",
|
||||
"application_date",
|
||||
"accepted_at",
|
||||
"membership_started_at",
|
||||
"membership_ended_at",
|
||||
|
||||
@@ -25,6 +25,7 @@ def test_repository_creates_transparent_member_record(tmp_path) -> None:
|
||||
nickname="Enchantress",
|
||||
email="ada@example.org",
|
||||
birth_date="1990-12-10",
|
||||
application_date="30.07.2026",
|
||||
member_number="0042",
|
||||
)
|
||||
|
||||
@@ -38,6 +39,7 @@ def test_repository_creates_transparent_member_record(tmp_path) -> None:
|
||||
raw = json.loads((member_dir / "member.json").read_text(encoding="utf-8"))
|
||||
assert raw["person"]["first_name"] == "Ada"
|
||||
assert raw["person"]["nickname"] == "Enchantress"
|
||||
assert raw["membership"]["application_date"] == "2026-07-30"
|
||||
assert raw["schema_version"] == 1
|
||||
assert raw["membership"]["ended_at"] == ""
|
||||
assert raw["contribution_profile"]["payment_frequency"] == "semiannual"
|
||||
@@ -144,6 +146,18 @@ def test_repository_accepts_local_date_input_and_rejects_invalid_dates(tmp_path)
|
||||
repository.create_member(first_name="Invalid", last_name="Date", birth_date="31.02.2000")
|
||||
|
||||
|
||||
def test_existing_member_without_application_date_stays_empty(tmp_path) -> None:
|
||||
repository = MemberRepository(tmp_path)
|
||||
repository.initialize()
|
||||
member = repository.create_member(first_name="Legacy", last_name="Member")
|
||||
path = repository.members_root / member.member_id / "member.json"
|
||||
raw = json.loads(path.read_text(encoding="utf-8"))
|
||||
raw["membership"].pop("application_date")
|
||||
path.write_text(json.dumps(raw), encoding="utf-8")
|
||||
|
||||
assert repository.get_member(member.member_id).application_date == ""
|
||||
|
||||
|
||||
def test_repository_reports_empty_contributions_file(tmp_path) -> None:
|
||||
repository = MemberRepository(tmp_path)
|
||||
repository.initialize()
|
||||
|
||||
Reference in New Issue
Block a user