mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-09-06 03:20:49 +02:00
Stop rolling back reminders/SEPA mails after a successful send
The previous fix reverted the reminder to "draft" on any failure after mark_reminder_sent, including failures that happened after the mail had already been handed to send_via_smtp or appended to an IMAP folder. That made a successful send followed by a Sent-folder-copy or archive-move failure look like nothing was sent, inviting a duplicate send/fee booking on retry -- the same "sent" flag needs to be preserved once delivery is no longer reversible, per follow-up review. reminder_mail.generate_and_send_reminder_mail and sepa_mail.generate_debit_mails now track whether the mail actually left the building (SMTP accepted / IMAP append succeeded / local file written) separately from the later archiving step: - Failure before that point: reminder_mail reverts to draft (unchanged); sepa_mail now records a warning and continues with the remaining debits instead of aborting the whole batch. - Failure after that point (Sent-copy append, moving the archive file into place): the reminder stays "sent" / the debit stays in the batch's results, an event is still logged for traceability (with an archive_error note and no document reference), and the caller gets a clear error to follow up on manually -- no rollback, no silent loss of the fact that the mail already went out.
This commit is contained in:
@@ -166,43 +166,66 @@ def generate_debit_mails(
|
||||
archive_dir.mkdir(parents=True, exist_ok=True)
|
||||
archive_path = _available_path(archive_dir, filename)
|
||||
export_path: Path | None = None
|
||||
if delivery_mode == "local":
|
||||
export_path = _available_path(output, filename)
|
||||
export_path.write_bytes(content)
|
||||
elif delivery_mode == "send":
|
||||
send_via_smtp(smtp_client, content)
|
||||
if imap_client is not None:
|
||||
append_message(
|
||||
imap_client, content, folder=email_settings["imap_sent_folder"], flags=r"(\Seen)"
|
||||
)
|
||||
else:
|
||||
append_message(
|
||||
imap_client, content, folder=email_settings["imap_drafts_folder"], flags=r"(\Draft)"
|
||||
)
|
||||
# Once the mail has actually left the building for this debit -- SMTP
|
||||
# accepted it, it's filed in the IMAP folder, or the local export file was
|
||||
# written -- a later archiving failure must not abort the whole batch and
|
||||
# lose track of the fact that this one was already delivered; nor may it
|
||||
# abort earlier/later debits that have nothing to do with this failure.
|
||||
delivered = False
|
||||
archive_failure: Exception | None = None
|
||||
try:
|
||||
if delivery_mode == "local":
|
||||
export_path = _available_path(output, filename)
|
||||
export_path.write_bytes(content)
|
||||
delivered = True
|
||||
elif delivery_mode == "send":
|
||||
send_via_smtp(smtp_client, content)
|
||||
delivered = True
|
||||
if imap_client is not None:
|
||||
append_message(
|
||||
imap_client, content, folder=email_settings["imap_sent_folder"], flags=r"(\Seen)"
|
||||
)
|
||||
else:
|
||||
append_message(
|
||||
imap_client, content, folder=email_settings["imap_drafts_folder"], flags=r"(\Draft)"
|
||||
)
|
||||
delivered = True
|
||||
archive_path.write_bytes(content)
|
||||
except OSError:
|
||||
if export_path is not None:
|
||||
export_path.unlink(missing_ok=True)
|
||||
raise
|
||||
relative_path = archive_path.relative_to(
|
||||
repository.members_root / member.member_id / "files"
|
||||
).as_posix()
|
||||
except (OSError, RepositoryError) as exc:
|
||||
if not delivered:
|
||||
if export_path is not None:
|
||||
export_path.unlink(missing_ok=True)
|
||||
warnings.append(f"{member.member_number or member.display_name}: {exc}")
|
||||
continue
|
||||
warnings.append(
|
||||
f"{member.member_number or member.display_name}: E-Mail wurde versandt/abgelegt, "
|
||||
f"konnte aber nicht archiviert werden ({exc}); bitte manuell prüfen."
|
||||
)
|
||||
archive_failure = exc
|
||||
|
||||
digest = hashlib.sha256(content).hexdigest()
|
||||
references = {}
|
||||
if archive_failure is None:
|
||||
references["document"] = archive_path.relative_to(
|
||||
repository.members_root / member.member_id / "files"
|
||||
).as_posix()
|
||||
data = {
|
||||
"sha256": digest,
|
||||
"recipient": member.email,
|
||||
"collection_date": collection_date.isoformat(),
|
||||
"amount": f"{debit.amount:.2f}",
|
||||
"delivery_mode": delivery_mode,
|
||||
}
|
||||
if archive_failure is not None:
|
||||
data["archive_error"] = str(archive_failure)
|
||||
repository.append_event(
|
||||
member.member_id,
|
||||
event_type="sepa_notification_generated",
|
||||
summary=f"SEPA-Info-Mail erzeugt: {archive_path.name}",
|
||||
actor_type="user",
|
||||
actor_name="Vorstand",
|
||||
references={"document": relative_path},
|
||||
data={
|
||||
"sha256": digest,
|
||||
"recipient": member.email,
|
||||
"collection_date": collection_date.isoformat(),
|
||||
"amount": f"{debit.amount:.2f}",
|
||||
"delivery_mode": delivery_mode,
|
||||
},
|
||||
references=references,
|
||||
data=data,
|
||||
)
|
||||
generated.append(
|
||||
GeneratedDebitMail(member.member_id, member.email, export_path, archive_path)
|
||||
|
||||
Reference in New Issue
Block a user