···1818 name = models.CharField(max_length=200)
1919 description = models.TextField(blank=True)
2020 members = models.ManyToManyField(PawUser)
2121+ access_non_category_tickets = models.BooleanField(default=False)
2222+ readonly_access = models.BooleanField(default=False)
21232224 def __str__(self):
2325 return self.name
···6769 models.Index(fields=["priority", "title"]),
6870 ]
69717272+ @classmethod
7373+ def _get_tickets(cls, user) -> models.QuerySet:
7474+ """
7575+ For regular users with no team: return all open tickets that are created by the user
7676+ """
7777+ if user.is_superuser:
7878+ return cls.objects.all()
7979+8080+ user_teams = user.team_set.all()
8181+ if not user_teams:
8282+ return cls.objects.filter(user=user)
8383+8484+ q = cls.objects.filter(
8585+ models.Q(user=user) | # tickets created by user
8686+ (models.Q(assigned_team__in=user_teams) | models.Q(assigned_to=user)) | # tickets assigned to user or user's team
8787+ (models.Q(assigned_team=None) & models.Q(category=None)) # tickets that are not assigned and have no category (general), needs to be excluded with filter
8888+ )
8989+9090+ if not any([team.access_non_category_tickets for team in user_teams]):
9191+ return q.exclude(models.Q(assigned_team=None) & models.Q(category=None) & ~models.Q(user=user))
9292+ return q
9393+9494+ @classmethod
9595+ def get_open_tickets(cls, user) -> models.QuerySet:
9696+ """
9797+ For regular users with no team: return all open tickets that are created by the user
9898+ """
9999+ return cls._get_tickets(user).exclude(status=cls.Status.CLOSED)
100100+101101+ @classmethod
102102+ def get_closed_tickets(cls, user) -> models.QuerySet:
103103+ """
104104+ For regular users with no team: return all closed tickets that are created by the user
105105+ """
106106+ return cls._get_tickets(user).filter(status=cls.Status.CLOSED)
107107+108108+ def can_open(self, user):
109109+ if user.is_superuser:
110110+ return True
111111+ return self.user == user or self.assigned_to == user or self.assigned_team in user.team_set.all() or self.assigned_team is None and user.team_set.filter(access_non_category_tickets=True).exists()
112112+113113+ def can_edit(self, user):
114114+ if user.is_superuser:
115115+ return True
116116+ assigned_and_write_access = self.assigned_team in user.team_set.filter(readonly_access=False) or self.assigned_to == user
117117+ unassigned_and_write_access = self.assigned_team is None and user.team_set.filter(access_non_category_tickets=True, readonly_access=False).exists()
118118+ print(assigned_and_write_access, unassigned_and_write_access)
119119+ return self.can_open(user) and (assigned_and_write_access or unassigned_and_write_access)
120120+121121+70122 def close_ticket(self):
71123 self.status = self.Status.CLOSED
72124 self.save()
···8313584136 def get_priority(self):
85137 return self.Priority(self.priority).label
138138+139139+ def get_status(self):
140140+ return self.Status(self.status).label
8614187142 def __str__(self):
88143 return self.title
···125180 'ticket_description': instance.description, 'ticket_category': instance.category.name if instance.category else _('General')})
126181127182@receiver(pre_save, sender=Ticket, dispatch_uid="mail_change_notification")
128128-def send_mail_change_notification(sender, instance, update_fields=None, **kwargs):
183183+def send_mail_change_notification(sender, instance: Ticket, update_fields=None, **kwargs):
129184 if not instance.user.receive_email_notifications:
130185 return None
131186 try:
···138193 if not mail_template:
139194 return None
140195 mail_template.send_mail(instance.user.email, {
141141- 'ticket_id': instance.id, 'ticket_creator_username': instance.user.username, 'ticket_status': instance.status,
142142- 'ticket_status_old': old_instance.status, 'ticket_title': instance.title
196196+ 'ticket_id': instance.id, 'ticket_creator_username': instance.user.username, 'ticket_status': instance.get_status(),
197197+ 'ticket_status_old': old_instance.get_status(), 'ticket_title': instance.title
143198 })
144199145200class Comment(models.Model):
+11-17
ticketing/views.py
···8899@login_required
1010def show_tickets(request):
1111- if request.user.is_staff:
1212- # show only tickets that are not closed and are not assigned or assigned to the current user's team
1313- tickets = Ticket.objects.filter(
1414- ~Q(status=Ticket.Status.CLOSED) & (~Q(assigned_team=None)
1515- | ~Q(assigned_team__in=request.user.team_set.all()))
1616- ).order_by("priority", "-created_at")
1717- else:
1818- tickets = Ticket.objects.filter(
1919- user=request.user).order_by("-created_at")
1111+ tickets = Ticket.get_open_tickets(request.user).order_by("priority", "-updated_at")
2012 return render(request, "ticketing/tickets.html", {"tickets": tickets})
21132214···3527@login_required
3628def show_ticket(request, ticket_id):
3729 ticket = get_object_or_404(Ticket, pk=ticket_id)
3030+ can_edit = ticket.can_edit(request.user)
3831 # comment_templates = Template.objects.filter(category=ticket.category)
39324040- if request.user != ticket.user and not request.user.is_staff:
3333+ if not ticket.can_open(request.user):
4134 return redirect("all_tickets")
42354336 form, template_form, team_assignment_form, category_assignment_form = CommentForm(
4437 ), TemplateForm(), TeamAssignmentForm(), CategoryAssignmentForm()
45384639 if request.method == "POST":
4747- if 'apply_template' in request.POST and request.user.is_staff:
4040+ if 'apply_template' in request.POST and can_edit:
4841 template_form = TemplateForm(request.POST)
4942 if template_form.is_valid():
5043 template = template_form.cleaned_data["template_select"]
5144 form = CommentForm(initial={"text": template.content})
5252- elif 'assign_to_team' in request.POST and request.user.is_staff:
4545+ elif 'assign_to_team' in request.POST and can_edit:
5346 team_assignment_form = TeamAssignmentForm(request.POST)
5447 if team_assignment_form.is_valid():
5548 ticket.assign_to_team(
5649 team_assignment_form.cleaned_data["team_select"])
5757- elif 'assign_to_category' in request.POST and request.user.is_staff:
5050+ elif 'assign_to_category' in request.POST and can_edit:
5851 category_assignment_form = CategoryAssignmentForm(request.POST)
5952 if category_assignment_form.is_valid():
6053 ticket.category = category_assignment_form.cleaned_data["category_select"]
6154 ticket.save()
6262- elif 'assign_self' in request.POST and request.user.is_staff:
5555+ elif 'assign_self' in request.POST and can_edit:
6356 ticket.assigned_to = request.user
6457 ticket.save()
6565- elif 'reopen_ticket' in request.POST and request.user.is_staff:
5858+ elif 'reopen_ticket' in request.POST and can_edit:
6659 ticket.status = Ticket.Status.IN_PROGRESS
6760 ticket.save()
6861 else:
···7770 for file in form.cleaned_data["attachments"]:
7871 ticket.fileattachment_set.create(file=file)
79728080- if 'close' in request.POST and request.user.is_staff:
7373+ if 'close' in request.POST and can_edit:
8174 ticket.close_ticket()
82758376 comments = ticket.comment_set.all()
8477 context = {
8578 "ticket": ticket, "comments": comments, "attachments": [attachment.file for attachment in ticket.fileattachment_set.all()],
8679 "form": form, "template_form": template_form,
8787- "team_assignment_form": team_assignment_form, "category_assignment_form": category_assignment_form
8080+ "team_assignment_form": team_assignment_form, "category_assignment_form": category_assignment_form,
8181+ "can_edit": can_edit
8882 }
8983 return render(request, "ticketing/ticket_detail.html", context)
9084