From 8b6fbd89657d36de3cc856fe2f0356188abbc2d2 Mon Sep 17 00:00:00 2001 From: Ross Poulton Date: Tue, 10 May 2011 19:27:11 +1000 Subject: [PATCH] Add ticket dependency so you can't resolve a ticket until it's dependents are resolved. Addresses GH-43 --- helpdesk/forms.py | 6 +- ..._unique_ticketdependency_ticket_depends.py | 237 ++++++++++++++++++ helpdesk/models.py | 35 +++ helpdesk/templates/helpdesk/ticket.html | 9 +- .../helpdesk/ticket_dependency_add.html | 25 ++ .../helpdesk/ticket_dependency_del.html | 14 ++ .../templates/helpdesk/ticket_desc_table.html | 13 + helpdesk/urls.py | 8 + helpdesk/views/staff.py | 35 ++- 9 files changed, 375 insertions(+), 7 deletions(-) create mode 100644 helpdesk/migrations/0002_auto__add_ticketdependency__add_unique_ticketdependency_ticket_depends.py create mode 100644 helpdesk/templates/helpdesk/ticket_dependency_add.html create mode 100644 helpdesk/templates/helpdesk/ticket_dependency_del.html diff --git a/helpdesk/forms.py b/helpdesk/forms.py index 076df45b..502a90bb 100644 --- a/helpdesk/forms.py +++ b/helpdesk/forms.py @@ -16,7 +16,7 @@ from django.contrib.auth.models import User from django.utils.translation import ugettext as _ from helpdesk.lib import send_templated_mail -from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC, CustomField, TicketCustomFieldValue +from helpdesk.models import Ticket, Queue, FollowUp, Attachment, IgnoreEmail, TicketCC, CustomField, TicketCustomFieldValue, TicketDependency from helpdesk.settings import HAS_TAG_SUPPORT class EditTicketForm(forms.ModelForm): @@ -574,3 +574,7 @@ class TicketCCForm(forms.ModelForm): model = TicketCC exclude = ('ticket',) +class TicketDependencyForm(forms.ModelForm): + class Meta: + model = TicketDependency + exclude = ('ticket',) diff --git a/helpdesk/migrations/0002_auto__add_ticketdependency__add_unique_ticketdependency_ticket_depends.py b/helpdesk/migrations/0002_auto__add_ticketdependency__add_unique_ticketdependency_ticket_depends.py new file mode 100644 index 00000000..47a1f321 --- /dev/null +++ b/helpdesk/migrations/0002_auto__add_ticketdependency__add_unique_ticketdependency_ticket_depends.py @@ -0,0 +1,237 @@ +# encoding: utf-8 +import datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + +class Migration(SchemaMigration): + + def forwards(self, orm): + + # Adding model 'TicketDependency' + db.create_table('helpdesk_ticketdependency', ( + ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('ticket', self.gf('django.db.models.fields.related.ForeignKey')(related_name='ticket', to=orm['helpdesk.Ticket'])), + ('depends_on', self.gf('django.db.models.fields.related.ForeignKey')(related_name='depends_on', to=orm['helpdesk.Ticket'])), + )) + db.send_create_signal('helpdesk', ['TicketDependency']) + + # Adding unique constraint on 'TicketDependency', fields ['ticket', 'depends_on'] + db.create_unique('helpdesk_ticketdependency', ['ticket_id', 'depends_on_id']) + + + def backwards(self, orm): + + # Removing unique constraint on 'TicketDependency', fields ['ticket', 'depends_on'] + db.delete_unique('helpdesk_ticketdependency', ['ticket_id', 'depends_on_id']) + + # Deleting model 'TicketDependency' + db.delete_table('helpdesk_ticketdependency') + + + models = { + 'auth.group': { + 'Meta': {'object_name': 'Group'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '80', 'unique': 'True'}), + 'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}) + }, + 'auth.permission': { + 'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'}, + 'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '50'}) + }, + 'auth.user': { + 'Meta': {'object_name': 'User'}, + 'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}), + 'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}), + 'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}), + 'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}), + 'username': ('django.db.models.fields.CharField', [], {'max_length': '30', 'unique': 'True'}) + }, + 'contenttypes.contenttype': { + 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, + 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'helpdesk.attachment': { + 'Meta': {'ordering': "['filename']", 'object_name': 'Attachment'}, + 'file': ('django.db.models.fields.files.FileField', [], {'max_length': '100'}), + 'filename': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'followup': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.FollowUp']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mime_type': ('django.db.models.fields.CharField', [], {'max_length': '30'}), + 'size': ('django.db.models.fields.IntegerField', [], {}) + }, + 'helpdesk.customfield': { + 'Meta': {'object_name': 'CustomField'}, + 'data_type': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'decimal_places': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'help_text': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'label': ('django.db.models.fields.CharField', [], {'max_length': "'30'"}), + 'list_values': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'max_length': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'name': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'unique': 'True', 'db_index': 'True'}), + 'required': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'staff_only': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + 'helpdesk.emailtemplate': { + 'Meta': {'ordering': "['template_name', 'locale']", 'object_name': 'EmailTemplate'}, + 'heading': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'html': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'locale': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'plain_text': ('django.db.models.fields.TextField', [], {}), + 'subject': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'template_name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'helpdesk.escalationexclusion': { + 'Meta': {'object_name': 'EscalationExclusion'}, + 'date': ('django.db.models.fields.DateField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'queues': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['helpdesk.Queue']", 'symmetrical': 'False', 'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.followup': { + 'Meta': {'ordering': "['date']", 'object_name': 'FollowUp'}, + 'comment': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'date': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2011, 5, 10, 12, 20, 8, 45416)'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'new_status': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.Ticket']"}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.ignoreemail': { + 'Meta': {'object_name': 'IgnoreEmail'}, + 'date': ('django.db.models.fields.DateField', [], {'blank': 'True'}), + 'email_address': ('django.db.models.fields.CharField', [], {'max_length': '150'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'keep_in_mailbox': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'queues': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['helpdesk.Queue']", 'symmetrical': 'False', 'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.kbcategory': { + 'Meta': {'ordering': "['title']", 'object_name': 'KBCategory'}, + 'description': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}) + }, + 'helpdesk.kbitem': { + 'Meta': {'ordering': "['title']", 'object_name': 'KBItem'}, + 'answer': ('django.db.models.fields.TextField', [], {}), + 'category': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.KBCategory']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_updated': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}), + 'question': ('django.db.models.fields.TextField', [], {}), + 'recommendations': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'votes': ('django.db.models.fields.IntegerField', [], {'default': '0'}) + }, + 'helpdesk.presetreply': { + 'Meta': {'ordering': "['name']", 'object_name': 'PreSetReply'}, + 'body': ('django.db.models.fields.TextField', [], {}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'queues': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['helpdesk.Queue']", 'symmetrical': 'False', 'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.queue': { + 'Meta': {'ordering': "('title',)", 'object_name': 'Queue'}, + 'allow_email_submission': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'allow_public_submission': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_address': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'email_box_host': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'email_box_imap_folder': ('django.db.models.fields.CharField', [], {'max_length': '100', 'null': 'True', 'blank': 'True'}), + 'email_box_interval': ('django.db.models.fields.IntegerField', [], {'default': "'5'", 'null': 'True', 'blank': 'True'}), + 'email_box_last_check': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'email_box_pass': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'email_box_port': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'email_box_ssl': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email_box_type': ('django.db.models.fields.CharField', [], {'max_length': '5', 'null': 'True', 'blank': 'True'}), + 'email_box_user': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'escalate_days': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'locale': ('django.db.models.fields.CharField', [], {'max_length': '10', 'null': 'True', 'blank': 'True'}), + 'new_ticket_cc': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}), + 'slug': ('django.db.models.fields.SlugField', [], {'max_length': '50', 'db_index': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'updated_ticket_cc': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.savedsearch': { + 'Meta': {'object_name': 'SavedSearch'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'query': ('django.db.models.fields.TextField', [], {}), + 'shared': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']"}) + }, + 'helpdesk.ticket': { + 'Meta': {'object_name': 'Ticket'}, + 'assigned_to': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'assigned_to'", 'blank': 'True', 'null': 'True', 'to': "orm['auth.User']"}), + 'created': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}), + 'description': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'last_escalation': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + 'modified': ('django.db.models.fields.DateTimeField', [], {'blank': 'True'}), + 'on_hold': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'priority': ('django.db.models.fields.IntegerField', [], {'default': '3', 'blank': '3'}), + 'queue': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.Queue']"}), + 'resolution': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'status': ('django.db.models.fields.IntegerField', [], {'default': '1'}), + 'submitter_email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'title': ('django.db.models.fields.CharField', [], {'max_length': '200'}) + }, + 'helpdesk.ticketcc': { + 'Meta': {'object_name': 'TicketCC'}, + 'can_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'can_view': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'null': 'True', 'blank': 'True'}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.Ticket']"}), + 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.ticketchange': { + 'Meta': {'object_name': 'TicketChange'}, + 'field': ('django.db.models.fields.CharField', [], {'max_length': '100'}), + 'followup': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.FollowUp']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'new_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'old_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.ticketcustomfieldvalue': { + 'Meta': {'unique_together': "(('ticket', 'field'),)", 'object_name': 'TicketCustomFieldValue'}, + 'field': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.CustomField']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['helpdesk.Ticket']"}), + 'value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}) + }, + 'helpdesk.ticketdependency': { + 'Meta': {'unique_together': "(('ticket', 'depends_on'),)", 'object_name': 'TicketDependency'}, + 'depends_on': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'depends_on'", 'to': "orm['helpdesk.Ticket']"}), + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'ticket': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ticket'", 'to': "orm['helpdesk.Ticket']"}) + }, + 'helpdesk.usersettings': { + 'Meta': {'object_name': 'UserSettings'}, + 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'settings_pickled': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}), + 'user': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['auth.User']", 'unique': 'True'}) + } + } + + complete_apps = ['helpdesk'] diff --git a/helpdesk/models.py b/helpdesk/models.py index 804c770c..4520fd32 100644 --- a/helpdesk/models.py +++ b/helpdesk/models.py @@ -413,6 +413,16 @@ class Ticket(models.Model): ) staff_url = property(_get_staff_url) + def _can_be_resolved(self): + """ + Returns a boolean. + 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 + can_be_resolved = property(_can_be_resolved) + if HAS_TAG_SUPPORT: tags = TagField(blank=True) @@ -1209,3 +1219,28 @@ class TicketCustomFieldValue(models.Model): class Meta: unique_together = ('ticket', 'field'), + + +class TicketDependency(models.Model): + """ + The ticket identified by `ticket` cannot be resolved until the ticket in `depends_on` has been resolved. + To help enforce this, a helper function `can_be_resolved` on each Ticket instance checks that + these have all been resolved. + """ + ticket = models.ForeignKey( + Ticket, + verbose_name=_('Ticket'), + related_name='ticketdependency', + ) + + depends_on = models.ForeignKey( + Ticket, + verbose_name=_('Depends On Ticket'), + related_name='depends_on', + ) + + def __unicode__(self): + return '%s / %s' % (self.ticket, self.depends_on) + + class Meta: + unique_together = ('ticket', 'depends_on') diff --git a/helpdesk/templates/helpdesk/ticket.html b/helpdesk/templates/helpdesk/ticket.html index 393a70c9..48372fb8 100644 --- a/helpdesk/templates/helpdesk/ticket.html +++ b/helpdesk/templates/helpdesk/ticket.html @@ -82,16 +82,17 @@
{% trans "You can insert ticket and queue details in your message. For more information, see the context help page." %}
+ {% if not ticket.can_be_resolved %}
{% trans "This ticket cannot be resolved or closed until the tickets it depends on are resolved." %}
{% endif %} {% ifequal ticket.status 1 %}
» - » - » + » + »
{% endifequal %} {% ifequal ticket.status 2 %}
» - » - » + » + »
{% endifequal %} {% ifequal ticket.status 3 %} diff --git a/helpdesk/templates/helpdesk/ticket_dependency_add.html b/helpdesk/templates/helpdesk/ticket_dependency_add.html new file mode 100644 index 00000000..93df3fb4 --- /dev/null +++ b/helpdesk/templates/helpdesk/ticket_dependency_add.html @@ -0,0 +1,25 @@ +{% extends "helpdesk/base.html" %}{% load i18n %} + +{% block helpdesk_title %}{% trans "Add Ticket Dependency" %}{% endblock %} + +{% block helpdesk_body %}{% blocktrans %} +

