Commit Graph
28 Commits
Author SHA1 Message Date
Marcel Peterkau aab43cc0fe Roll back reminder status when direct send/draft delivery fails
generate_and_send_reminder_mail marked a reminder as "sent" before
attempting SMTP send/IMAP append. If the connection, login, or append
failed, the reminder stayed persisted as sent even though no mail was
ever delivered or filed, so it could not be corrected and resent.

Wrap content generation, archiving, and delivery in a try/except that
calls the existing revert_reminder_sent on any failure and re-raises,
putting the reminder back into "draft" for all three delivery modes.
2026-08-20 12:08:44 +02:00
Marcel PeterkauandClaude Sonnet 5 cc4aaef895 Copy directly-sent e-mails to an IMAP Sent folder, with a live folder picker
A raw SMTP send has no server-side "Sent" copy on its own (unlike IMAP
drafts, which are inherently server-side) -- add an opt-in checkbox plus a
configurable target folder so directly sent Mahnungen/SEPA-info-mails still
show up in the account's Gesendet/Sent folder like a normal mail client
would leave them. Applies only to "send" delivery; drafts already live on
the server by definition.

Both the Entwürfe- and Gesendet-folder fields are now editable comboboxes:
a new "Ordnerliste laden" button fetches the real folder list from the IMAP
server (needs working credentials first) via LIST, decoding folder names
from modified UTF-7 (RFC 3501) so names like "Entwürfe" render correctly
instead of as "Entw&APw-rfe". Free text still works -- ensure_imap_folder()
creates the folder on first use if it doesn't exist yet, checked once per
batch rather than before every single message.

mail_delivery.append_to_imap_drafts() became the more general
append_message(client, content, folder=, flags=), reused for both the
\Draft and \Seen cases.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 04:34:34 +02:00
Marcel PeterkauandClaude Sonnet 5 6b0da82b45 Add configurable e-mail delivery: direct SMTP send or IMAP drafts
Mahnungs- and SEPA-info-mails could previously only be saved as a local .eml
file that still had to be manually imported into Thunderbird. Add a per-store
"E-Mail-Versand" configuration (Optionen -> E-Mail-Versand, stored in
repository.json alongside the rest of the club's settings, since different
stores may use different mailboxes) with four delivery modes:

- "Lokal speichern": today's behaviour, unchanged default for existing stores.
- "Direkt versenden": sends via SMTP.
- "Als Entwurf ablegen": IMAP APPENDs into a configurable drafts folder, so it
  shows up live in whatever mail client is already watching that account.
- "Jedes Mal fragen": prompts once per generation action (not per e-mail --
  a SEPA batch can cover dozens of members) with Senden/Entwürfe/Abbrechen.

New ccma.services.mail_delivery module (smtplib/imaplib, no new dependency)
opens one authenticated connection per batch and reuses it across all
messages instead of reconnecting per recipient. Both "Verbindung testen"
buttons in Options exercise the same connection path used for real delivery.
The archived per-member copy of every generated e-mail is unaffected and
still always written regardless of delivery mode.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 04:34:34 +02:00
Marcel PeterkauandClaude Sonnet 5 a1719cad5e Handle failed direct debits through the same reminder mechanism
A Rücklastschrift is still the member's own failure to ensure cover or
provide valid bank details, so it belongs in the same escalation ladder
as a regular reminder -- it now occupies whatever level the claim is next
due for, alongside a "Rücklastschrift" preset next to the existing
Mahnstufe presets.

Mahnung vorbereiten no longer lets the board pick a level (it's computed
automatically from what's already been sent, since the sequencing was
already server-enforced) and replaces the single fee field with a small
items table (Beschreibung + Betrag), so a reminder can carry several
charges at once -- e.g. Rücklastschriftgebühr plus Bankgebühr plus Porto
-- each landing on the claim as its own line item instead of one lump
sum under a generic label. The description field offers the configured
standard texts but stays free-editable.

Those standard texts and amounts, plus the dunning levels themselves
(name/fee/deadline), are now configurable from Optionen -> Mahnungen
instead of only being editable by hand-editing repository.json, which
was the case for the whole reminder policy until now. Cleaned up
reminder_fee/failed_debit_fee on contribution rules while at it -- both
were unused leftovers superseded by this.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 03:13:04 +02:00
Marcel PeterkauandClaude Sonnet 5 ee99577b20 Run the housekeeper for a single member, optionally retroactively
Add a "Hausmeister für dieses Mitglied" button to the member view. It
runs every rule scoped to just that member -- e.g. after editing a
contribution override or payment frequency, to recompute their claims on
demand instead of waiting for or triggering a full run over every
member. A checkbox lets the board force retroactive claim creation for
just that one run, without changing the application-wide default
setting.

