bottleneckcrm/backend/webforms/tasks.py
Ashwin 2be3f51efc Add comprehensive tests for ticket closing, calendar feed, deal board, macros, session caches, and web form models
- Implement tests for closing tickets via menu and status picker, ensuring correct API interactions and error handling.
- Create tests for calendar feed provider, verifying throttling behavior and success responses.
- Add tests for deal board provider, covering reading and moving deals, including edge cases for card movement and filters.
- Enhance macro tests to validate action parsing and application to tickets, including error handling.
- Introduce session cache tests to ensure proper cleanup on sign-out and validate session-scoped providers.
- Extend web form model tests to cover lead rotation functionality, ensuring correct serialization and defaults.
2026-09-27 06:28:09 +05:30

111 lines
3.8 KiB
Python

"""Background work for web forms.
A Celery worker does not run Django middleware, so `set_rls_context` has to be
called explicitly before any ORM query. Without it every query below runs with
an empty `app.current_org` and returns zero rows, which reads as "no submission
found" rather than as a bug.
"""
import logging
from celery import shared_task
from django.template.loader import render_to_string
from common.links import frontend_url
from common.tasks import set_rls_context
from leads.tasks import send_email
from webforms.models import WebFormSubmission
from webforms.service import active_assignee
logger = logging.getLogger(__name__)
@shared_task
def send_webform_submission_email(submission_id, org_id):
"""Tell the form's recipients that a lead or ticket came in.
The only mail a submission sends. Recipients are the form's active
`notify_profiles` plus the record's assignee: the lead's own active
assignees on a lead form, the form's active `assign_to` on a ticket form.
One message to the union, so an assignee who is also a notify recipient
receives one email rather than two.
Rejected submissions notify nobody. Telling an org about every bot that
hits their form is how they learn to ignore the notification, and then to
miss the real one.
`send_email` is called directly rather than through `.delay()`. This is
already inside a task, so enqueuing a second one buys nothing and would
only add a hop where the message can be lost.
"""
set_rls_context(org_id)
submission = (
WebFormSubmission.objects.filter(id=submission_id, org_id=org_id)
.select_related("form", "lead", "case", "form__assign_to")
.first()
)
if submission is None:
logger.info(
"Web form submission %s is gone or belongs to another org; "
"nothing to notify.",
submission_id,
)
return
if submission.status not in WebFormSubmission.ACCEPTED_STATUSES:
return
form = submission.form
# Active members only: a deactivated one has left, and this mail carries
# the prospect's details.
profiles = set(
form.notify_profiles.filter(is_active=True).select_related("user").all()
)
if submission.case_id:
assignee = active_assignee(form)
if assignee is not None:
profiles.add(assignee)
elif submission.lead_id:
# Whoever the lead actually belongs to, not the form's `assign_to`. A
# rotation form picks a different member each time, and a repeat
# submission merges into a lead somebody else may already own.
profiles.update(
submission.lead.assigned_to.filter(
org_id=org_id, is_active=True
).select_related("user")
)
recipients = sorted(
{
profile.user.email
for profile in profiles
if getattr(profile, "user", None) and profile.user.email
}
)
if not recipients:
logger.info("Web form %s has no notification recipients.", form.id)
return
if submission.case_id:
record_url = frontend_url(f"/tickets/{submission.case_id}")
subject = f"New ticket: {form.name}"
else:
record_url = frontend_url(f"/leads/{submission.lead_id}")
subject = f"New submission: {form.name}"
html_content = render_to_string(
"webforms/submission_email.html",
{
"form": form,
"submission": submission,
"lead": submission.lead,
"case": submission.case,
"record_url": record_url,
"is_duplicate": (submission.status == WebFormSubmission.ACCEPTED_DUPLICATE),
},
)
send_email(
subject=subject,
html_content=html_content,
recipients=recipients,
)