emile 10 months ago
parent fc986afd16
commit db49a0ca5c

BIN
.DS_Store vendored

Binary file not shown.

BIN
osinaweb/.DS_Store vendored

Binary file not shown.

Binary file not shown.

@ -5,7 +5,5 @@ from . import views
urlpatterns = [
# ADD
path('ticket/', views.customer_add_ticket, name='customeraddticket'),
path('customer/ticketupdate/<int:ticket_id>/', views.customer_add_ticket_update, name='customeraddticketupdate'),
path('ticketupdatereaction/<str:reaction_type>/<int:ticketupdate_id>/', views.customer_add_ticket_update_reaction, name='customeraddticketupdatereaction'),
]

@ -20,19 +20,14 @@ def customer_add_ticket(request, *args, **kwargs):
if request.method == 'POST':
project = None
product = None
departments = [support_department]
regarding = 'General/Account/Billing'
if request.POST.get('project') and request.POST.get('regarding') == 'Project':
project = get_object_or_404(Project, id=request.POST.get('project'))
project_types = project.project_type.all()
departments = [project_type.department for project_type in project_types]
regarding = 'Project/Product'
elif request.POST.get('product') and request.POST.get('regarding') == 'Product':
product = get_object_or_404(Item, id=request.POST.get('product'))
departments = [product.item_type.department]
regarding = 'Project/Product'
ticket = Ticket(
status = 'Open',
customer = request.user.customerprofile,
title = request.POST.get('title'),
description = request.POST.get('description'),
@ -40,10 +35,9 @@ def customer_add_ticket(request, *args, **kwargs):
project = project,
product = product,
opened_by = request.user,
opened_date = datetime.now()
opened_date = datetime.now(),
)
ticket.save()
ticket.departments.set(departments)
ticket_status = TicketStatus(
ticket = ticket,
@ -53,6 +47,13 @@ def customer_add_ticket(request, *args, **kwargs):
)
ticket_status.save()
ticket_department = TicketDepartment(
ticket=ticket,
department=support_department,
date_added=datetime.now()
)
ticket_department.save()
file_paths = request.POST.getlist('filePath')
@ -63,22 +64,8 @@ def customer_add_ticket(request, *args, **kwargs):
)
ticket_attachment.save()
return redirect('ticketroom', ticket_number=ticket.ticket_number)
department_ids = [dept.id for dept in departments]
staff_profiles = StaffProfile.objects.filter(staff_position__department__id__in=department_ids, active=True)
print(staff_profiles)
for staff in staff_profiles:
subject = f"New Ticket Opened: {ticket.title}"
html_message = render_to_string('email_templates/new_ticket.html', {
'ticket': ticket,
'staff_first_name': staff.user.first_name
})
plain_message = strip_tags(html_message)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = staff.user.email
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
return redirect('customerticketdetails', ticket_number=ticket.ticket_number)
context = {
'customer_products': customer_products,
@ -90,95 +77,4 @@ def customer_add_ticket(request, *args, **kwargs):
@customer_login_required
def customer_add_ticket_update(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
if request.method == 'POST':
ticket_update = TicketUpdate(
ticket = ticket,
description = request.POST.get('description'),
added_by = request.user,
date_added = datetime.now(),
)
ticket_update.save()
file_paths = request.POST.getlist('filePath')
for file_path in file_paths:
ticket_attachment = TicketAttachment(
ticket_update=ticket_update,
file_path=file_path
)
ticket_attachment.save()
if ticket.ticket_members.exists():
members_ids = ticket.ticket_members.values_list('id', flat=True)
staff_profiles = StaffProfile.objects.filter(id__in=members_ids)
else:
department_ids = ticket.departments.values_list('id', flat=True)
staff_profiles = StaffProfile.objects.filter(staff_position__department__id__in=department_ids, active=True)
for staff in staff_profiles:
subject = f"New Ticket Update: {ticket.title}"
html_message = render_to_string('email_templates/new_ticket_update.html', {
'ticket_update': ticket_update,
'staff_first_name': staff.user.first_name
})
plain_message = strip_tags(html_message)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = staff.user.email
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
return redirect('customerticketdetails', ticket_number=ticket.ticket_number)
context = {
'ticket': ticket,
}
return render(request, 'add_templates/customer-add-ticket.html', context)
@customer_login_required
def customer_add_ticket_update_reaction(request, reaction_type, ticketupdate_id):
ticket_update = get_object_or_404(TicketUpdate, id=ticketupdate_id)
existing_reaction = TicketUpdateReaction.objects.filter(ticket_update=ticket_update, customer=request.user).first()
if existing_reaction:
# If the existing reaction type is equal to the new reaction, delete it
if existing_reaction.reaction == reaction_type:
existing_reaction.delete()
else:
# If not, delete all previous reactions and add a new one
TicketUpdateReaction.objects.filter(ticket_update=ticket_update, customer=request.user).delete()
reaction = TicketUpdateReaction.objects.create(
ticket_update=ticket_update,
reaction=reaction_type,
customer=request.user
)
else:
# If there's no existing reaction, simply add the new one
reaction = TicketUpdateReaction.objects.create(
ticket_update=ticket_update,
reaction=reaction_type,
customer=request.user
)
return redirect('customerticketdetails', ticket_number=ticket_update.ticket.ticket_number)

Binary file not shown.

Binary file not shown.

@ -656,42 +656,32 @@ def add_reaction(request, status_id, emoji):
def add_ticket(request, customer_id):
customer = get_object_or_404(CustomerProfile, id=customer_id)
customer_orders = Order.objects.filter(customer=customer)
customer_orders_with_last_status = customer_orders.annotate(
max_status_date=Max('orderstatus__date'))
customer_orders_completed = customer_orders_with_last_status.filter(
orderstatus__status='Completed', orderstatus__date=F('max_status_date'))
customer_products = OrderItem.objects.filter(active__in=[
True, None], item__type='Product', order__customer=customer, order__in=customer_orders_completed)
customer_orders_with_last_status = customer_orders.annotate(max_status_date=Max('orderstatus__date'))
customer_orders_completed = customer_orders_with_last_status.filter(orderstatus__status='Completed', orderstatus__date=F('max_status_date'))
customer_products = OrderItem.objects.filter(active__in=[True, None], item__type='Product', order__customer=customer, order__in=customer_orders_completed)
customer_projects = Project.objects.filter(customer=customer)
all_departments = Department.objects.all().order_by('name')
support_department = get_object_or_404(Department, name='Support')
if request.method == 'POST':
project = None
product = None
regarding = 'General/Account/Billing'
departments = [support_department]
if request.POST.get('project'):
project = get_object_or_404(
Project, id=request.POST.get('project'))
project = get_object_or_404(Project, id=request.POST.get('project'))
regarding = 'Project/Product'
project_types = project.project_type.all()
departments = [
project_type.department for project_type in project_types]
elif request.POST.get('product'):
product = get_object_or_404(Item, id=request.POST.get('product'))
departments = [product.item_type.department]
regarding = 'Project/Product'
if request.POST.getlist('departments'):
department_ids = request.POST.getlist('departments')
departments = Department.objects.filter(id__in=department_ids)
if request.POST.get('department'):
department = get_object_or_404(Department, id=request.POST.get('department'))
else:
department = support_department
ticket = Ticket(
status='Open',
customer=customer,
title=request.POST.get('title'),
description=request.POST.get('description'),
@ -711,7 +701,14 @@ def add_ticket(request, customer_id):
)
ticket_status.save()
ticket.departments.set(departments)
ticketdepartment = TicketDepartment(
ticket=ticket,
department=department,
date_added = datetime.now()
)
ticketdepartment.save()
file_paths = request.POST.getlist('filePath')

File diff suppressed because one or more lines are too long

@ -8,3 +8,5 @@ admin.site.register(TicketRead)
admin.site.register(TicketUpdate)
admin.site.register(TicketAttachment)
admin.site.register(TicketUpdateReaction)
admin.site.register(TicketDepartment)
admin.site.register(TicketStaff)

@ -12,6 +12,7 @@ from django.template.loader import render_to_string
from asgiref.sync import async_to_sync
class TicketRoomConsumer(WebsocketConsumer):
def connect(self):
self.user = self.scope['user']

@ -0,0 +1,41 @@
# Generated by Django 4.2.5 on 2024-06-29 17:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('osinacore', '0085_rename_date_staffposition_start_date_and_more'),
('support', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='ticket',
name='departments',
),
migrations.RemoveField(
model_name='ticket',
name='ticket_members',
),
migrations.CreateModel(
name='TicketStaff',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_added', models.DateTimeField()),
('staff', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.staffposition')),
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='support.ticket')),
],
),
migrations.CreateModel(
name='TicketDepartment',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_added', models.DateTimeField()),
('department', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='osinacore.department')),
('ticket', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='support.ticket')),
],
),
]

