05/22 Enhance codes and fix bugs 3
This commit is contained in:
+77
-2
@@ -10,11 +10,12 @@ from app import db
|
||||
from app.models import (Ticket, Comment, Attachment, Notification,
|
||||
TicketStatus, TicketPriority, TicketCategory,
|
||||
User, UserRole, KnowledgeBase, TicketLink, CannedResponse,
|
||||
KBFeedback, TicketTemplate, TicketSatisfaction)
|
||||
KBFeedback, TicketTemplate, TicketSatisfaction,
|
||||
TicketWatcher, TimeEntry)
|
||||
from app.services.notification_service import (
|
||||
notify_new_ticket, notify_status_change,
|
||||
notify_comment_added, notify_assignment,
|
||||
send_satisfaction_survey,
|
||||
send_satisfaction_survey, notify_watchers,
|
||||
)
|
||||
from app.services.log_service import log_action, log_ticket_history
|
||||
from app.services.sla_service import clear_sla_notification
|
||||
@@ -446,12 +447,21 @@ def ticket_detail(ticket_id):
|
||||
CannedResponse.category, CannedResponse.title
|
||||
).all()
|
||||
|
||||
is_watching = TicketWatcher.query.filter_by(
|
||||
ticket_id=ticket_id, user_id=current_user.id
|
||||
).first() is not None
|
||||
total_minutes = sum(e.minutes for e in ticket.time_entries.all())
|
||||
|
||||
return render_template('tickets/detail.html',
|
||||
ticket=ticket, comments=comments,
|
||||
it_staff=it_staff, history=history,
|
||||
statuses=_statuses(), priorities=_priorities(),
|
||||
linked_tickets=linked_tickets,
|
||||
canned_responses=canned_responses,
|
||||
now=datetime.utcnow(),
|
||||
is_watching=is_watching,
|
||||
total_minutes=total_minutes,
|
||||
time_entries=ticket.time_entries.order_by(TimeEntry.logged_at.desc()).limit(10).all(),
|
||||
)
|
||||
|
||||
|
||||
@@ -538,6 +548,14 @@ def update_ticket(ticket_id):
|
||||
if new_status == TicketStatus.RESOLVED:
|
||||
send_satisfaction_survey(ticket)
|
||||
|
||||
if changes:
|
||||
notify_watchers(
|
||||
ticket,
|
||||
event_title = f'Ticket {ticket.ticket_number} updated',
|
||||
event_message = '; '.join(changes),
|
||||
exclude_user_id = current_user.id,
|
||||
)
|
||||
|
||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||
from flask import jsonify
|
||||
return jsonify({'ok': True, 'changes': changes,
|
||||
@@ -976,6 +994,63 @@ def ticket_survey(token):
|
||||
quick_rating=quick_rating)
|
||||
|
||||
|
||||
# ─── Ticket Watchers ─────────────────────────────────────────────────────────
|
||||
|
||||
@tickets_bp.route('/tickets/<int:ticket_id>/watch', methods=['POST'])
|
||||
@login_required
|
||||
def watch_ticket(ticket_id):
|
||||
ticket = db.session.get(Ticket, ticket_id) or abort(404)
|
||||
if not current_user.is_it_staff and ticket.created_by_id != current_user.id:
|
||||
abort(403)
|
||||
existing = TicketWatcher.query.filter_by(ticket_id=ticket_id, user_id=current_user.id).first()
|
||||
if not existing:
|
||||
db.session.add(TicketWatcher(ticket_id=ticket_id, user_id=current_user.id))
|
||||
db.session.commit()
|
||||
from flask import jsonify
|
||||
return jsonify({'watching': True})
|
||||
|
||||
|
||||
@tickets_bp.route('/tickets/<int:ticket_id>/unwatch', methods=['POST'])
|
||||
@login_required
|
||||
def unwatch_ticket(ticket_id):
|
||||
ticket = db.session.get(Ticket, ticket_id) or abort(404)
|
||||
if not current_user.is_it_staff and ticket.created_by_id != current_user.id:
|
||||
abort(403)
|
||||
TicketWatcher.query.filter_by(ticket_id=ticket_id, user_id=current_user.id).delete()
|
||||
db.session.commit()
|
||||
from flask import jsonify
|
||||
return jsonify({'watching': False})
|
||||
|
||||
|
||||
# ─── Time Tracking ────────────────────────────────────────────────────────────
|
||||
|
||||
@tickets_bp.route('/tickets/<int:ticket_id>/log-time', methods=['POST'])
|
||||
@login_required
|
||||
def log_time(ticket_id):
|
||||
if not current_user.is_it_staff:
|
||||
abort(403)
|
||||
ticket = db.session.get(Ticket, ticket_id) or abort(404)
|
||||
try:
|
||||
minutes = int(request.form.get('minutes', 0))
|
||||
except (ValueError, TypeError):
|
||||
minutes = 0
|
||||
if minutes <= 0:
|
||||
flash('Please enter a valid number of minutes.', 'warning')
|
||||
return redirect(url_for('tickets.ticket_detail', ticket_id=ticket_id))
|
||||
note = request.form.get('note', '').strip()[:200]
|
||||
entry = TimeEntry(ticket_id=ticket_id, user_id=current_user.id, minutes=minutes, note=note)
|
||||
db.session.add(entry)
|
||||
log_action(current_user.id, 'time_log', 'ticket', ticket_id,
|
||||
f'minutes={minutes} note={note!r}')
|
||||
db.session.commit()
|
||||
from flask import jsonify
|
||||
if request.headers.get('X-Requested-With') == 'XMLHttpRequest':
|
||||
total = sum(e.minutes for e in ticket.time_entries.all())
|
||||
return jsonify({'ok': True, 'minutes': minutes, 'total_minutes': total})
|
||||
flash(f'{minutes} minutes logged.', 'success')
|
||||
return redirect(url_for('tickets.ticket_detail', ticket_id=ticket_id))
|
||||
|
||||
|
||||
# ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
def _sla_due_date(priority: str) -> 'datetime':
|
||||
|
||||
Reference in New Issue
Block a user