Add Ticket Dependency

+ +

Adding a dependency will stop you resolving this ticket until the dependent ticket has been resolved or closed.

{% endblocktrans %} + +
+ +
+
{% for field in form %} +
+
{{ field }}
+ {% if field.errors %}
{{ field.errors }}
{% endif %} + {% if field.help_text %}
{{ field.help_text }}
{% endif %} + {% endfor %}
+
+ + + +{% csrf_token %}
+ +{% endblock %} diff --git a/helpdesk/templates/helpdesk/ticket_dependency_del.html b/helpdesk/templates/helpdesk/ticket_dependency_del.html new file mode 100644 index 00000000..2a2c99f2 --- /dev/null +++ b/helpdesk/templates/helpdesk/ticket_dependency_del.html @@ -0,0 +1,14 @@ +{% extends "helpdesk/base.html" %}{% load i18n %} + +{% block helpdesk_title %}{% trans "Delete Ticket Dependency" %}{% endblock %} + +{% block helpdesk_body %}{% blocktrans %} +

Delete Ticket Dependency

+ +

Are you sure you wish to remove the dependency on this ticket?

+{% endblocktrans %} + +

{% trans "Don't Delete" %}

+ +
{% csrf_token %}
+{% endblock %} diff --git a/helpdesk/templates/helpdesk/ticket_desc_table.html b/helpdesk/templates/helpdesk/ticket_desc_table.html index 902e3cf0..01239590 100644 --- a/helpdesk/templates/helpdesk/ticket_desc_table.html +++ b/helpdesk/templates/helpdesk/ticket_desc_table.html @@ -35,6 +35,19 @@ {% endif %} + + {% trans "Dependencies" %} + {% for dep in ticket.ticketdependency.all %} + {% if forloop.first %}

