Add email notification for staff

This commit is contained in:
2026-05-17 12:31:44 +03:00
parent 8ffac1b8a7
commit f3da494f73
5 changed files with 187 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
"""Outgoing email -- plan.md §7 (state-transition side effects).
Five public functions, one per dedicated template:
Six public functions, one per dedicated template:
send_confirmation_email(submission)
Guest path: token-link emailed immediately after `SubmitView` creates
@@ -32,7 +32,15 @@ Five public functions, one per dedicated template:
when present (typically pickup instructions). Template:
`emails/completed.*`.
All five delegate to Django's email machinery. The backend is wired in
send_staff_verify_email(submission)
Operator broadcast fired on any transition into `status = verifying`.
One message to every active staff user with a non-empty email
(recipients placed in `bcc` so individual addresses stay hidden).
Surfaces the submitter's private `notes_for_op` -- not echoed on the
public detail page (plan.md §5) -- since the audience is staff only.
Template: `emails/staff_verify.*`.
All six delegate to Django's email machinery. The backend is wired in
`hamprint/settings/base.py`: Mailtrap via `django-anymail` when
`MAILTRAP_API_TOKEN` is present, console when not. Send failures are caught
+ logged so a flaky transport never blocks the submission flow.
@@ -175,3 +183,62 @@ def send_completed_email(sub: Submission) -> bool:
pickup-instruction the operator typed in admin reaches the user."""
detail_url = f"{settings.SITE_URL}/p/{sub.slug}/"
return _send("completed", sub, {"detail_url": detail_url})
def send_staff_verify_email(sub: Submission) -> bool:
"""Broadcast a "please verify" notice to every active staff user when
a submission lands in `verifying`. Fired by the centralised
`Submission.save()` hook so the worker's `processing -> verifying`
success branch AND any future admin path that returns a row to
`verifying` both trigger it (plan.md §7.3).
Recipients are placed in `bcc` so individual staff addresses stay
hidden from each other; `to` carries `DEFAULT_FROM_EMAIL` purely to
satisfy transports that reject envelopes with no `to`. Returns False
(without raising) when there is no active staff with a usable email
or when the transport surfaces an exception.
"""
from django.contrib.auth import get_user_model
UserModel = get_user_model()
recipients = list(
UserModel.objects
.filter(is_staff=True, is_active=True)
.exclude(email="")
.values_list("email", flat=True)
)
if not recipients:
logger.info(
"no active staff with email; skipping staff_verify for %s", sub.slug
)
return False
admin_url = (
f"{settings.SITE_URL}/admin/submissions/submission/{sub.pk}/change/"
)
detail_url = f"{settings.SITE_URL}/p/{sub.slug}/"
context = {
"submission": sub,
"site_url": settings.SITE_URL,
"admin_url": admin_url,
"detail_url": detail_url,
}
subject, body_text, body_html = _render("staff_verify", context)
msg = EmailMultiAlternatives(
subject=subject,
body=body_text,
from_email=settings.DEFAULT_FROM_EMAIL,
to=[settings.DEFAULT_FROM_EMAIL],
bcc=recipients,
)
msg.attach_alternative(body_html, "text/html")
try:
msg.send()
except Exception:
logger.exception("failed to send staff_verify email for %s", sub.slug)
return False
logger.info(
"sent staff_verify email for %s to %d staff", sub.slug, len(recipients)
)
return True