Cerfa mécénat rempli envoyé par email à la confirmation du paiement
Quand un paiement « mécénat » est confirmé, l'email de confirmation porte en pièce jointe le reçu fiscal (cerfa 11580*05) rempli du bénévole, avec une phrase l'annonçant dans le corps. Généré en tâche de fond : un échec de génération envoie l'email sans pièce jointe plutôt que rien. - build_message/send acceptent des pièces jointes - app/mecenat/receipts.py : passage Volunteer → MecenatReceipt, partagé avec l'export coordination - modèle cerfa remplacé par une version pré-remplie avec l'association (organisme bénéficiaire, cases 200/978, signature) ; position de la date du versement ajustée Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
5675e1d220
commit
5a0209ce43
@@ -8,7 +8,7 @@ Pour diagnostiquer : `python -m app.email.check <destinataire>`."""
|
||||
import logging
|
||||
import smtplib
|
||||
import ssl
|
||||
from collections.abc import Iterable
|
||||
from collections.abc import Iterable, Sequence
|
||||
from email.message import EmailMessage
|
||||
|
||||
from app.config import Settings, get_settings
|
||||
@@ -33,8 +33,17 @@ def _smtp_error_summary(exc: smtplib.SMTPException) -> str:
|
||||
return exc.__class__.__name__
|
||||
|
||||
|
||||
# (octets, content-type, nom de fichier) d'une pièce jointe.
|
||||
Attachment = tuple[bytes, str, str]
|
||||
|
||||
|
||||
def build_message(
|
||||
to: str, subject: str, body: str, *, logo: tuple[bytes, str] | None = None
|
||||
to: str,
|
||||
subject: str,
|
||||
body: str,
|
||||
*,
|
||||
logo: tuple[bytes, str] | None = None,
|
||||
attachments: Sequence[Attachment] = (),
|
||||
) -> EmailMessage:
|
||||
"""`logo` = (octets, content-type) (voir `app/email/branding.py:
|
||||
edition_logo`) : ajoute une version HTML au thème de l'appli
|
||||
@@ -43,7 +52,9 @@ def build_message(
|
||||
HTML. Le logo, s'il y en a un, est intégré en pièce jointe « inline »
|
||||
(Content-ID) référencée par la partie HTML — jamais une URL externe, qui
|
||||
aurait exposé le jeton d'accès du formulaire public à quiconque lit la
|
||||
source du message (voir `app/email/branding.py`)."""
|
||||
source du message (voir `app/email/branding.py`). `attachments` : vraies
|
||||
pièces jointes (ex. reçu fiscal mécénat), ajoutées après le logo — le
|
||||
message devient alors `multipart/mixed`."""
|
||||
message = EmailMessage()
|
||||
message["From"] = get_settings().smtp_from
|
||||
message["To"] = to
|
||||
@@ -60,6 +71,9 @@ def build_message(
|
||||
html_part.add_related(
|
||||
content, maintype or "image", subtype or "png", cid=f"<{LOGO_CID}>"
|
||||
)
|
||||
for content, content_type, filename in attachments:
|
||||
maintype, _, subtype = content_type.partition("/")
|
||||
message.add_attachment(content, maintype=maintype, subtype=subtype, filename=filename)
|
||||
return message
|
||||
|
||||
|
||||
@@ -131,8 +145,15 @@ def deliver_many(messages: list[EmailMessage]) -> None:
|
||||
logger.info("Emails remis : %d/%d", sent, len(messages))
|
||||
|
||||
|
||||
def send(to: str, subject: str, body: str, *, logo: tuple[bytes, str] | None = None) -> None:
|
||||
deliver_many([build_message(to, subject, body, logo=logo)])
|
||||
def send(
|
||||
to: str,
|
||||
subject: str,
|
||||
body: str,
|
||||
*,
|
||||
logo: tuple[bytes, str] | None = None,
|
||||
attachments: Sequence[Attachment] = (),
|
||||
) -> None:
|
||||
deliver_many([build_message(to, subject, body, logo=logo, attachments=attachments)])
|
||||
|
||||
|
||||
def send_many(
|
||||
|
||||
@@ -32,22 +32,23 @@ def render_registration_confirmation(*, first_name: str) -> tuple[str, str]:
|
||||
|
||||
|
||||
def render_registration_paid_confirmation(
|
||||
*, first_name: str, label: str, amount: float
|
||||
*, first_name: str, label: str, amount: float, with_mecenat_receipt: bool = False
|
||||
) -> tuple[str, str]:
|
||||
"""Inscription prise en compte **et** paiement en ligne confirmé — envoyé
|
||||
une fois seulement, au moment de la promotion (`app/api/forms.py:
|
||||
_promote_pending`). Sert de reçu (montant, type de paiement) en
|
||||
l'absence de confirmation documentée que l'API SumUp Hosted Checkout en
|
||||
envoie un elle-même."""
|
||||
return (
|
||||
t("email.registration_confirmation_paid.subject"),
|
||||
t(
|
||||
"email.registration_confirmation_paid.body",
|
||||
prenom=first_name,
|
||||
label=label,
|
||||
montant=amount,
|
||||
),
|
||||
envoie un elle-même. `with_mecenat_receipt` : le cerfa rempli est joint
|
||||
(`_promote_pending`), le corps l'annonce."""
|
||||
body = t(
|
||||
"email.registration_confirmation_paid.body",
|
||||
prenom=first_name,
|
||||
label=label,
|
||||
montant=amount,
|
||||
)
|
||||
if with_mecenat_receipt:
|
||||
body += "\n\n" + t("email.registration_confirmation_paid.mecenat_receipt_note")
|
||||
return t("email.registration_confirmation_paid.subject"), body
|
||||
|
||||
|
||||
def _fill(template: str, values: dict[str, object]) -> str:
|
||||
|
||||
Reference in New Issue
Block a user