mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-09-06 03:20:49 +02:00
Render every outgoing e-mail from an editable text template
The wording of the dunning and SEPA pre-notification mails was hard-coded in
Python, so adjusting a single sentence required a new release. Both texts now
live in plain text templates that ship as defaults, are copied into the store's
templates/mail/ directory on first start and can be edited there or under
Optionen -> E-Mail-Vorlagen; an existing file is never overwritten and a deleted
one is restored from the shipped default.
A template carries its subject in the first line and the body after a blank
line. Placeholders use the same {{ ... }} syntax as the document templates and
share their member/organization values, so a name means the same thing in a
letter and in the mail that carries it. Unknown placeholders are rejected while
editing instead of during a send run, a line holding nothing but placeholders
that render empty is dropped, and {{#claims}} ... {{/claims}} repeats per entry.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
e3807bf7dc
commit
d6cd58a788
@@ -24,7 +24,8 @@ from ccma.domain.contributions import (
|
||||
reminder_items_total,
|
||||
)
|
||||
from ccma.domain.dates import format_date_for_display
|
||||
from ccma.domain.models import MEMBERSHIP_STATUS_LABELS, Member
|
||||
from ccma.domain.models import Member
|
||||
from ccma.domain.placeholders import base_placeholder_values
|
||||
from ccma.storage.repository import MemberRepository
|
||||
|
||||
SUPPORTED_TEMPLATE_SUFFIXES = {".fodt", ".odt", ".ott"}
|
||||
@@ -182,65 +183,14 @@ def _template_values(
|
||||
reminder: dict | None = None,
|
||||
organization: dict | None = None,
|
||||
) -> tuple[dict[str, str], dict[str, list[dict[str, str]]]]:
|
||||
organization = organization or {}
|
||||
created_at = datetime.now().astimezone()
|
||||
created_date = format_date_for_display(created_at.date().isoformat())
|
||||
created_timestamp = created_at.strftime("%d.%m.%Y %H:%M")
|
||||
organization_address = " ".join(
|
||||
part
|
||||
for part in (
|
||||
str(organization.get("street", "")),
|
||||
str(organization.get("postal_code", "")),
|
||||
str(organization.get("city", "")),
|
||||
)
|
||||
if part
|
||||
values = base_placeholder_values(member, organization, created_at=created_at)
|
||||
values.update(
|
||||
{
|
||||
"document.created_date": values["current_date"],
|
||||
"document.created_at": values["current_datetime"],
|
||||
}
|
||||
)
|
||||
values = {
|
||||
"document.created_date": created_date,
|
||||
"document.created_at": created_timestamp,
|
||||
"current_date": created_date,
|
||||
"current_datetime": created_timestamp,
|
||||
"member.id": member.member_id,
|
||||
"member.number": member.member_number,
|
||||
"member.first_name": member.first_name,
|
||||
"member.last_name": member.last_name,
|
||||
"member.nickname": member.nickname,
|
||||
"member.full_name": member.display_name,
|
||||
"member.email": member.email,
|
||||
"member.phone": member.phone,
|
||||
"member.birth_date": format_date_for_display(member.birth_date),
|
||||
"member.status": MEMBERSHIP_STATUS_LABELS.get(member.status, member.status),
|
||||
"member.accepted_at": format_date_for_display(member.accepted_at),
|
||||
"member.started_at": format_date_for_display(member.membership_started_at),
|
||||
"member.ended_at": format_date_for_display(member.membership_ended_at),
|
||||
"member.street": member.street,
|
||||
"member.address_addition": member.address_addition,
|
||||
"member.postal_code": member.postal_code,
|
||||
"member.city": member.city,
|
||||
"member.country": member.country,
|
||||
"member.address_line": " ".join(
|
||||
part for part in (member.street, member.postal_code, member.city) if part
|
||||
),
|
||||
"member.account_holder": member.account_holder,
|
||||
"member.iban": member.iban,
|
||||
"member.bic": member.bic,
|
||||
"member.mandate_reference": member.mandate_reference,
|
||||
"member.mandate_signed_at": format_date_for_display(member.mandate_signed_at),
|
||||
"member.mandate_revoked_at": format_date_for_display(member.mandate_revoked_at),
|
||||
"member.mandate_active": "Ja" if member.mandate_active else "Nein",
|
||||
"organization.name": str(organization.get("name", "")),
|
||||
"organization.street": str(organization.get("street", "")),
|
||||
"organization.postal_code": str(organization.get("postal_code", "")),
|
||||
"organization.city": str(organization.get("city", "")),
|
||||
"organization.country": str(organization.get("country", "")),
|
||||
"organization.address_line": organization_address,
|
||||
"organization.email": str(organization.get("email", "")),
|
||||
"organization.phone": str(organization.get("phone", "")),
|
||||
"organization.website": str(organization.get("website", "")),
|
||||
"organization.iban": str(organization.get("iban", "")),
|
||||
"organization.bic": str(organization.get("bic", "")),
|
||||
"organization.creditor_id": str(organization.get("creditor_id", "")),
|
||||
}
|
||||
repeats: dict[str, list[dict[str, str]]] = {}
|
||||
if claim is not None and data is not None:
|
||||
claim_id = str(claim.get("claim_id", ""))
|
||||
|
||||
Reference in New Issue
Block a user