04/07 updated linking ticket
This commit is contained in:
@@ -188,3 +188,46 @@ def search_users():
|
||||
}
|
||||
for u in users
|
||||
]})
|
||||
|
||||
|
||||
# ─── Ticket Search API ────────────────────────────────────────────────────────
|
||||
|
||||
@api_bp.route('/tickets/search')
|
||||
@login_required
|
||||
def search_tickets():
|
||||
"""Return tickets matching a query string, used by the link-ticket picker.
|
||||
|
||||
Searches ticket_number and title (case-insensitive).
|
||||
?q=<search term> — required, min 1 char
|
||||
?exclude=<id> — ticket ID to exclude (the current ticket)
|
||||
Restricted to IT staff only.
|
||||
"""
|
||||
if not current_user.is_it_staff:
|
||||
return jsonify({'error': 'Forbidden'}), 403
|
||||
|
||||
q = request.args.get('q', '').strip()
|
||||
exclude_id = request.args.get('exclude', 0, type=int)
|
||||
|
||||
if not q:
|
||||
return jsonify({'tickets': []})
|
||||
|
||||
from app.models import Ticket
|
||||
query = Ticket.query.filter(
|
||||
Ticket.ticket_number.ilike(f'%{q}%') |
|
||||
Ticket.title.ilike(f'%{q}%')
|
||||
)
|
||||
if exclude_id:
|
||||
query = query.filter(Ticket.id != exclude_id)
|
||||
|
||||
results = query.order_by(Ticket.created_at.desc()).limit(10).all()
|
||||
logger.info(f'[TICKET SEARCH] q="{q}" results={len(results)} user_id={current_user.id}')
|
||||
return jsonify({'tickets': [
|
||||
{
|
||||
'id' : t.id,
|
||||
'ticket_number': t.ticket_number,
|
||||
'title' : t.title,
|
||||
'status' : t.status,
|
||||
'priority' : t.priority,
|
||||
}
|
||||
for t in results
|
||||
]})
|
||||
+18
-8
@@ -687,22 +687,32 @@ def link_ticket(ticket_id):
|
||||
abort(403)
|
||||
|
||||
ticket = db.session.get(Ticket, ticket_id) or abort(404)
|
||||
other_id = request.form.get('linked_ticket_id', type=int)
|
||||
raw_input = request.form.get('linked_ticket_id', '').strip()
|
||||
link_type = request.form.get('link_type', 'related')
|
||||
|
||||
if not other_id:
|
||||
flash('Please specify a ticket to link.', 'danger')
|
||||
if not raw_input:
|
||||
flash('Please search for and select a ticket to link.', 'danger')
|
||||
return redirect(url_for('tickets.ticket_detail', ticket_id=ticket_id))
|
||||
|
||||
# Resolve by ticket_number (e.g. TKT-20260406-0001) or numeric DB id
|
||||
if raw_input.upper().startswith('TKT-'):
|
||||
other = Ticket.query.filter_by(ticket_number=raw_input.upper()).first()
|
||||
else:
|
||||
try:
|
||||
other = db.session.get(Ticket, int(raw_input))
|
||||
except ValueError:
|
||||
other = Ticket.query.filter_by(ticket_number=raw_input.upper()).first()
|
||||
|
||||
if not other:
|
||||
flash(f'Ticket "{raw_input}" not found.', 'danger')
|
||||
return redirect(url_for('tickets.ticket_detail', ticket_id=ticket_id))
|
||||
|
||||
other_id = other.id
|
||||
|
||||
if other_id == ticket_id:
|
||||
flash('A ticket cannot be linked to itself.', 'danger')
|
||||
return redirect(url_for('tickets.ticket_detail', ticket_id=ticket_id))
|
||||
|
||||
other = db.session.get(Ticket, other_id)
|
||||
if not other:
|
||||
flash(f'Ticket #{other_id} not found.', 'danger')
|
||||
return redirect(url_for('tickets.ticket_detail', ticket_id=ticket_id))
|
||||
|
||||
# Enforce canonical ordering (smaller id first) for the uniqueness constraint
|
||||
a, b = sorted([ticket_id, other_id])
|
||||
existing = TicketLink.query.filter_by(ticket_id=a, linked_ticket_id=b).first()
|
||||
|
||||
Reference in New Issue
Block a user