04/09: fixed searching ticket, and deleting/bulk deleting tickets

This commit is contained in:
2026-04-09 13:10:56 -04:00
parent cc81e0cca3
commit 05ecf5a624
3 changed files with 213 additions and 5 deletions
+78 -3
View File
@@ -408,7 +408,7 @@ def all_tickets():
from sqlalchemy import func
submitter_alias = db.aliased(User)
assignee_alias = db.aliased(User)
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '', 'g')
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '')
q = (
q
.outerjoin(submitter_alias, submitter_alias.id == Ticket.created_by_id)
@@ -431,6 +431,44 @@ def all_tickets():
search=search)
@admin_bp.route('/tickets/<int:ticket_id>/delete', methods=['POST'])
@login_required
@admin_required
def delete_ticket(ticket_id):
"""Permanently delete a single ticket and all its related data.
Cascades handled by SQLAlchemy relationships (cascade='all, delete-orphan'):
comments, attachments (rows), notifications, history, satisfaction, links.
Physical attachment files on disk are deleted explicitly before the commit.
"""
ticket = db.session.get(Ticket, ticket_id) or abort(404)
ticket_number = ticket.ticket_number
ticket_title = ticket.title
# Delete physical attachment files from disk before removing DB rows
upload_dir = current_app.config.get('UPLOAD_FOLDER', '')
for attachment in ticket.attachments.all():
if attachment.stored_name and upload_dir:
filepath = os.path.join(upload_dir, attachment.stored_name)
if os.path.isfile(filepath):
try:
os.remove(filepath)
except OSError as exc:
logger.warning(f'[TICKET DELETE] Could not remove file '
f'{filepath}: {exc}')
log_action(current_user.id, 'admin_ticket_delete', 'ticket', ticket.id,
f'ticket_number={ticket_number} title={ticket_title!r}')
db.session.delete(ticket)
db.session.commit()
logger.info(f'[TICKET DELETE] ticket_number={ticket_number} '
f'ticket_id={ticket_id} by admin_id={current_user.id}')
flash(f'Ticket {ticket_number} has been permanently deleted.', 'success')
return redirect(url_for('admin.all_tickets'))
# ─── Knowledge Base Management ────────────────────────────────────────────────
@admin_bp.route('/kb')
@@ -793,7 +831,7 @@ def export_tickets():
from sqlalchemy import func
submitter_alias = db.aliased(User)
assignee_alias = db.aliased(User)
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '', 'g')
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '')
q = (
q
.outerjoin(submitter_alias, submitter_alias.id == Ticket.created_by_id)
@@ -955,16 +993,53 @@ def bulk_ticket_action():
flash('No tickets selected.', 'warning')
return redirect(url_for('admin.all_tickets'))
valid_actions = ('resolve', 'close', 'assign_me', 'unassign')
valid_actions = ('resolve', 'close', 'assign_me', 'unassign', 'delete')
if action not in valid_actions:
flash('Invalid action.', 'danger')
return redirect(url_for('admin.all_tickets'))
# Bulk delete is admin-only
if action == 'delete' and not current_user.is_admin:
flash('Only administrators may delete tickets.', 'danger')
return redirect(url_for('admin.all_tickets'))
tickets = Ticket.query.filter(Ticket.id.in_(ticket_ids)).all()
now = datetime.utcnow()
count = 0
notif_tickets = [] # collect for post-commit notifications
# ── Bulk delete: handle separately so we can early-return cleanly ─────────
if action == 'delete':
upload_dir = current_app.config.get('UPLOAD_FOLDER', '')
for ticket in tickets:
for attachment in ticket.attachments.all():
if attachment.stored_name and upload_dir:
filepath = os.path.join(upload_dir, attachment.stored_name)
if os.path.isfile(filepath):
try:
os.remove(filepath)
except OSError as exc:
logger.warning(f'[BULK DELETE] Could not remove file '
f'{filepath}: {exc}')
log_action(current_user.id, 'admin_ticket_delete', 'ticket',
ticket.id,
f'bulk=true ticket_number={ticket.ticket_number} title={ticket.title!r}')
db.session.delete(ticket)
count += 1
db.session.commit()
logger.info(f'[BULK DELETE] deleted={count} ticket_ids={ticket_ids} '
f'by admin_id={current_user.id}')
flash(f'{count} ticket{"s" if count != 1 else ""} permanently deleted.', 'success')
return redirect(url_for('admin.all_tickets',
status = request.form.get('status', ''),
priority = request.form.get('priority', ''),
assigned = request.form.get('assigned', ''),
q = request.form.get('q', ''),
page = request.form.get('page', 1),
))
# ─────────────────────────────────────────────────────────────────────────
for ticket in tickets:
old_status = ticket.status
old_assigned = ticket.assigned_to_id
+1 -1
View File
@@ -330,7 +330,7 @@ def ticket_list():
from app.models import Comment
from sqlalchemy import func
assignee_alias = db.aliased(User)
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '', 'g')
stripped_body = func.regexp_replace(Comment.body, r'<[^>]+>', '')
query = (
query
.outerjoin(Comment, Comment.ticket_id == Ticket.id)