Reformat Resolves/Depends lists into tables. Allow adding/deleting of parent tickets

This commit is contained in:
Georg Lehner 2024-06-06 00:28:36 +02:00
parent 5b2441b994
commit d7afcb6578
5 changed files with 150 additions and 26 deletions

View File

@ -591,6 +591,14 @@ class TicketDependencyForm(forms.ModelForm):
model = TicketDependency model = TicketDependency
exclude = ('ticket',) exclude = ('ticket',)
class TicketResolvesForm(forms.ModelForm):
''' Adds this ticket as a dependency for a different ticket '''
class Meta:
model = TicketDependency
#exclude = ('depends_on',)
fields = ('ticket',)
class MultipleTicketSelectForm(forms.Form): class MultipleTicketSelectForm(forms.Form):
tickets = forms.ModelMultipleChoiceField( tickets = forms.ModelMultipleChoiceField(

View File

@ -130,34 +130,57 @@
</tr> </tr>
{% if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET != False %} {% if helpdesk_settings.HELPDESK_ENABLE_DEPENDENCIES_ON_TICKET != False %}
<tr> <tr>
<th class="table-active">{% trans "Depends" %}</th> <th class="table-active">
{% trans "Resolves" %}
<a data-toggle='tooltip' href='{% url 'helpdesk:ticket_resolves_add' ticket.id %}'
title='{% trans "Make this ticket resolve another ticket." %}'>
<button type="button" class="btn btn-primary btn-sm float-right"><i class="fas fa-link"></i></button></a>
</th>
<td colspan="3" class="p-0">
{% for resolves in ticket.depends_on.all %}
{% if forloop.first %}<table class="table table-borderless table-responsive m-0">{% endif %}
<tr>
<td> <td>
<a data-toggle='tooltip' href='{% url 'helpdesk:ticket_resolves_del' resolves.ticket.id resolves.id %}'
title='{% trans "Drop the dependency on this ticket. A ticket may not be closed until all tickets it depends on are closed or removed." %}'>
<button type="button" class="btn btn-warning btn-sm"><i class="fas fa-trash"></i></button></a>
</td>
<td>{{ resolves.ticket.get_status_display }}</td>
<td>
<a href='{{ resolves.ticket.get_absolute_url }}'>{{ resolves.ticket.ticket }} {{ resolves.ticket.title }}</a>
</td>
</tr>
{% if forloop.last %}</table>{% endif %}
{% empty %}
<small class="p-2">{% trans "This ticket does not resolve any other" %}</small>
{% endfor %}
</td>
</tr>
<tr>
<th class="table-active">
{% trans "Depends" %}
<a data-toggle='tooltip' href='{% url 'helpdesk:ticket_dependency_add' ticket.id %}' <a data-toggle='tooltip' href='{% url 'helpdesk:ticket_dependency_add' ticket.id %}'
title='{% trans "Make this ticket dependent on another ticket. A ticket may not be closed until all tickets it depends on are closed or removed." %}'> title='{% trans "Make this ticket dependent on another ticket. A ticket may not be closed until all tickets it depends on are closed or removed." %}'>
<button type="button" class="btn btn-primary btn-sm float-right"><i class="fas fa-link"></i></button></a> <button type="button" class="btn btn-primary btn-sm float-right"><i class="fas fa-link"></i></button></a>
{% for dep in ticket.ticketdependency.all %}
{% if forloop.first %}<ul>{% endif %} </th>
<li> <td colspan="3" class="p-0">
<a href='{{ dep.depends_on.get_absolute_url }}'>{{ dep.depends_on.ticket }} {{ dep.depends_on.title }}</a> {% for dep in dependencies %}
({{ dep.depends_on.get_status_display }}) {% if forloop.first %}<table class="table table-borderless table-hover table-responsive m-0">{% endif %}
<tr>
<td>
<a data-toggle='tooltip' href='{% url 'helpdesk:ticket_dependency_del' ticket.id dep.id %}' <a data-toggle='tooltip' href='{% url 'helpdesk:ticket_dependency_del' ticket.id dep.id %}'
title='{% trans "Drop the dependency on this ticket. A ticket may not be closed until all tickets it depends on are closed or removed." %}'> title='{% trans "Drop the dependency on this ticket. A ticket may not be closed until all tickets it depends on are closed or removed." %}'>
<button type="button" class="btn btn-warning btn-sm"><i class="fas fa-trash"></i></button></a></li> <button type="button" class="btn btn-warning btn-sm"><i class="fas fa-trash"></i></button></a>
{% if forloop.last %}</ul>{% endif %}
{% empty %}
{% trans "This ticket has no dependencies." %}
{% endfor %}
</td> </td>
<th class="table-active">{% trans "Resolves" %}</th> <td>{{ dep.depends_on.get_status_display }}</td>
<td> <td>
{% for parent in ticket.depends_on.all %} <a href='{{ dep.depends_on.get_absolute_url }}'>{{ dep.depends_on.ticket }} {{ dep.depends_on.title }}</a>
{% if forloop.first %}<ul>{% endif %}
<li> </td>
<a href='{{ parent.ticket.get_absolute_url }}'>{{ parent.ticket.ticket }} {{ parent.ticket.title }}</a> {% if forloop.last %}</table>{% endif %}
</li>
{% if forloop.last %}</ul>{% endif %}
{% empty %} {% empty %}
{% trans "This ticket does not resolve any other" %} <small class="p-2">{% trans "This ticket has no dependencies." %}</small>
{% endfor %} {% endfor %}
</td> </td>
</tr> </tr>

View File

@ -0,0 +1,37 @@
{% extends "helpdesk/base.html" %}{% load i18n %}
{% block helpdesk_title %}{% trans "Add Ticket as Dependency" %}{% endblock %}
{% block helpdesk_breadcrumb %}
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:list' %}">{% trans "Tickets" %}</a>
</li>
<li class="breadcrumb-item">
<a href="{% url 'helpdesk:list' %}{{ depends_on.id }}/">{{ depends_on.queue.slug }}-{{ depends_on.id }}</a>
</li>
<li class="breadcrumb-item active">{% trans "Add Ticket as Dependency" %}</li>
{% endblock %}
{% block helpdesk_body %}{% blocktrans %}
<h2>Add Ticket as Dependency</h2>
<p>Ticket: {{ depends_on }} </p>
<p>Adding this ticket as dependency will stop you resolving the selected ticket until this ticket has been resolved or closed.</p>{% endblocktrans %}
<form method='post' action='./'>
<fieldset>
<dl>{% for field in form %}
<dt><label for='id_{{ field.name }}'>{{ field.label }}</label></dt>
<dd>{{ field }}</dd>
{% if field.errors %}<dd class='error'>{{ field.errors }}</dd>{% endif %}
{% if field.help_text %}<dd class='form_help_text'>{{ field.help_text }}</dd>{% endif %}
{% endfor %}</dl>
</fieldset>
<input class="btn btn-primary" type='submit' value='{% trans "Save Ticket Dependency" %}' />
{% csrf_token %}</form>
{% endblock %}

View File

@ -88,6 +88,16 @@ urlpatterns = [
staff.ticket_dependency_del, staff.ticket_dependency_del,
name="ticket_dependency_del", name="ticket_dependency_del",
), ),
path(
"tickets/<int:ticket_id>/resolves/add/",
staff.ticket_resolves_add,
name="ticket_resolves_add",
),
path(
"tickets/<int:ticket_id>/resolves/delete/<int:dependency_id>/",
staff.ticket_resolves_del,
name="ticket_resolves_del",
),
path( path(
"tickets/<int:ticket_id>/attachment_delete/<int:attachment_id>/", "tickets/<int:ticket_id>/attachment_delete/<int:attachment_id>/",
staff.attachment_del, staff.attachment_del,

View File

@ -54,6 +54,7 @@ from helpdesk.forms import (
TicketCCUserForm, TicketCCUserForm,
TicketDependencyForm, TicketDependencyForm,
TicketForm, TicketForm,
TicketResolvesForm,
UserSettingsForm UserSettingsForm
) )
from helpdesk.lib import queue_template_context, safe_template_context from helpdesk.lib import queue_template_context, safe_template_context
@ -79,6 +80,7 @@ from helpdesk.user import HelpdeskUser
from helpdesk.update_ticket import update_ticket, subscribe_to_ticket_updates, return_ticketccstring_and_show_subscribe from helpdesk.update_ticket import update_ticket, subscribe_to_ticket_updates, return_ticketccstring_and_show_subscribe
import helpdesk.views.abstract_views as abstract_views import helpdesk.views.abstract_views as abstract_views
from helpdesk.views.permissions import MustBeStaffMixin from helpdesk.views.permissions import MustBeStaffMixin
import itertools
import json import json
import re import re
from rest_framework import status from rest_framework import status
@ -422,8 +424,16 @@ def view_ticket(request, ticket_id):
return redirect('helpdesk:edit_ticket_checklist', ticket.id, checklist.id) return redirect('helpdesk:edit_ticket_checklist', ticket.id, checklist.id)
open_dependencies = ticket.ticketdependency.filter(depends_on__status__in=Ticket.OPEN_STATUSES)
dependencies = [d for d in itertools.chain(
open_dependencies,
ticket.ticketdependency.all().difference(open_dependencies)
)]
return render(request, 'helpdesk/ticket.html', { return render(request, 'helpdesk/ticket.html', {
'ticket': ticket, 'ticket': ticket,
'dependencies': dependencies,
'open_dependencies': open_dependencies,
'submitter_userprofile_url': submitter_userprofile_url, 'submitter_userprofile_url': submitter_userprofile_url,
'form': form, 'form': form,
'active_users': users, 'active_users': users,
@ -1695,6 +1705,42 @@ def ticket_dependency_del(request, ticket_id, dependency_id):
ticket_dependency_del = staff_member_required(ticket_dependency_del) ticket_dependency_del = staff_member_required(ticket_dependency_del)
@helpdesk_staff_member_required
def ticket_resolves_add(request, ticket_id):
depends_on = get_object_or_404(Ticket, id=ticket_id)
ticket_perm_check(request, depends_on)
if request.method == 'POST':
form = TicketResolvesForm(request.POST)
if form.is_valid():
ticketdependency = form.save(commit=False)
ticketdependency.depends_on = depends_on
if ticketdependency.ticket != ticketdependency.depends_on:
ticketdependency.save()
return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on.id]))
else:
form = TicketResolvesForm()
return render(request, 'helpdesk/ticket_resolves_add.html', {
'depends_on': depends_on,
'form': form,
})
ticket_resolves_add = staff_member_required(ticket_resolves_add)
@helpdesk_staff_member_required
def ticket_resolves_del(request, ticket_id, dependency_id):
dependency = get_object_or_404(
TicketDependency, ticket__id=ticket_id, id=dependency_id)
depends_on_id = dependency.depends_on.id
if request.method == 'POST':
dependency.delete()
return HttpResponseRedirect(reverse('helpdesk:view', args=[depends_on_id]))
return render(request, 'helpdesk/ticket_dependency_del.html', {'dependency': dependency})
ticket_resolves_del = staff_member_required(ticket_resolves_del)
@helpdesk_staff_member_required @helpdesk_staff_member_required
def attachment_del(request, ticket_id, attachment_id): def attachment_del(request, ticket_id, attachment_id):
ticket = get_object_or_404(Ticket, id=ticket_id) ticket = get_object_or_404(Ticket, id=ticket_id)