@ -0,0 +1,17 @@
# Generated by Django 4.2.5 on 2024-06-29 17:47
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('support', '0002_remove_ticket_departments_and_more'),
]
operations = [
migrations.RemoveField(
model_name='ticket',
name='status',
),
]

@ -1,9 +1,83 @@
from django.db import models
# Create your models here.
from django.db import models
from osinacore.models import *
from billing.models import *
from django.utils import timezone
from django.shortcuts import get_object_or_404
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from threading import Timer
def send_ticket(ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
department = TicketDepartment.objects.filter(ticket=ticket).last().department
staff_profiles = department.get_staff()
for staff in staff_profiles:
subject = f"New Ticket Opened: {ticket.title}"
html_message = render_to_string('email_templates/new-ticket.html', {
'ticket': ticket,
'user': staff.user
})
plain_message = strip_tags(html_message)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = staff.user.email
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
customer = ticket.customer
subject = f"New Ticket Opened: {ticket.title}"
html_message = render_to_string('email_templates/new-ticket.html', {
'ticket': ticket,
'user': customer.user
})
plain_message = strip_tags(html_message)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = staff.user.email
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
def send_update(update_id):
update = get_object_or_404(TicketUpdate, id=update_id)
if TicketDepartment.objects.filter(ticket=update.ticket):
department = TicketDepartment.objects.filter(ticket=update.ticket).last().department
else:
department = Department.objects.get(name='Support')
if department:
staff_profiles = department.get_staff()
print(staff_profiles)
for staff in staff_profiles:
subject = f"New Ticket Update: {update.ticket.title}"
html_message = render_to_string('email_templates/new-ticket-update.html', {
'update': update,
'user': staff.user
})
plain_message = strip_tags(html_message)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = staff.user.email
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
customer = update.ticket.customer
if customer:
subject = f"New Ticket Update: {update.ticket.title}"
html_message = render_to_string('email_templates/new-ticket-update.html', {
'update': update,
'usee': customer.user
})
plain_message = strip_tags(html_message)
from_email = settings.DEFAULT_FROM_EMAIL
to_email = customer.user.email
send_mail(subject, plain_message, from_email, [to_email], html_message=html_message)
# Create your models here.
class Ticket(models.Model):
@ -11,23 +85,15 @@ class Ticket(models.Model):
('General/Account/Billing', 'General/Account/Billing'),
('Project/Product', 'Project/Product'),
)
STATUS_CHOICES = (
('Open', 'Open'),
('Working On', 'Working On'),
('Closed', 'Closed'),
)
ticket_number = models.CharField(max_length=400, blank=True)
title = models.CharField(max_length=400)
description = models.TextField(null=True, blank=True)
status = models.CharField(max_length=50, choices=STATUS_CHOICES, null=True)
regarding = models.CharField(max_length=50, choices=REGARDING_CHOICES, null=True)
project = models.ForeignKey(Project, on_delete=models.SET_NULL, blank=True, null=True)
product = models.ForeignKey(Item, on_delete=models.SET_NULL, blank=True, null=True)
departments = models.ManyToManyField(Department, null=True, blank=True)
opened_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
opened_date = models.DateTimeField()
opened_date = models.DateField(null=True, blank=True)
customer = models.ForeignKey(CustomerProfile, on_delete=models.SET_NULL, null=True)
ticket_members = models.ManyToManyField(StaffProfile, null=True, blank=True, related_name='ticket_members')
def save(self, *args, **kwargs):
if not self.ticket_number:
last_ticket = Ticket.objects.filter(opened_date__year=timezone.now().year).order_by('-ticket_number').first()
@ -44,6 +110,27 @@ class Ticket(models.Model):
super().save(*args, **kwargs)
@receiver(post_save, sender=Ticket)
def send_signal_on_save(sender, instance, created, **kwargs):
if created:
def send_after_delay():
send_ticket(instance.id)
timer = Timer(10, send_after_delay)
timer.start()
class TicketDepartment(models.Model):
department = models.ForeignKey(Department, on_delete=models.CASCADE)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
date_added = models.DateTimeField()
class TicketStaff(models.Model):
staff = models.ForeignKey(StaffPosition, on_delete=models.CASCADE)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
date_added = models.DateTimeField()
class TicketStatus(models.Model):
STATUS_CHOICES = (
('Open', 'Open'),
@ -62,6 +149,15 @@ class TicketUpdate(models.Model):
added_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
date_added = models.DateTimeField()
@receiver(post_save, sender=TicketUpdate)
def send_signal_on_save(sender, instance, created, **kwargs):
if created:
def send_after_delay():
send_update(instance.id)
timer = Timer(10, send_after_delay)
timer.start()
class TicketRead(models.Model):
ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE)
@ -85,7 +181,6 @@ class TicketUpdateReaction(models.Model):
ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE)
class TicketTask(models.Model):
ticket = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE)
task = models.ForeignKey(Task, on_delete=models.CASCADE)

