mirror of
https://gitea.mueller.network/extern/django-helpdesk.git
synced 2024-11-25 01:13:31 +01:00
Merge pull request #1167 from samsplunks/followup_queue_change
Allow to track queue change in follow-ups
This commit is contained in:
commit
9f7c18e507
@ -1007,35 +1007,60 @@ class FollowUp(models.Model):
|
||||
def time_spent_calculation(self):
|
||||
"Returns timedelta according to rules settings."
|
||||
|
||||
open_hours = helpdesk_settings.FOLLOWUP_TIME_SPENT_OPENING_HOURS
|
||||
holidays = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_HOLIDAYS
|
||||
exclude_statuses = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_STATUSES
|
||||
exclude_queues = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES
|
||||
|
||||
# queryset for this ticket previous follow-ups
|
||||
prev_fup_qs = self.ticket.followup_set.all()
|
||||
if self.id:
|
||||
# if the follow-up exist in DB, only keep previous follow-ups
|
||||
prev_fup_qs = prev_fup_qs.filter(date__lt=self.date)
|
||||
|
||||
# handle exclusions
|
||||
|
||||
# extract previous status from follow-up or ticket for exclusion check
|
||||
if exclude_statuses:
|
||||
try:
|
||||
prev_fup = prev_fup_qs.latest("date")
|
||||
prev_status = prev_fup.new_status
|
||||
except ObjectDoesNotExist:
|
||||
prev_status = self.ticket.status
|
||||
|
||||
# don't calculate status exclusions
|
||||
if prev_status in exclude_statuses:
|
||||
return datetime.timedelta(seconds=0)
|
||||
|
||||
# find the previous queue for exclusion check
|
||||
if exclude_queues:
|
||||
try:
|
||||
prev_fup_ids = prev_fup_qs.values_list('id', flat=True)
|
||||
prev_queue_change = TicketChange.objects.filter(followup_id__in=prev_fup_ids,
|
||||
field=_('Queue')).latest('id')
|
||||
prev_queue = Queue.objects.get(pk=prev_queue_change.new_value)
|
||||
prev_queue_slug = prev_queue.slug
|
||||
except ObjectDoesNotExist:
|
||||
prev_queue_slug = self.ticket.queue.slug
|
||||
|
||||
# don't calculate queue exclusions
|
||||
if prev_queue_slug in exclude_queues:
|
||||
return datetime.timedelta(seconds=0)
|
||||
|
||||
# no exclusion found
|
||||
|
||||
time_spent_seconds = 0
|
||||
|
||||
# extract earliest from previous follow-up or ticket
|
||||
try:
|
||||
prev_fup_qs = self.ticket.followup_set.all()
|
||||
if self.id:
|
||||
prev_fup_qs = prev_fup_qs.filter(id__lt=self.id)
|
||||
prev_fup = prev_fup_qs.latest("date")
|
||||
earliest = prev_fup.date
|
||||
except ObjectDoesNotExist:
|
||||
earliest = self.ticket.created
|
||||
|
||||
# extract previous status from follow-up or ticket
|
||||
try:
|
||||
prev_fup_qs = self.ticket.followup_set.exclude(new_status__isnull=True)
|
||||
if self.id:
|
||||
prev_fup_qs = prev_fup_qs.filter(id__lt=self.id)
|
||||
prev_fup = prev_fup_qs.latest("date")
|
||||
prev_status = prev_fup.new_status
|
||||
except ObjectDoesNotExist:
|
||||
prev_status = self.ticket.status
|
||||
|
||||
# latest time is current follow-up date
|
||||
latest = self.date
|
||||
|
||||
time_spent_seconds = 0
|
||||
open_hours = helpdesk_settings.FOLLOWUP_TIME_SPENT_OPENING_HOURS
|
||||
holidays = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_HOLIDAYS
|
||||
exclude_statuses = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_STATUSES
|
||||
exclude_queues = helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES
|
||||
|
||||
# split time interval by days
|
||||
days = latest.toordinal() - earliest.toordinal()
|
||||
for day in range(days + 1):
|
||||
@ -1054,9 +1079,7 @@ class FollowUp(models.Model):
|
||||
start_day_time = middle_day_time.replace(hour=0, minute=0, second=0)
|
||||
end_day_time = middle_day_time.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
|
||||
if (start_day_time.strftime("%Y-%m-%d") not in holidays and
|
||||
prev_status not in exclude_statuses and
|
||||
self.ticket.queue.slug not in exclude_queues):
|
||||
if start_day_time.strftime("%Y-%m-%d") not in holidays:
|
||||
time_spent_seconds += daily_time_spent_calculation(start_day_time, end_day_time, open_hours)
|
||||
|
||||
return datetime.timedelta(seconds=time_spent_seconds)
|
||||
|
@ -150,6 +150,9 @@
|
||||
<dt><label for='id_priority'>{% trans "Priority" %}</label></dt>
|
||||
<dd><select id='id_priority' name='priority'>{% for p in priorities %}{% if p.0 == ticket.priority %}<option value='{{ p.0 }}' selected='selected'>{{ p.1 }}</option>{% else %}<option value='{{ p.0 }}'>{{ p.1 }}</option>{% endif %}{% endfor %}</select></dd>
|
||||
|
||||
<dt><label for='id_queue'>{% trans "Queue" %}</label></dt>
|
||||
<dd><select id='id_queue' name='queue'>{% for queue_id, queue_name in queues %}<option value='{{ queue_id }}'{% if queue_id == ticket.queue.id %} selected{% endif %}>{{ queue_name }}</option>{% endfor %}</select></dd>
|
||||
|
||||
<dt><label for='id_due_date'>{% trans "Due on" %}</label></dt>
|
||||
<dd>{{ form.due_date }}</dd>
|
||||
|
||||
|
@ -7,6 +7,7 @@ from helpdesk import settings as helpdesk_settings
|
||||
from helpdesk.models import CustomField, Queue, Ticket
|
||||
from helpdesk.templatetags.ticket_to_link import num_to_link
|
||||
from helpdesk.user import HelpdeskUser
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
try: # python 3
|
||||
@ -323,3 +324,46 @@ class TicketActionsTestCase(TestCase):
|
||||
ticket_1_follow_up, ticket_2_follow_up])
|
||||
self.assertEqual(list(ticket_1.ticketcc_set.all()),
|
||||
[ticket_1_cc, ticket_2_cc])
|
||||
|
||||
def test_update_ticket_queue(self):
|
||||
"""Tests whether user can change the queue in the Respond to this ticket section."""
|
||||
|
||||
# log user in
|
||||
self.loginUser()
|
||||
|
||||
# create ticket
|
||||
initial_data = {
|
||||
'title': 'Queue change ticket test',
|
||||
'queue': self.queue_public,
|
||||
'assigned_to': self.user,
|
||||
'status': Ticket.OPEN_STATUS,
|
||||
}
|
||||
ticket = Ticket.objects.create(**initial_data)
|
||||
ticket_id = ticket.id
|
||||
|
||||
# initial queue
|
||||
self.assertEqual(ticket.queue, self.queue_public)
|
||||
|
||||
# POST first follow-up with new queue
|
||||
new_queue = Queue.objects.create(
|
||||
title='New Queue',
|
||||
slug='newqueue',
|
||||
)
|
||||
post_data = {
|
||||
'comment': 'first follow-up in new queue',
|
||||
'queue': str(new_queue.id),
|
||||
}
|
||||
response = self.client.post(reverse('helpdesk:update',
|
||||
kwargs={'ticket_id': ticket_id}),
|
||||
post_data)
|
||||
|
||||
# queue was correctly modified
|
||||
ticket.refresh_from_db()
|
||||
self.assertEqual(ticket.queue, new_queue)
|
||||
|
||||
# ticket change was saved
|
||||
latest_fup = ticket.followup_set.latest('date')
|
||||
latest_ticketchange = latest_fup.ticketchange_set.latest('id')
|
||||
self.assertEqual(latest_ticketchange.field, _('Queue'))
|
||||
self.assertEqual(int(latest_ticketchange.old_value), self.queue_public.id)
|
||||
self.assertEqual(int(latest_ticketchange.new_value), new_queue.id)
|
@ -2,7 +2,10 @@
|
||||
from datetime import datetime, timedelta
|
||||
from django.contrib.auth.hashers import make_password
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.test import TestCase, override_settings
|
||||
from django.test.client import Client
|
||||
from django.urls import reverse
|
||||
from helpdesk.models import FollowUp, Queue, Ticket
|
||||
from helpdesk import settings as helpdesk_settings
|
||||
import uuid
|
||||
@ -33,6 +36,21 @@ class TimeSpentAutoTestCase(TestCase):
|
||||
is_active=True
|
||||
)
|
||||
|
||||
self.client = Client()
|
||||
|
||||
|
||||
def loginUser(self, is_staff=True):
|
||||
"""Create a staff user and login"""
|
||||
User = get_user_model()
|
||||
self.user = User.objects.create(
|
||||
username='User_1',
|
||||
is_staff=is_staff,
|
||||
)
|
||||
self.user.set_password('pass')
|
||||
self.user.save()
|
||||
self.client.login(username='User_1', password='pass')
|
||||
|
||||
|
||||
def test_add_two_followups_time_spent_auto(self):
|
||||
"""Tests automatic time_spent calculation."""
|
||||
# activate automatic calculation
|
||||
@ -252,3 +270,56 @@ class TimeSpentAutoTestCase(TestCase):
|
||||
|
||||
# Remove queues exclusion
|
||||
helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ()
|
||||
|
||||
def test_http_followup_time_spent_auto_exclude_queues(self):
|
||||
"""Tests automatic time_spent calculation queues exclusion with client"""
|
||||
|
||||
# activate automatic calculation
|
||||
helpdesk_settings.FOLLOWUP_TIME_SPENT_AUTO = True
|
||||
helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ('stop1', 'stop2')
|
||||
|
||||
# make staff user
|
||||
self.loginUser()
|
||||
|
||||
# create queues
|
||||
queues_sequence = ('new', 'stop1', 'resume1', 'stop2', 'resume2', 'end')
|
||||
queues = dict()
|
||||
for slug in queues_sequence:
|
||||
queues[slug] = Queue.objects.create(
|
||||
title=slug,
|
||||
slug=slug,
|
||||
)
|
||||
|
||||
# create ticket
|
||||
initial_data = {
|
||||
'title': 'Queue change ticket test',
|
||||
'queue': queues['new'],
|
||||
'assigned_to': self.user,
|
||||
'status': Ticket.OPEN_STATUS,
|
||||
'created': datetime.strptime('2024-04-09T08:00:00+00:00', "%Y-%m-%dT%H:%M:%S%z")
|
||||
}
|
||||
ticket = Ticket.objects.create(**initial_data)
|
||||
|
||||
# create a change queue follow-up every hour
|
||||
# first follow-up created at the same time of the ticket without queue change
|
||||
# new --1h--> stop1 --0h--> resume1 --1h--> stop2 --0h--> resume2 --1h--> end
|
||||
for (i, queue) in enumerate(queues_sequence):
|
||||
# create follow-up
|
||||
post_data = {
|
||||
'comment': 'ticket in queue {}'.format(queue),
|
||||
'queue': queues[queue].id,
|
||||
}
|
||||
response = self.client.post(reverse('helpdesk:update', kwargs={
|
||||
'ticket_id': ticket.id}), post_data)
|
||||
latest_fup = ticket.followup_set.latest('id')
|
||||
latest_fup.date = ticket.created + timedelta(hours=i)
|
||||
latest_fup.time_spent = None
|
||||
latest_fup.save()
|
||||
|
||||
# total ticket time for followups is 5 hours
|
||||
self.assertEqual(latest_fup.date - ticket.created, timedelta(hours=5))
|
||||
# calculated time spent with 2 hours exclusion is 3 hours
|
||||
self.assertEqual(ticket.time_spent.total_seconds(), timedelta(hours=3).total_seconds())
|
||||
|
||||
# remove queues exclusion
|
||||
helpdesk_settings.FOLLOWUP_TIME_SPENT_EXCLUDE_QUEUES = ()
|
@ -200,6 +200,7 @@ def update_ticket(
|
||||
owner=-1,
|
||||
ticket_title=None,
|
||||
priority=-1,
|
||||
queue=-1,
|
||||
new_status=None,
|
||||
time_spent=None,
|
||||
due_date=None,
|
||||
@ -213,6 +214,8 @@ def update_ticket(
|
||||
title = ticket.title
|
||||
if priority == -1:
|
||||
priority = ticket.priority
|
||||
if queue == -1:
|
||||
queue = ticket.queue.id
|
||||
if new_status is None:
|
||||
new_status = ticket.status
|
||||
if new_checklists is None:
|
||||
@ -302,6 +305,14 @@ def update_ticket(
|
||||
c.save()
|
||||
ticket.priority = priority
|
||||
|
||||
if queue != ticket.queue.id:
|
||||
c = f.ticketchange_set.create(
|
||||
field=_('Queue'),
|
||||
old_value=ticket.queue.id,
|
||||
new_value=queue,
|
||||
)
|
||||
ticket.queue_id = queue
|
||||
|
||||
if due_date != ticket.due_date:
|
||||
c = TicketChange(
|
||||
followup=f,
|
||||
|
@ -428,6 +428,7 @@ def view_ticket(request, ticket_id):
|
||||
'form': form,
|
||||
'active_users': users,
|
||||
'priorities': Ticket.PRIORITY_CHOICES,
|
||||
'queues': queue_choices,
|
||||
'preset_replies': PreSetReply.objects.filter(
|
||||
Q(queues=ticket.queue) | Q(queues__isnull=True)),
|
||||
'ticketcc_string': ticketcc_string,
|
||||
@ -566,6 +567,7 @@ def update_ticket_view(request, ticket_id, public=False):
|
||||
title = request.POST.get('title', '')
|
||||
owner = int(request.POST.get('owner', -1))
|
||||
priority = int(request.POST.get('priority', ticket.priority))
|
||||
queue = int(request.POST.get('queue', ticket.queue.id))
|
||||
|
||||
# Check if a change happened on checklists
|
||||
new_checklists = {}
|
||||
@ -589,6 +591,7 @@ def update_ticket_view(request, ticket_id, public=False):
|
||||
new_status == ticket.status,
|
||||
title == ticket.title,
|
||||
priority == int(ticket.priority),
|
||||
queue == int(ticket.queue.id),
|
||||
due_date == ticket.due_date,
|
||||
(owner == -1) or (not owner and not ticket.assigned_to) or
|
||||
(owner and User.objects.get(id=owner) == ticket.assigned_to),
|
||||
@ -605,6 +608,7 @@ def update_ticket_view(request, ticket_id, public=False):
|
||||
public = request.POST.get('public', False),
|
||||
owner = int(request.POST.get('owner', -1)),
|
||||
priority = int(request.POST.get('priority', -1)),
|
||||
queue = int(request.POST.get('queue', -1)),
|
||||
new_status = new_status,
|
||||
time_spent = get_time_spent_from_request(request),
|
||||
due_date = get_due_date_from_request_or_ticket(request, ticket),
|
||||
|
Loading…
Reference in New Issue
Block a user