diff --git a/docs/settings.rst b/docs/settings.rst index 97f51768..56199046 100644 --- a/docs/settings.rst +++ b/docs/settings.rst @@ -179,6 +179,95 @@ Options that change ticket updates Options that change ticket properties ------------------------------------- +- **HELPDESK_TICKET_OPEN_STATUS** Customize the id of OPEN_STATUS status. + + **Default:** ``HELPDESK_TICKET_OPEN_STATUS = 1`` + +- **HELPDESK_TICKET_REOPENED_STATUS** Customize the id of REOPENED_STATUS status. + + **Default:** ``HELPDESK_TICKET_REOPENED_STATUS = 2`` + +- **HELPDESK_TICKET_RESOLVED_STATUS** Customize the id of RESOLVED_STATUS status. + + **Default:** ``HELPDESK_TICKET_RESOLVED_STATUS = 3`` + +- **HELPDESK_TICKET_CLOSED_STATUS** Customize the id of CLOSED_STATUS status. + + **Default:** ``HELPDESK_TICKET_CLOSED_STATUS = 4`` + +- **HELPDESK_TICKET_DUPLICATE_STATUS** Customize the id of DUPLICATE_STATUS status. + + **Default:** ``HELPDESK_TICKET_DUPLICATE_STATUS = 5`` + +- **HELPDESK_TICKET_STATUS_CHOICES** Customize the list of status choices for all tickets. + + The **default** is below:: + + HELPDESK_TICKET_STATUS_CHOICES = ( + (HELPDESK_TICKET_OPEN_STATUS, _('Open')), + (HELPDESK_TICKET_REOPENED_STATUS, _('Reopened')), + (HELPDESK_TICKET_RESOLVED_STATUS, _('Resolved')), + (HELPDESK_TICKET_CLOSED_STATUS, _('Closed')), + (HELPDESK_TICKET_DUPLICATE_STATUS, _('Duplicate')), + ) + + If you wish to modify or introduce new status choices, you may add them like this:: + + # don't forget to import the gettext_lazy function at the begining of your settings file + from django.utils.translation import gettext_lazy as _ + + # explicitly define status list integer values + HELPDESK_TICKET_OPEN_STATUS = 1 + HELPDESK_TICKET_REOPENED_STATUS = 2 + HELPDESK_TICKET_RESOLVED_STATUS = 3 + HELPDESK_TICKET_CLOSED_STATUS = 4 + HELPDESK_TICKET_DUPLICATE_STATUS = 5 + HELPDESK_TICKET_FORKED_STATUS = 6 + + # create the list with associated labels + HELPDESK_TICKET_STATUS_CHOICES = ( + (HELPDESK_TICKET_OPEN_STATUS, _('Open')), + (HELPDESK_TICKET_REOPENED_STATUS, _('Reopened')), + (HELPDESK_TICKET_RESOLVED_STATUS, _('Resolved')), + (HELPDESK_TICKET_CLOSED_STATUS, _('Closed')), + (HELPDESK_TICKET_DUPLICATE_STATUS, _('Duplicate')), + (HELPDESK_TICKET_FORKED_STATUS, _('Forked')), + ) + +- **HELPDESK_TICKET_OPEN_STATUSES** Define the list of statuses to be considered as a type of open status. + + **Default:** ``HELPDESK_TICKET_OPEN_STATUSES = (HELPDESK_TICKET_OPEN_STATUS, HELPDESK_TICKET_REOPENED_STATUS)`` + + If you have added the ``HELPDESK_TICKET_FORKED_STATUS`` status and wish to have django-helpdesk treat it as an open status choice, add it to the list of OPEN_STATUSES like this:: + + HELPDESK_TICKET_OPEN_STATUSES = (HELPDESK_TICKET_OPEN_STATUS, + HELPDESK_TICKET_REOPENED_STATUS, + HELPDESK_TICKET_FORKED_STATUS) + +- **HELPDESK_TICKET_STATUS_CHOICES_FLOW** Customize the allowed state changes depending on the current state. + + The **default** is below:: + + HELPDESK_TICKET_STATUS_CHOICES_FLOW = { + HELPDESK_TICKET_OPEN_STATUS: (HELPDESK_TICKET_OPEN_STATUS, HELPDESK_TICKET_RESOLVED_STATUS, HELPDESK_TICKET_CLOSED_STATUS, HELPDESK_TICKET_DUPLICATE_STATUS,), + HELPDESK_TICKET_REOPENED_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_RESOLVED_STATUS, HELPDESK_TICKET_CLOSED_STATUS, HELPDESK_TICKET_DUPLICATE_STATUS,), + HELPDESK_TICKET_RESOLVED_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_RESOLVED_STATUS, HELPDESK_TICKET_CLOSED_STATUS,), + HELPDESK_TICKET_CLOSED_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_CLOSED_STATUS,), + HELPDESK_TICKET_DUPLICATE_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_DUPLICATE_STATUS,), + } + + If you wish to modify or have introduce new status choices, you may configure their status change flow like this:: + + # adding HELPDESK_TICKET_FORKED_STATUS to the other allowed states flow and defining its own flow + HELPDESK_TICKET_STATUS_CHOICES_FLOW = { + HELPDESK_TICKET_OPEN_STATUS: (HELPDESK_TICKET_OPEN_STATUS, HELPDESK_TICKET_FORKED_STATUS, HELPDESK_TICKET_RESOLVED_STATUS, HELPDESK_TICKET_CLOSED_STATUS, HELPDESK_TICKET_DUPLICATE_STATUS,), + HELPDESK_TICKET_REOPENED_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_FORKED_STATUS, HELPDESK_TICKET_RESOLVED_STATUS, HELPDESK_TICKET_CLOSED_STATUS, HELPDESK_TICKET_DUPLICATE_STATUS,), + HELPDESK_TICKET_RESOLVED_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_RESOLVED_STATUS, HELPDESK_TICKET_CLOSED_STATUS,), + HELPDESK_TICKET_CLOSED_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_CLOSED_STATUS,), + HELPDESK_TICKET_DUPLICATE_STATUS: (HELPDESK_TICKET_REOPENED_STATUS, HELPDESK_TICKET_DUPLICATE_STATUS,), + HELPDESK_TICKET_FORKED_STATUS: (HELPDESK_TICKET_OPEN_STATUS, HELPDESK_TICKET_FORKED_STATUS, HELPDESK_TICKET_RESOLVED_STATUS, HELPDESK_TICKET_CLOSED_STATUS, HELPDESK_TICKET_DUPLICATE_STATUS,), + } + - **HELPDESK_TICKET_PRIORITY_CHOICES** Customize the priority choices for all tickets. The **default** is below:: diff --git a/helpdesk/forms.py b/helpdesk/forms.py index f9abe18c..e63072cd 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -186,10 +186,10 @@ class EditFollowUpForm(forms.ModelForm): exclude = ('date', 'user',) def __init__(self, *args, **kwargs): - """Filter not openned tickets here.""" + """Filter not opened tickets here.""" super(EditFollowUpForm, self).__init__(*args, **kwargs) self.fields["ticket"].queryset = Ticket.objects.filter( - status__in=(Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS)) + status__in=Ticket.OPEN_STATUSES) class AbstractTicketForm(CustomFieldMixin, forms.Form): diff --git a/helpdesk/management/commands/escalate_tickets.py b/helpdesk/management/commands/escalate_tickets.py index d0a920a8..82cb1948 100644 --- a/helpdesk/management/commands/escalate_tickets.py +++ b/helpdesk/management/commands/escalate_tickets.py @@ -83,9 +83,12 @@ def escalate_tickets(queues, verbose): if verbose: print("Processing: %s" % q) + Q_OPEN_STATUSES = Q() + for open_status in Ticket.OPEN_STATUSES: + Q_OPEN_STATUSES |= Q(status=open_status) + for t in q.ticket_set.filter( - Q(status=Ticket.OPEN_STATUS) | - Q(status=Ticket.REOPENED_STATUS) + Q_OPEN_STATUSES ).exclude( priority=1 ).filter( diff --git a/helpdesk/models.py b/helpdesk/models.py index 02e09ca1..b585f9ea 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -463,19 +463,15 @@ class Ticket(models.Model): the dashboard to prompt users to take ownership of them. """ - OPEN_STATUS = 1 - REOPENED_STATUS = 2 - RESOLVED_STATUS = 3 - CLOSED_STATUS = 4 - DUPLICATE_STATUS = 5 + OPEN_STATUS = helpdesk_settings.OPEN_STATUS + REOPENED_STATUS = helpdesk_settings.REOPENED_STATUS + RESOLVED_STATUS = helpdesk_settings.RESOLVED_STATUS + CLOSED_STATUS = helpdesk_settings.CLOSED_STATUS + DUPLICATE_STATUS = helpdesk_settings.DUPLICATE_STATUS - STATUS_CHOICES = ( - (OPEN_STATUS, _('Open')), - (REOPENED_STATUS, _('Reopened')), - (RESOLVED_STATUS, _('Resolved')), - (CLOSED_STATUS, _('Closed')), - (DUPLICATE_STATUS, _('Duplicate')), - ) + STATUS_CHOICES = helpdesk_settings.TICKET_STATUS_CHOICES + OPEN_STATUSES = helpdesk_settings.TICKET_OPEN_STATUSES + STATUS_CHOICES_FLOW = helpdesk_settings.TICKET_STATUS_CHOICES_FLOW PRIORITY_CHOICES = helpdesk_settings.TICKET_PRIORITY_CHOICES @@ -715,6 +711,22 @@ class Ticket(models.Model): return u'%s%s%s' % (self.get_status_display(), held_msg, dep_msg) get_status = property(_get_status) + def _get_allowed_status_flow(self): + """ + Returns the list of allowed ticket status modifications for current state. + """ + status_id_list = self.STATUS_CHOICES_FLOW.get(self.status, ()) + if status_id_list: + # keep defined statuses in order and add labels for display + status_dict = dict(helpdesk_settings.TICKET_STATUS_CHOICES) + new_statuses = [(status_id, status_dict.get(status_id, _('No label'))) + for status_id in status_id_list] + else: + # defaults to all choices if status was not mapped + new_statuses = helpdesk_settings.TICKET_STATUS_CHOICES + return new_statuses + get_allowed_status_flow = property(_get_allowed_status_flow) + def _get_ticket_url(self): """ Returns a publicly-viewable URL for this ticket, used when giving @@ -771,9 +783,8 @@ class Ticket(models.Model): True = any dependencies are resolved False = There are non-resolved dependencies """ - OPEN_STATUSES = (Ticket.OPEN_STATUS, Ticket.REOPENED_STATUS) return TicketDependency.objects.filter(ticket=self).filter( - depends_on__status__in=OPEN_STATUSES).count() == 0 + depends_on__status__in=Ticket.OPEN_STATUSES).count() == 0 can_be_resolved = property(_can_be_resolved) def get_submitter_userprofile(self): diff --git a/helpdesk/settings.py b/helpdesk/settings.py index e23a59cf..cf5ca644 100644 --- a/helpdesk/settings.py +++ b/helpdesk/settings.py @@ -103,6 +103,42 @@ ALLOWED_URL_SCHEMES = getattr(settings, 'ALLOWED_URL_SCHEMES', ( 'file', 'ftp', 'ftps', 'http', 'https', 'irc', 'mailto', 'sftp', 'ssh', 'tel', 'telnet', 'tftp', 'vnc', 'xmpp', )) +# Ticket status choices +OPEN_STATUS = getattr(settings, 'HELPDESK_TICKET_OPEN_STATUS', 1) +REOPENED_STATUS = getattr(settings, 'HELPDESK_TICKET_REOPENED_STATUS', 2) +RESOLVED_STATUS = getattr(settings, 'HELPDESK_TICKET_RESOLVED_STATUS', 3) +CLOSED_STATUS = getattr(settings, 'HELPDESK_TICKET_CLOSED_STATUS', 4) +DUPLICATE_STATUS = getattr(settings, 'HELPDESK_TICKET_DUPLICATE_STATUS', 5) + +DEFAULT_TICKET_STATUS_CHOICES = ( + (OPEN_STATUS, _('Open')), + (REOPENED_STATUS, _('Reopened')), + (RESOLVED_STATUS, _('Resolved')), + (CLOSED_STATUS, _('Closed')), + (DUPLICATE_STATUS, _('Duplicate')), +) +TICKET_STATUS_CHOICES = getattr(settings, + 'HELPDESK_TICKET_STATUS_CHOICES', + DEFAULT_TICKET_STATUS_CHOICES) + +# List of status choices considered as "open" +DEFAULT_TICKET_OPEN_STATUSES = (OPEN_STATUS, REOPENED_STATUS) +TICKET_OPEN_STATUSES = getattr(settings, + 'HELPDESK_TICKET_OPEN_STATUSES', + DEFAULT_TICKET_OPEN_STATUSES) + +# New status list choices depending on current ticket status +DEFAULT_TICKET_STATUS_CHOICES_FLOW = { + OPEN_STATUS: (OPEN_STATUS, RESOLVED_STATUS, CLOSED_STATUS, DUPLICATE_STATUS,), + REOPENED_STATUS: (REOPENED_STATUS, RESOLVED_STATUS, CLOSED_STATUS, DUPLICATE_STATUS,), + RESOLVED_STATUS: (REOPENED_STATUS, RESOLVED_STATUS, CLOSED_STATUS,), + CLOSED_STATUS: (REOPENED_STATUS, CLOSED_STATUS,), + DUPLICATE_STATUS: (REOPENED_STATUS, DUPLICATE_STATUS,), +} +TICKET_STATUS_CHOICES_FLOW = getattr(settings, + 'HELPDESK_TICKET_STATUS_CHOICES_FLOW', + DEFAULT_TICKET_STATUS_CHOICES_FLOW) + # Ticket priority choices DEFAULT_TICKET_PRIORITY_CHOICES = ( (1, _('1. Critical')), @@ -112,8 +148,8 @@ DEFAULT_TICKET_PRIORITY_CHOICES = ( (5, _('5. Very Low')), ) TICKET_PRIORITY_CHOICES = getattr(settings, - 'HELPDESK_TICKET_PRIORITY_CHOICES', - DEFAULT_TICKET_PRIORITY_CHOICES) + 'HELPDESK_TICKET_PRIORITY_CHOICES', + DEFAULT_TICKET_PRIORITY_CHOICES) ############################ # options for public pages # diff --git a/helpdesk/templates/helpdesk/public_view_ticket.html b/helpdesk/templates/helpdesk/public_view_ticket.html index cdca2de7..62f37a43 100644 --- a/helpdesk/templates/helpdesk/public_view_ticket.html +++ b/helpdesk/templates/helpdesk/public_view_ticket.html @@ -107,31 +107,12 @@ {% if not ticket.can_be_resolved %}
{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}
{% endif %} {% if ticket.status == 1 %} - - {% endif %} - {% if ticket.status == 2 %} + + {% else %}
- - - - -
- {% endif %} - {% if ticket.status == 3 %} -
- - - -
- {% endif %} - {% if ticket.status == 4 %} -
-
- {% endif %} - {% if ticket.status == 5 %} -
- - + {% for status_choice in ticket.get_allowed_status_flow %} + + {% endfor %}
{% endif %} diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html index b3d814c0..b4eb61db 100644 --- a/helpdesk/templates/helpdesk/ticket.html +++ b/helpdesk/templates/helpdesk/ticket.html @@ -109,39 +109,11 @@
{% if not ticket.can_be_resolved %}
{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}
{% endif %} - {% if ticket.status == 1 %}
- - - - + {% for status_choice in ticket.get_allowed_status_flow %} + + {% endfor %}
- {% endif %} - {% if ticket.status == 2 %} -
- - - - -
- {% endif %} - {% if ticket.status == 3 %} -
- - - -
- {% endif %} - {% if ticket.status == 4 %} -
-
- {% endif %} - {% if ticket.status == 5 %} -
- - -
- {% endif %} {% if helpdesk_settings.HELPDESK_UPDATE_PUBLIC_DEFAULT %} diff --git a/helpdesk/views/feeds.py b/helpdesk/views/feeds.py index cccf4b19..0653a242 100644 --- a/helpdesk/views/feeds.py +++ b/helpdesk/views/feeds.py @@ -19,6 +19,11 @@ from helpdesk.models import FollowUp, Queue, Ticket User = get_user_model() +Q_OPEN_STATUSES = Q() +for open_status in Ticket.OPEN_STATUSES: + Q_OPEN_STATUSES |= Q(status=open_status) + + class OpenTicketsByUser(Feed): title_template = 'helpdesk/rss/ticket_title.html' description_template = 'helpdesk/rss/ticket_description.html' @@ -73,15 +78,11 @@ class OpenTicketsByUser(Feed): assigned_to=obj['user'] ).filter( queue=obj['queue'] - ).filter( - Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS) - ) + ).filter(Q_OPEN_STATUSES) else: return Ticket.objects.filter( assigned_to=obj['user'] - ).filter( - Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS) - ) + ).filter(Q_OPEN_STATUSES) def item_pubdate(self, item): return item.created @@ -104,9 +105,7 @@ class UnassignedTickets(Feed): def items(self, obj): return Ticket.objects.filter( assigned_to__isnull=True - ).filter( - Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS) - ) + ).filter(Q_OPEN_STATUSES) def item_pubdate(self, item): return item.created @@ -157,9 +156,7 @@ class OpenTicketsByQueue(Feed): def items(self, obj): return Ticket.objects.filter( queue=obj - ).filter( - Q(status=Ticket.OPEN_STATUS) | Q(status=Ticket.REOPENED_STATUS) - ) + ).filter(Q_OPEN_STATUSES) def item_pubdate(self, item): return item.created