Binary file not shown.

@ -25,7 +25,7 @@
</div>
</div>
<div id="submitted-reactions" class="w-full border-t border-gray-200 pt-5 flex justify-start items-center gap-3 p-5 {% if user.customerprofile %} hidden {% endif %}">
<div id="submitted-reactions" class="{% if update.ticketupdatereaction_set.all %} w-full border-t border-gray-200 pt-5 flex justify-start items-center gap-3 p-5 {% endif %} {% if user.customerprofile %} hidden {% endif %}">
<div class="flex justify-start items-center gap-2">
<button data-reaction="Happy"
class="submittedreaction-button w-fit h-fit rounded-full {% if not update.ticketupdatereaction_set.all.last.reaction == 'Happy' %} hidden {% endif %} ">

@ -184,9 +184,12 @@
button.classList.add('border-2', 'border-secondosiblue');
}
});
const submittedReactions = updateElement.querySelector('#submitted-reactions');
submittedReactions.classList.remove('w-full', 'border-t', 'border-gray-200', 'pt-5', 'flex', 'justify-start', 'items-center', 'gap-3', 'p-5');
updateElement.querySelectorAll('.submittedreaction-button').forEach(button => {
button.classList.add('hidden');
if (button.dataset.reaction === data.reaction) {
submittedReactions.classList.add('w-full', 'border-t', 'border-gray-200', 'pt-5', 'flex', 'justify-start', 'items-center', 'gap-3', 'p-5');
button.classList.remove('hidden');
}
});
@ -229,18 +232,12 @@
const formData = new FormData(form);
const description = formData.get('description');
const files = formData.getAll('filePath');
const filePaths = [];
for (const file of files) {
const filePath = `/path/to/uploaded/files/${file.name}`;
filePaths.push(filePath);
}
const filePath = formData.getAll('filePath');
const data = {
event_type: 'update',
description: description,
filePath: filePaths
filePath: filePath
};
socket.send(JSON.stringify(data));

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Account Activation</title>
<title>New Ticket Update</title>
<style>
body {
font-family: Arial, sans-serif;
@ -43,22 +43,26 @@
</head>
<body>
<div class="container">
<h2>New Ticket Update for: {{ticket_update.ticket.title}}</h2>
<p>Hello {{staff_first_name}},</p>
<p>A new ticket update has been added by {{ticket_update.ticket.customer.user.first_name}} {{ticket_update.ticket.customer.user.last_name}} with the following details:</p>
<p><strong>Ticket:</strong> {{ ticket_update.ticket.title }}</p>
<h2>New Ticket Update for: {{update.ticket.title}}</h2>
<p>Hello {{user.first_name}},</p>
{% if update.added_by.id == user.id %}
<p>You added a new ticket update with the following details:</p>
{% else %}
<p>A new ticket update has been added by {{update.added_by.first_name}} {{update.added_by.last_name}} with the following details:</p>
{% endif %}
<p><strong>Ticket:</strong> {{ update.ticket.title }}</p>
<p><strong>Regarding:</strong>
{% if not ticket_update.ticket.project and not ticket_update.ticket.product %}
{{ ticket_update.ticket.regarding }}
{% elif ticket_update.ticket.product %}
{{ ticket_update.ticket.product }}
{% elif ticket_update.ticket.project %}
{{ ticket_update.ticket.project }}
{% if not update.ticket.project and not update.ticket.product %}
{{ update.ticket.regarding }}
{% elif update.ticket.product %}
{{ update.ticket.product }}
{% elif update.ticket.project %}
{{ update.ticket.project }}
{% endif %}</p>
<p><strong>Description:</strong> {{ ticket_update.description|safe }}</p>
<p><strong>Added By:</strong> {{ticket_update.ticket.customer.user.first_name}} {{ticket_update.ticket.customer.user.last_name}}</p>
<p><strong>Date:</strong> {{ ticket_update.date_added }}</p>
<p><a class="btn" href="https://osina.ositcom.com/tickets/{{ticket_update.ticket.ticket_number}}/">View Update</a></p>
<p><strong>Description:</strong> {{ update.description|safe }}</p>
<p><strong>Added By:</strong> {{update.added_by}} {{update.added_by}}</p>
<p><strong>Date:</strong> {{ update.date_added }}</p>
<p><a class="btn" href="https://osina.ositcom.com/tickets/{{update.ticket.ticket_number}}/">View Update</a></p>
<p>Regards,<br>The Ositcom Team</p>
</div>
</body>

@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Account Activation</title>
<title>New Ticket</title>
<style>
body {
font-family: Arial, sans-serif;
@ -43,9 +43,18 @@
</head>
<body>
<div class="container">
<h2>New Ticket Opened by {{ticket.customer.user.first_name}} {{ticket.customer.user.last_name}}</h2>
<p>Hello {{staff_first_name}},</p>
<p>A new ticket has been opened by {{ticket.customer.user.first_name}} {{ticket.customer.user.last_name}} with the following details:</p>
{% if ticket.opened_by.id == user.id %}
<h2>New Ticket Opened at Ositcom</h2>
{% else %}
<h2>New Ticket Opened by {{ticket.opened_by.user.first_name}} {{ticket.opened_by.user.last_name}}</h2>
{% endif %}
<p>Hello {{user.first_name}},</p>
{% if ticket.opened_by.id == user.id %}
<p>You opened a new ticket at Ositcom with the following details:</p>
{% else %}
<p>A new ticket was opened by {{ticket.opened_by.first_name}} {{ticket.opened_by.last_name}} with the following details:</p>
{% endif %}
<p><strong>Title:</strong> {{ ticket.title }}</p>
<p><strong>Description:</strong> {{ ticket.description }}</p>
<p><strong>Department:</strong> {{ ticket.department.name }}</p>

@ -1,6 +1,10 @@
from django.shortcuts import render, get_object_or_404
from .models import *
from .decorators import *
from django.core.mail import send_mail
from django.template.loader import render_to_string
from django.utils.html import strip_tags
from django.conf import settings
# Create your views here.
@ticket_member_required
@ -26,3 +30,6 @@ def ticket_room(request, ticket_number):
}
return render(request, 'details_templates/ticket-room.html', context)

Loading…
Cancel
Save