Housekeeper.run() gained member_id and retroactive_claims parameters.
Scoping to one member skips the asset checks and leaves every other
member's tasks completely untouched, since their rules never ran this
pass and so fall outside the resolved-scope set that
_resolve_stale_tasks uses to decide what to clear.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-15 00:59:01 +02:00
Marcel PeterkauandClaude Sonnet 5 745e634a8b Import payments per member from a GnuCash file
Adds a "Zahlungen importieren" button to the member Zahlungen tab that opens
a dedicated window: point it at a GnuCash file (plain or gzip-compressed
XML, defaults to the file configured in Optionen), pick one of its accounts,
and narrow the account's bookings down with a description-contains filter
and an optional date range.

Each matching booking gets a checkbox to mark it for import as a payment.
Before anything is ticked, bookings are cross-checked against this member's
existing payments by date + amount; a match is highlighted and its checkbox
is refused, so re-importing the same statement can't create a duplicate
payment. Only bookings with a positive amount on the selected account are
offered, since those are the ones that make sense as an incoming payment.

Parsing lives in ccma.services.gnucash_import, independent of the UI, and
is covered by tests against a synthetic GnuCash XML fixture (plain and
gzip-compressed) -- no gnucash/piecash dependency needed since the native
file format is just XML.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 23:02:37 +02:00
Marcel Peterkau fc4eacb591 feat: add reminder email drafts 2026-07-30 01:17:33 +02:00
Marcel Peterkau 3d3b845d9f feat: add SEPA direct debit exports and notifications 2026-07-30 01:00:32 +02:00
Marcel Peterkau 070684d9bc feat: extend inventory and administration workflows 2026-07-22 22:26:02 +02:00
Marcel Peterkau d8900d9767 Show credit settlements as positive amounts 2026-06-27 15:39:52 +02:00
Marcel Peterkau 87e972bb43 Add JSON integrity hash checks 2026-06-27 10:35:35 +02:00
Marcel Peterkau 30b6d253b2 Update member UI and related app changes 2026-06-26 21:57:11 +02:00
Marcel Peterkau 0e3087a780 ci: add CCMA release builds 2026-06-23 20:19:53 +02:00
Marcel Peterkau 302170230a feat: Add Windows compatibility, PyInstaller build setup, and custom icon
- Add Windows LibreOffice detection fallback for soffice path
  - Check standard install locations (%PROGRAMFILES%, %PROGRAMFILES(X86)%)
  - Graceful fallback with clear error messages

- Add PyInstaller build infrastructure
  - build/ccma.spec: PyInstaller configuration with icon generation
  - build/build.ps1: Automated build script for standalone exe
  - main.py: Entry point for PyInstaller
  - Supports versioning and architecture tagging

- Create custom CCMA icon
  - Person + Gear symbol representing member administration
  - Cyan/White/Orange color scheme
  - Auto-converts PNG to ICO during build

- Update documentation
  - README: Windows PowerShell setup instructions
  - README: Linux/macOS bash setup instructions
  - README: Standalone executable build guide
  - pyproject.toml: Add 'build' extra with pyinstaller+pillow

- Add regression tests
  - Test office executable detection and Windows fallback
  - Verify all tests pass (80 passed)
2026-06-23 11:08:18 +02:00
Marcel Peterkau 576220ccd5 refactor: remove template timestamp aliases 2026-06-21 22:44:09 +02:00
Marcel Peterkau 3c842f29a3 feat: add template creation timestamps 2026-06-21 22:41:53 +02:00
Marcel Peterkau c58072fe45 feat: repeat claim items in document tables 2026-06-21 22:33:24 +02:00
Marcel Peterkau 6c7bf63280 feat: add member address and SEPA data 2026-06-21 22:14:45 +02:00
Marcel Peterkau 0622a22794 feat: add OpenDocument PDF templates 2026-06-21 22:10:16 +02:00
Marcel Peterkau e6200f4a02 feat: pace startup housekeeper runs 2026-06-21 21:42:38 +02:00
Marcel Peterkau e7962f77e1 refactor: localize UI labels and store filenames 2026-06-21 18:25:58 +02:00
Marcel Peterkau 80d4d5ef90 feat: add itemized claims and payments 2026-06-21 18:20:55 +02:00
Marcel Peterkau c717d6806b feat: allow deleting housekeeper tasks 2026-06-21 18:09:04 +02:00
Marcel Peterkau 7596e47981 fix: preflight member records before rules 2026-06-21 17:54:09 +02:00
Marcel Peterkau 55bc3b666e fix: tolerate damaged contribution files 2026-06-21 17:50:56 +02:00
Marcel Peterkau 4bc1a8a200 feat: add scriptable housekeeper rule engine 2026-06-21 17:43:04 +02:00
Marcel Peterkau e63abbae81 fix: tolerate invalid member dates in views 2026-06-21 16:49:51 +02:00
Marcel Peterkau dfd5b1192b feat: initialize CCMA member administration 2026-06-21 16:46:15 +02:00