Commit Graph
4 Commits
Author SHA1 Message Date
Marcel PeterkauandClaude Opus 5 9e9bb7d668 Ask every member to check the data the club stores about them
The club has to keep its member data current, and until now that meant writing
to each member by hand. "Datenüberprüfung anfragen" in the members tab sends one
mail per member, each listing that member's own record: number, name, nickname,
birth date, contact data, address, status, member since, payment frequency, and
the bank details only for members who have any.

Two decisions the record itself forced:

A field with no value is printed as "(nicht hinterlegt)" rather than left out.
The point of the mail is to have gaps filled in, and a missing line is a gap
nobody sees.

The IBAN is masked down to its country code and last four digits. That is enough
to recognise the account, and it keeps a full account number out of a mail the
club sends to dozens of people at once.

Every member is listed as a recipient, with the live memberships that have an
address preselected -- a member who resigned at year's end may still need to
confirm their address, so the board can add them by hand. Members without an
address are skipped and reported instead of failing the run.

Delivery reuses the existing mail machinery: the configured delivery mode, one
SMTP/IMAP connection for the whole run, an archive copy in the member file, a
"data_review_email_sent" event, and the read-only guard before anything is
rendered. A member whose mail fails is reported as a warning and the run
continues -- one bad address must not stop a mailing to the whole club halfway
through. Because the run cannot be taken back, the recipient count is confirmed
once more before it starts.

Subject and text come from a new "Datenüberprüfung" template, editable like the
others, with {{data.sheet}} for the whole record and a {{#data}} block for a
layout of the board's own.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 22:05:38 +02:00
Marcel PeterkauandClaude Opus 5 e87f859101 Refuse the welcome mail on a read-only store before it renders anything
generate_and_send_welcome_mail() arrived with the mail templates, after the
read-only guards were added to the other services, and never got one. On a
read-only store it therefore rendered the mail, could hand it to the mail
server, and only failed when it tried to create the archive directory in the
member file -- surfacing a PermissionError instead of the ReadOnlyStoreError
every other write path reports.

The guard now sits at the top, next to the delivery-mode check, so nothing is
rendered, sent or written. The read-only test covers this path (and the SEPA
batch alongside it) and asserts that nothing at all was left behind: no export
file, no archive directory, no "sent" event.

Its member carries an e-mail address now -- without one the mail services bail
out for that reason, and the write the test exists for is never reached.

Note that the SEPA CSV/XML export keeps writing without a guard on purpose: it
writes to a path the board picks outside the store, which a read-only store has
no say over.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 01:09:05 +02:00
Marcel PeterkauandClaude Opus 5 ae53c168fd Merge branch 'dev' into feature/read-only-store
dev gained the editable mail templates, which live in the store like everything
else -- so they follow the same read-only rules: the options dialog skips saving
them, save_mail_template() reports the store instead of a raw PermissionError,
and a store that cannot keep its own copy simply renders from the shipped
default.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:26:40 +02:00
Marcel PeterkauandClaude Opus 5 be042949a2 Keep CCMA usable when the member store is mounted read-only
The encrypted volume holding the member data can be mounted without write
access, but starting against such a store failed: the housekeeper takes a lock
file before doing anything, so its startup pass died with a PermissionError and
took the whole start with it.

The store is now probed with an actual write once at startup -- permissions,
mount options and filesystem state all matter, and only an attempt covers them
together -- and a read-only store opens as a read-only session. The housekeeper
is skipped rather than attempted, every write inside the repository goes through
one guard that reports ReadOnlyStoreError (a RepositoryError, so the dialogs
already handle it) instead of letting an OS error surface, and the services that
archive into the member file check before they start sending or rendering.

The session says so permanently: a warning banner above the tabs, "NUR LESEN" in
the window title and status bar, and refused actions explaining why. Program
settings still save -- they live in the user's config directory -- while the
store-backed ones are skipped with a notice. "Erneut prüfen" picks up a volume
that was remounted writable without restarting.

A store that was never initialized still fails, but says that creating one needs
write access.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-28 21:10:31 +02:00