mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-07-01 03:04:52 +02:00
feat: add template creation timestamps
This commit is contained in:
@@ -80,12 +80,15 @@ Placeholders use `{{group.field}}`. Available values include:
|
||||
`member.mandate_signed_at`, `member.mandate_revoked_at`,
|
||||
`member.mandate_active`
|
||||
- Claim: `claim.id`, `claim.title`, `claim.due_date`, `claim.total`,
|
||||
`claim.paid`, `claim.balance`, `claim.status`, `claim.items`
|
||||
`claim.created_date`, `claim.created_at`, `claim.paid`, `claim.balance`,
|
||||
`claim.status`, `claim.items`
|
||||
- Reminder: `reminder.id`, `reminder.level`, `reminder.name`,
|
||||
`reminder.status`, `reminder.created_at`, `reminder.sent_at`,
|
||||
`reminder.payment_deadline`, `reminder.payment_deadline_days`,
|
||||
`reminder.fee`, `reminder.detail`, `reminder.channel`
|
||||
- Document: `document.date`, `document.datetime`
|
||||
- Document: `document.created_date`, `document.created_at`; compatibility
|
||||
aliases: `document.date`, `document.datetime`, `current_date`,
|
||||
`current_datetime`
|
||||
- Organization: `organization.name`, `organization.street`,
|
||||
`organization.postal_code`, `organization.city`, `organization.country`,
|
||||
`organization.address_line`, `organization.email`, `organization.phone`,
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"OpenDocument-Templates mit Platzhaltern, lokaler PDF-Erzeugung, Audit-Verknüpfung und Dokumentöffnung aus der Mitgliederakte ergänzt.",
|
||||
"Zentrale Vereins- und Absenderdaten sowie getrennte Mitgliedsbereiche für Anschrift, Telefon und validierte Bank-/SEPA-Daten ergänzt.",
|
||||
"Wiederholbare OpenDocument-Tabellenzeilen für beliebig viele Forderungspositionen eingeführt.",
|
||||
"Eindeutige Templatefelder für Dokument-, aktuelle und Forderungs-Erstellungszeit ergänzt.",
|
||||
"Dropdowns zeigen deutsche Begriffe bei weiterhin englischen Speicher-Keys; der Hausmeisterstatus liegt einheitlich in housekeeper.json.",
|
||||
"Mehrstufiger Mahnworkflow mit Hausmeister-Regel, Entwurf, Versandbestätigung, Zahlungsfrist, optionaler Gebühr und Mahnsperre ergänzt.",
|
||||
"Splash-Screen auf das eingebettete CCMA-Hintergrundmotiv umgestellt und redundante Titeltexte entfernt.",
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<office:text>
|
||||
<text:p text:style-name="Title">{{organization.name}}</text:p>
|
||||
<text:p>{{organization.address_line}} · {{organization.email}}</text:p>
|
||||
<text:p>Erstellt am {{document.date}}</text:p>
|
||||
<text:p>Erstellt am {{document.created_date}}</text:p>
|
||||
<text:p text:style-name="Heading">Forderung: {{claim.title}}</text:p>
|
||||
<text:p>Mitglied: {{member.full_name}} ({{member.number}})</text:p>
|
||||
<text:p>E-Mail: {{member.email}}</text:p>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<office:text>
|
||||
<text:p text:style-name="Title">{{organization.name}}</text:p>
|
||||
<text:p>{{organization.address_line}} · {{organization.email}}</text:p>
|
||||
<text:p>{{document.date}}</text:p>
|
||||
<text:p>{{document.created_date}}</text:p>
|
||||
<text:p>An {{member.full_name}} ({{member.number}})</text:p>
|
||||
<text:p>{{member.address_line}}</text:p>
|
||||
<text:p text:style-name="Heading">{{reminder.name}}</text:p>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<office:body>
|
||||
<office:text>
|
||||
<text:p text:style-name="Title">{{organization.name}}</text:p>
|
||||
<text:p>Erstellt am {{document.date}}</text:p>
|
||||
<text:p>Erstellt am {{document.created_date}}</text:p>
|
||||
<text:p text:style-name="Heading">Mitgliedsdaten</text:p>
|
||||
<text:p>Name: {{member.full_name}}</text:p>
|
||||
<text:p>Mitgliedsnummer: {{member.number}}</text:p>
|
||||
|
||||
@@ -182,6 +182,9 @@ def _template_values(
|
||||
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 (
|
||||
@@ -192,8 +195,12 @@ def _template_values(
|
||||
if part
|
||||
)
|
||||
values = {
|
||||
"document.date": format_date_for_display(date.today().isoformat()),
|
||||
"document.datetime": datetime.now().astimezone().strftime("%d.%m.%Y %H:%M"),
|
||||
"document.date": created_date,
|
||||
"document.datetime": created_timestamp,
|
||||
"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,
|
||||
@@ -246,6 +253,10 @@ def _template_values(
|
||||
"claim.id": claim_id,
|
||||
"claim.title": str(claim.get("title", "")),
|
||||
"claim.due_date": format_date_for_display(str(claim.get("due_date", ""))),
|
||||
"claim.created_date": _display_date_from_timestamp(
|
||||
str(claim.get("created_at", ""))
|
||||
),
|
||||
"claim.created_at": _display_timestamp(str(claim.get("created_at", ""))),
|
||||
"claim.total": f"{money_text(claim_total(claim))} EUR",
|
||||
"claim.paid": f"{money_text(allocated_total(data, claim_id))} EUR",
|
||||
"claim.balance": f"{money_text(claim_balance(data, claim))} EUR",
|
||||
@@ -516,5 +527,14 @@ def _display_timestamp(value: str) -> str:
|
||||
return value[:16]
|
||||
|
||||
|
||||
def _display_date_from_timestamp(value: str) -> str:
|
||||
if not value:
|
||||
return ""
|
||||
try:
|
||||
return format_date_for_display(datetime.fromisoformat(value).date().isoformat())
|
||||
except ValueError:
|
||||
return value[:10]
|
||||
|
||||
|
||||
def _local_name(tag: str) -> str:
|
||||
return tag.rsplit("}", 1)[-1]
|
||||
|
||||
+25
-1
@@ -6,7 +6,13 @@ from xml.etree import ElementTree
|
||||
import pytest
|
||||
|
||||
import ccma.services.documents as document_module
|
||||
from ccma.services.documents import DocumentError, DocumentService, _replace_xml_placeholders
|
||||
from ccma.domain.models import ContributionData, Member
|
||||
from ccma.services.documents import (
|
||||
DocumentError,
|
||||
DocumentService,
|
||||
_replace_xml_placeholders,
|
||||
_template_values,
|
||||
)
|
||||
from ccma.services.housekeeper import Housekeeper
|
||||
from ccma.storage.repository import MemberRepository, RepositoryError
|
||||
|
||||
@@ -32,6 +38,24 @@ def test_unknown_placeholder_is_reported() -> None:
|
||||
_replace_xml_placeholders(source, {})
|
||||
|
||||
|
||||
def test_document_and_claim_creation_time_placeholders() -> None:
|
||||
member = Member("member-1", "CCMA-1", "Ada", "Lovelace")
|
||||
claim = {
|
||||
"claim_id": "claim-1",
|
||||
"title": "Test",
|
||||
"amount": "10.00",
|
||||
"created_at": "2026-06-21T14:35:00+02:00",
|
||||
}
|
||||
data = ContributionData(claims=[claim])
|
||||
|
||||
values, _repeats = _template_values(member, data=data, claim=claim)
|
||||
|
||||
assert values["document.created_date"] == values["document.date"] == values["current_date"]
|
||||
assert values["document.created_at"] == values["document.datetime"] == values["current_datetime"]
|
||||
assert values["claim.created_date"] in {"21.06.2026", "2026-06-21"}
|
||||
assert values["claim.created_at"] == "21.06.2026 14:35"
|
||||
|
||||
|
||||
def test_claim_item_loop_clones_formatted_table_row() -> None:
|
||||
source = b"""<office:document
|
||||
xmlns:office="urn:oasis:names:tc:opendocument:xmlns:office:1.0"
|
||||
|
||||
Reference in New Issue
Block a user