06/09 Fix ticket submission doesn't trigger notification
This commit is contained in:
+29
-81
@@ -1,13 +1,11 @@
|
|||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
import threading
|
|
||||||
|
|
||||||
from flask import (Blueprint, render_template, redirect, url_for,
|
from flask import (Blueprint, render_template, redirect, url_for,
|
||||||
flash, request, current_app, jsonify, abort)
|
flash, request, current_app, jsonify, abort)
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from flask_mail import Message
|
|
||||||
|
|
||||||
from app import db, mail
|
from app import db
|
||||||
from app.models.support import SupportTicket, SupportTicketReply
|
from app.models.support import SupportTicket, SupportTicketReply
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.models.facility import Facility
|
from app.models.facility import Facility
|
||||||
@@ -15,6 +13,7 @@ from app.utils.decorators import supervisor_required
|
|||||||
from app.utils.scope import get_customer_scope
|
from app.utils.scope import get_customer_scope
|
||||||
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE
|
from app.utils.audit import log_action, ACTION_CREATE, ACTION_UPDATE
|
||||||
from app.utils.time_utils import now_eastern
|
from app.utils.time_utils import now_eastern
|
||||||
|
from app.utils.notifications import notify
|
||||||
|
|
||||||
bp = Blueprint('support', __name__, url_prefix='/support')
|
bp = Blueprint('support', __name__, url_prefix='/support')
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -167,54 +166,28 @@ def submit_ticket():
|
|||||||
|
|
||||||
|
|
||||||
def _notify_admins_new_ticket(ticket):
|
def _notify_admins_new_ticket(ticket):
|
||||||
"""Send email notification to all active admin users in a background thread."""
|
"""Create in-app notifications and send emails to all active admin users."""
|
||||||
admins = User.query.filter_by(role='admin', active=True).all()
|
admins = User.query.filter_by(role='admin', active=True).all()
|
||||||
if not admins:
|
if not admins:
|
||||||
return
|
return
|
||||||
|
|
||||||
base_url = current_app.config.get('APP_BASE_URL', '').rstrip('/')
|
|
||||||
ticket_url = f'{base_url}{url_for("support.admin_ticket_detail", ticket_id=ticket.id)}'
|
|
||||||
facility_label = ticket.facility.name if ticket.facility else 'N/A'
|
|
||||||
customer_label = ticket.customer.display_name if ticket.customer else 'Unknown'
|
customer_label = ticket.customer.display_name if ticket.customer else 'Unknown'
|
||||||
|
facility_label = ticket.facility.name if ticket.facility else 'N/A'
|
||||||
|
link = url_for('support.admin_ticket_detail', ticket_id=ticket.id)
|
||||||
|
title = f'New support ticket #{ticket.id} from {customer_label}'
|
||||||
|
body = (f'Subject: {ticket.subject}\n'
|
||||||
|
f'Facility: {facility_label}\n\n'
|
||||||
|
f'{ticket.body[:300]}{"…" if len(ticket.body) > 300 else ""}')
|
||||||
|
|
||||||
subject_line = f'[JQC Support] New ticket #{ticket.id}: {ticket.subject}'
|
|
||||||
html_body = f"""\
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<body style="font-family:Arial,sans-serif;color:#333;max-width:600px;margin:auto;">
|
|
||||||
<h2 style="color:#0d6efd;">New Support Ticket #{ticket.id}</h2>
|
|
||||||
<p><strong>From:</strong> {customer_label}</p>
|
|
||||||
<p><strong>Facility:</strong> {facility_label}</p>
|
|
||||||
<p><strong>Subject:</strong> {ticket.subject}</p>
|
|
||||||
<hr style="border:none;border-top:1px solid #eee;">
|
|
||||||
<p style="white-space:pre-wrap;">{ticket.body}</p>
|
|
||||||
<hr style="border:none;border-top:1px solid #eee;">
|
|
||||||
<p>
|
|
||||||
<a href="{ticket_url}"
|
|
||||||
style="background:#0d6efd;color:#fff;padding:10px 20px;
|
|
||||||
text-decoration:none;border-radius:4px;display:inline-block;">
|
|
||||||
View & Reply
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p style="font-size:12px;color:#888;">JQC Support System — automated notification.</p>
|
|
||||||
</body>
|
|
||||||
</html>"""
|
|
||||||
|
|
||||||
def _send():
|
|
||||||
try:
|
|
||||||
with current_app.app_context():
|
|
||||||
for admin in admins:
|
for admin in admins:
|
||||||
msg = Message(
|
notify(
|
||||||
subject = subject_line,
|
recipient = admin,
|
||||||
recipients = [admin.email],
|
title = title,
|
||||||
html = html_body,
|
body = body,
|
||||||
sender = current_app.config.get('MAIL_DEFAULT_SENDER'),
|
link = link,
|
||||||
|
send_email = True,
|
||||||
)
|
)
|
||||||
mail.send(msg)
|
db.session.commit()
|
||||||
except Exception as exc:
|
|
||||||
logger.error('SUPPORT | email notification failed: %s', exc)
|
|
||||||
|
|
||||||
threading.Thread(target=_send, daemon=True).start()
|
|
||||||
|
|
||||||
|
|
||||||
# ── Admin: ticket list ────────────────────────────────────────────────────────
|
# ── Admin: ticket list ────────────────────────────────────────────────────────
|
||||||
@@ -293,46 +266,21 @@ def admin_ticket_detail(ticket_id):
|
|||||||
|
|
||||||
|
|
||||||
def _notify_customer_reply(ticket, reply):
|
def _notify_customer_reply(ticket, reply):
|
||||||
"""Email the customer when an admin replies to their ticket."""
|
"""Create an in-app notification and send an email to the customer."""
|
||||||
if not ticket.customer or not ticket.customer.email:
|
if not ticket.customer:
|
||||||
return
|
return
|
||||||
|
|
||||||
base_url = current_app.config.get('APP_BASE_URL', '').rstrip('/')
|
|
||||||
chat_url = f'{base_url}{url_for("support.chat")}'
|
|
||||||
admin_name = reply.author.display_name if reply.author else 'Support Team'
|
admin_name = reply.author.display_name if reply.author else 'Support Team'
|
||||||
|
title = f'Reply to your support request #{ticket.id}'
|
||||||
|
body = (f'{admin_name} replied to your ticket "{ticket.subject}":\n\n'
|
||||||
|
f'{reply.body[:400]}{"…" if len(reply.body) > 400 else ""}')
|
||||||
|
link = url_for('support.chat')
|
||||||
|
|
||||||
html_body = f"""\
|
notify(
|
||||||
<!DOCTYPE html>
|
recipient = ticket.customer,
|
||||||
<html>
|
title = title,
|
||||||
<body style="font-family:Arial,sans-serif;color:#333;max-width:600px;margin:auto;">
|
body = body,
|
||||||
<h2 style="color:#0d6efd;">Reply to Your Support Request</h2>
|
link = link,
|
||||||
<p>Hi {ticket.customer.display_name},</p>
|
send_email = True,
|
||||||
<p><strong>{admin_name}</strong> replied to your support ticket
|
|
||||||
<strong>#{ticket.id}: {ticket.subject}</strong>:</p>
|
|
||||||
<blockquote style="border-left:4px solid #0d6efd;padding-left:12px;color:#555;
|
|
||||||
white-space:pre-wrap;">{reply.body}</blockquote>
|
|
||||||
<p>
|
|
||||||
<a href="{chat_url}"
|
|
||||||
style="background:#0d6efd;color:#fff;padding:10px 20px;
|
|
||||||
text-decoration:none;border-radius:4px;display:inline-block;">
|
|
||||||
View Support Chat
|
|
||||||
</a>
|
|
||||||
</p>
|
|
||||||
<p style="font-size:12px;color:#888;">JQC Support System — automated notification.</p>
|
|
||||||
</body>
|
|
||||||
</html>"""
|
|
||||||
|
|
||||||
def _send():
|
|
||||||
try:
|
|
||||||
with current_app.app_context():
|
|
||||||
msg = Message(
|
|
||||||
subject = f'[JQC Support] Reply to #{ticket.id}: {ticket.subject}',
|
|
||||||
recipients = [ticket.customer.email],
|
|
||||||
html = html_body,
|
|
||||||
sender = current_app.config.get('MAIL_DEFAULT_SENDER'),
|
|
||||||
)
|
)
|
||||||
mail.send(msg)
|
db.session.commit()
|
||||||
except Exception as exc:
|
|
||||||
logger.error('SUPPORT | customer reply email failed: %s', exc)
|
|
||||||
|
|
||||||
threading.Thread(target=_send, daemon=True).start()
|
|
||||||
|
|||||||
Reference in New Issue
Block a user