{% trans "This ticket cannot be resolved until the following ticket(s) are resolved" %}

{% endif %} + {% empty %} +

{% trans "This ticket has no dependencies." %}

+ {% endfor %} +

{% trans "Add Dependency" %}

+ + + {% for customfield in ticket.ticketcustomfieldvalue_set.all %} {{ customfield.field.label }} diff --git a/helpdesk/urls.py b/helpdesk/urls.py index 908d1a02..35433291 100644 --- a/helpdesk/urls.py +++ b/helpdesk/urls.py @@ -70,7 +70,15 @@ urlpatterns = patterns('helpdesk.views.staff', url(r'^tickets/(?P[0-9]+)/cc/delete/(?P[0-9]+)/$', 'ticket_cc_del', name='helpdesk_ticket_cc_del'), + + url(r'^tickets/(?P[0-9]+)/dependency/add/$', + 'ticket_dependency_add', + name='helpdesk_ticket_dependency_add'), + url(r'^tickets/(?P[0-9]+)/dependency/delete/(?P[0-9]+)/$', + 'ticket_dependency_del', + name='helpdesk_ticket_dependency_del'), + url(r'^raw/(?P\w+)/$', 'raw_details', name='helpdesk_raw'), diff --git a/helpdesk/views/staff.py b/helpdesk/views/staff.py index d8c46eec..2fc1a53b 100644 --- a/helpdesk/views/staff.py +++ b/helpdesk/views/staff.py @@ -23,9 +23,9 @@ from django.template import loader, Context, RequestContext from django.utils.translation import ugettext as _ from django.utils.html import escape -from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm, TicketCCForm, EditFollowUpForm +from helpdesk.forms import TicketForm, UserSettingsForm, EmailIgnoreForm, EditTicketForm, TicketCCForm, EditFollowUpForm, TicketDependencyForm from helpdesk.lib import send_templated_mail, line_chart, bar_chart, query_to_dict, apply_query, safe_template_context -from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC +from helpdesk.models import Ticket, Queue, FollowUp, TicketChange, PreSetReply, Attachment, SavedSearch, IgnoreEmail, TicketCC, TicketDependency from helpdesk.settings import HAS_TAG_SUPPORT if HAS_TAG_SUPPORT: @@ -1037,3 +1037,34 @@ def ticket_cc_del(request, ticket_id, cc_id): 'cc': cc, })) ticket_cc_del = staff_member_required(ticket_cc_del) + +def ticket_dependency_add(request, ticket_id): + ticket = get_object_or_404(Ticket, id=ticket_id) + if request.method == 'POST': + form = TicketDependencyForm(request.POST) + if form.is_valid(): + ticketdependency = form.save(commit=False) + ticketdependency.ticket = ticket + if ticketdependency.ticket <> ticketdependency.depends_on: + ticketdependency.save() + return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket.id])) + else: + form = TicketDependencyForm() + return render_to_response('helpdesk/ticket_dependency_add.html', + RequestContext(request, { + 'ticket': ticket, + 'form': form, + })) +ticket_dependency_add = staff_member_required(ticket_dependency_add) + +def ticket_dependency_del(request, ticket_id, dependency_id): + dependency = get_object_or_404(TicketDependency, ticket__id=ticket_id, id=dependency_id) + if request.method == 'POST': + dependency.delete() + return HttpResponseRedirect(reverse('helpdesk_view', args=[ticket_id])) + return render_to_response('helpdesk/ticket_dependency_del.html', + RequestContext(request, { + 'dependency': dependency, + })) +ticket_dependency_del = staff_member_required(ticket_dependency_del) +