emile 1 year ago
parent 406d89696e
commit 43e8203fc1

@ -8,4 +8,5 @@ urlpatterns = [
path('order/<int:customer_id>/', views.add_order, name='addorder'),
path('invoice/<int:order_id>/', views.add_invoice_pdf, name='addinvoice'),
path('service/<int:service_id>/<int:order_id>/', views.add_service_in_order, name='addserviceinorder'),
]

@ -5,11 +5,12 @@ from django.http import JsonResponse, HttpResponse
from django.template.loader import get_template
from django.conf import settings
import os
from osinacore.decorators import *
from django.core.files.base import ContentFile
from weasyprint import HTML, CSS
@staff_login_required
def add_product(request, *args, **kwargs):
item_types = ProjectType.objects.all().order_by('name')
if request.method == 'POST':
@ -38,7 +39,7 @@ def add_product(request, *args, **kwargs):
return render(request, 'add_templates/add-product.html', context)
@staff_login_required
def add_service (request, *args, **kwargs):
item_types = ProjectType.objects.all().order_by('name')
customers = CustomerProfile.objects.all().order_by('user__first_name')
@ -72,38 +73,26 @@ def add_service (request, *args, **kwargs):
return render(request, 'add_templates/add-service.html', context)
def add_order (request, customer_id):
@staff_login_required
def add_order(request, customer_id):
customer= get_object_or_404(CustomerProfile, id=customer_id)
businesses = Business.objects.filter(customer=customer)
if request.method == 'POST':
customer_id = request.POST.get('customer')
customer = get_object_or_404(CustomerProfile, id=customer_id)
status = request.POST.get('status')
customer_id = request.POST.get('customer')
customer = get_object_or_404(CustomerProfile, id=customer_id)
business_id = request.POST.get('business')
if business_id:
business = get_object_or_404(Business, id=business_id)
else:
business = None
selected_items = request.POST.getlist('items')
order = Order.objects.create(
customer=customer,
status=status,
business=business,
)
for item_id in selected_items:
item = Item.objects.get(id=item_id)
OrderItem.objects.create(order=order, item=item, purchased_at=datetime.now())
return redirect('orders')
return redirect('orderdetails', order_id=order.id)
context = {
'customer' : customer,
@ -114,12 +103,27 @@ def add_order (request, customer_id):
@staff_login_required
def add_service_in_order(request, service_id, order_id):
service =get_object_or_404(Item, id=service_id)
order= get_object_or_404(Order, id=order_id)
order_item = OrderItem.objects.create(
order = order,
item = service,
purchased_at = datetime.now()
)
order_item.save()
return redirect('orderdetails', order_id=order.id)
def add_invoice_pdf(request, order_id):
def add_invoice_pdf(request, order_id):
order = get_object_or_404(Order, id=order_id)
current_year = str(timezone.now().year)[-2:]
last_invoice = Invoice.objects.filter(invoice_number__startswith=current_year).order_by('-invoice_number').first()
if last_invoice:
@ -163,21 +167,15 @@ def add_invoice_pdf(request, order_id):
presentational_hints=True
)
filename = f'invoice_{invoice.invoice_number}.pdf'
pdf_content = ContentFile(pdf)
invoice.pdf.save(filename, pdf_content, save=True)
# Save PDF to a file
pdf_file_path = os.path.join(settings.MEDIA_ROOT, f'invoice_{invoice.id}.pdf')
with open(pdf_file_path, 'wb') as pdf_file:
pdf_file.write(pdf)
# Associate PDF file path with the Invoice object
invoice.pdf = pdf_file_path
invoice.save()
# Return PDF
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="my_pdf.pdf"'
return response
return response

@ -0,0 +1,7 @@
from django.urls import path
from billing.delete import views
urlpatterns = [
path('orderitem<int:orderitem_id>/', views.delete_orderitem, name='deleteorderitem'),
]

@ -0,0 +1,11 @@
from django.shortcuts import render, get_object_or_404, redirect
from osinacore.models import *
from billing.models import *
from osinacore.decorators import *
@staff_login_required
def delete_orderitem(request, orderitem_id):
order_item = get_object_or_404(OrderItem, id=orderitem_id)
order_id = order_item.order.id
order_item.delete()
return redirect('orderdetails', order_id=order_id)

@ -9,78 +9,71 @@
<div class="w-full h-fit bg-white rounded-md shadow-md p-5">
<div class="w-full bg-gray-50 px-3 py-3 border border-gray-100 shadow-md rounded-md flex flex-col s:flex-row justify-between items-start s:items-center gap-3">
<div>
<h1 class="text-secondosiblue text-xl">Order 234443</h1>
<p class="text-gray-500">Salim Elliye</p>
<h1 class="text-secondosiblue text-xl">Order {{order.order_id}}</h1>
<p class="text-gray-500">{{order.customer.user.first_name}} {{order.customer.user.last_name}}</p>
</div>
<p class="uppercase text-green-700 font-semibold">Completed</p>
<p class="uppercase text-green-700 font-semibold {% if order.status == 'Completed' %} text-green-700 {% elif order.status == 'Pending' %} text-yellow-500 {% elif order.status == 'Failed' %} text-red-500 {% endif %}">{{order.status}}</p>
</div>
<div class="w=full mt-5">
<p class="text-secondosiblue text-xl">Services</p>
{% if order_items %}
<div class="w-full mt-10">
<p class="text-secondosiblue text-xl">Order Items</p>
<div class="w-full grid grid-cols-1 md:grid-cols-2 l:grid-cols-3 gap-5 mt-3">
<div class="w-full shadow-md border border-gray-200 rounded-t-md ">
<div
class="w-ful py-9 px-5 flex justify-center items-center text-center text-secondosiblue bg-white rounded-t-md">
<p>Service Name</p>
{% for item in order_items %}
<div class="w-full shadow-md border border-gray-200 flex flex-col justify-between rounded-t-md">
<div class="w-full h-full p-9 flex flex-col justify-center items-center gap-5 rounded-t-md bg-white">
<p class="text-secondosiblue uppercase font-poppinsBold text-xl text-center">{{item.item.title}}
</p>
</div>
<div
class="w-ful px-3 py-2 text-white flex justify-center items-center gap-2 bg-osiblue rounded-b-md cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
<p>Add</p>
<div class="w-full flex flex-col justify-center items-center text-center mb-2">
<p class="text-secondosiblue font-poppinsLight text-[17px] font-semibold">${{item.item.amount}}</p>
</div>
<a href="{% url 'deleteorderitem' item.id %}"><div
class="w-full px-3 py-2 text-white flex justify-center items-center gap-2 bg-red-500 rounded-b-md cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-white">>
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12H9m12 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
</svg>
<p>Remove</p>
</div></a>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<div class="w=full mt-10">
<p class="text-secondosiblue text-xl">Products</p>
<div class="w-full grid grid-cols-1 md:grid-cols-2 l:grid-cols-3 gap-5 mt-5">
<div>
<div class="w-full p-9 border border-gray-200 flex flex-col items-center gap-5 rounded-t-md bg-white">
<p class="text-secondosiblue uppercase font-poppinsBold text-xl text-center">Basic
</p>
<div class="w-full flex flex-col justify-center items-center text-center">
<p class="text-osiblue font-poppinsLight text-[17px] font-semibold">$0</p>
<p class="text-gray-500">monthly/restaurant</p>
</div>
</div>
<div
class="w-ful px-3 py-2 text-white flex justify-center items-center gap-2 bg-osiblue border border-osiblue rounded-b-md cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
<p>Add</p>
</div>
</div>
{% if services %}
<div class="w-full mt-10">
<p class="text-secondosiblue text-xl">Add Services</p>
<div class="w-full grid grid-cols-1 md:grid-cols-2 l:grid-cols-3 gap-5 mt-3">
<div>
<div class="w-full p-9 border border-gray-200 flex flex-col items-center gap-5 rounded-t-md bg-white">
<p class="text-secondosiblue uppercase font-poppinsBold text-xl text-center">Standard
{% for service in services %}
<div class="w-full shadow-md border border-gray-200 flex flex-col justify-between rounded-t-md">
<div class="w-full h-full p-9 flex flex-col justify-center items-center gap-5 rounded-t-md bg-white">
<p class="text-secondosiblue uppercase font-poppinsBold text-xl text-center">{{service.title}}
</p>
<div class="w-full flex flex-col justify-center items-center text-center">
<p class="text-osiblue font-poppinsLight text-[17px] font-semibold">$20</p>
<p class="text-gray-500">monthly/restaurant</p>
</div>
</div>
<div
class="w-ful px-3 py-2 text-white flex justify-center items-center gap-2 bg-osiblue border border-osiblue rounded-b-md cursor-pointer">
<div class="w-full flex flex-col justify-center items-center text-center mb-2">
<p class="text-secondosiblue font-poppinsLight text-[17px] font-semibold">${{service.amount}}</p>
</div>
<a href="{% url 'addserviceinorder' service.id order.id %}"><div
class="w-full px-3 py-2 text-white flex justify-center items-center gap-2 bg-osiblue rounded-b-md cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M12 4.5v15m7.5-7.5h-15" />
</svg>
<p>Add</p>
</div>
</div></a>
</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</div>

@ -5,6 +5,7 @@ from billing import views
urlpatterns = [
path('add/', include('billing.add.urls')),
path('delete/', include('billing.delete.urls')),
# LISTING
path('items/', views.items, name='items'),
@ -12,6 +13,6 @@ urlpatterns = [
path('invoices/', views.invoices, name='invoices'),
# DETAILS
path('invoice-details/<int:order_id>/', views.invoice_details, name='invoicedetails'),
path('order-details/<int:order_id>/', views.order_details, name='orderdetails'),
path('invoices/<int:order_id>/', views.invoice_details, name='invoicedetails'),
path('orders/<int:order_id>/', views.order_details, name='orderdetails'),
]

@ -1,9 +1,7 @@
from django.shortcuts import render, get_object_or_404
from django.utils import timezone
from datetime import timedelta
from .models import *
from django.http import JsonResponse, HttpResponse
from django.template.loader import get_template
from django.db.models import Q
# LISTING
@ -49,13 +47,18 @@ def invoice_details(request, order_id):
def order_details(request, order_id):
order = get_object_or_404(Order, id=order_id)
order_items = OrderItem.objects.filter(order=order).order_by('-id')
order_item_ids = order_items.values_list('item_id', flat=True)
services = Item.objects.filter(Q(type='Service') & (Q(customer=order.customer) | Q(customer__isnull=True))).exclude(id__in=order_item_ids).order_by('-id')
products = Item.objects.filter(Q(type='Product') & (Q(customer=order.customer) | Q(customer__isnull=True))).exclude(id__in=order_item_ids).order_by('-id')
context = {
'order' : order,
'order_items' : order_items,
'services': services,
}
return render(request, 'details_templates/order-details.html', context)

@ -20,4 +20,13 @@ def utilities(request):
unread_updates_count += 1
ticket.unread_updates_count = unread_updates_count
last_update = ticket.ticketupdate_set.order_by('-date_added').first()
if last_update:
ticket.last_update_added_by = last_update.added_by.first_name
ticket.last_update_date_added = last_update.date_added
else:
ticket.last_update_added_by = None
ticket.last_update_date_added = None
return {'active_subscriptions': active_subscriptions, 'customer_open_tickets': customer_open_tickets}

@ -0,0 +1,19 @@
# Generated by Django 4.2.5 on 2024-04-26 08:49
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('customercore', '0008_rename_ticketupdatereadstatus_ticketread'),
]
operations = [
migrations.AlterField(
model_name='ticketattachment',
name='ticket',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='customercore.ticket'),
),
]

@ -65,7 +65,7 @@ class TicketRead(models.Model):
read = models.BooleanField(default=False)
class TicketAttachment(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE, null=True, blank=True)
ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE, null=True, blank=True)
file = models.FileField()

@ -6,33 +6,47 @@
<div class="w-full px-5 s:px-9 flex flex-col gap-3">
<div class="w-full bg-white rounded-md h-fit shadow-md p-5">
<div class="w-full h-fit flex flex-col gap-2 bg-gray-100 shadow-md rounded-md px-3 py-3">
<p class="text-secondosiblue text-xl">Ticket <span class="font-semibold">{{ticket.ticket_number}}</span></p>
<p class="text-secondosiblue text-[20px]">Ticket <span class="font-semibold">#{{ticket.ticket_number}}</span></p>
{% if last_ticket_status.status == 'Open' %}
<div class="flex justify-start items-center gap-1">
<div class="w-[16px] h-[16px] rounded-full bg-green-200 shadow-md"></div>
<p class="text-secondosiblue font-light">Opened by {{last_ticket_status.added_by.first_name}} at {{last_ticket_status.date_added}}</p>
</div>
{% elif last_ticket_status.status == 'Working On' %}
<div class="flex justify-start items-center gap-1">
<div class="w-[16px] h-[16px] rounded-full bg-yellow-200 shadow-md"></div>
<p class="text-secondosiblue font-light">Updated to 'Working On' by {{last_ticket_status.added_by.first_name}} at {{last_ticket_status.date_added}}</p>
</div>
{% elif last_ticket_status.status == 'Closed' %}
<div class="flex justify-start items-center gap-1">
<div class="w-[16px] h-[16px] rounded-full bg-red-200 shadow-md"></div>
<p class="text-secondosiblue font-light">Closed by Linode at 20-4-24 16:30</p>
<p class="text-secondosiblue font-light">Closed by {{last_ticket_status.added_by.first_name}} at {{last_ticket_status.date_added}}</p>
</div>
{% endif %}
<div class="w-full mt-3">
<p class="text-gray-500 font-light text-sm leading-7">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus sagittis, risus id sollicitudin
maximus, diam velit tempus est, pulvinar cursus arcu nisi nec ligula. Nunc eleifend, est eget
lacinia semper, ligula purus scelerisque nunc, a molestie elit magna quis sem. Curabitur eget auctor
mi, vel sodales purus. Nullam odio erat, convallis sit amet feugiat molestie, fermentum sit amet
purus. Proin eget nunc eget risus tristique tempus. Donec egestas bibendum ligula sit amet porta.
<p class="text-gray-500 font-light text-sm leading-7">{{ticket.description}}
</p>
</div>
{% if ticket.ticketattachment_set.all %}
<div class="w-full flex flex-wrap justify-end items-center gap-3">
<a>
<div class="flex items-center gap-1 text-secondosiblue hover:text-gray-500 duration-300 cursor-pointer">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 text-secondosiblue">
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
</svg>
<p>Filename.docx</p>
{% for file in ticket.ticketattachment_set.all %}
<a href="{{file.file.url}}">{{ file.file }}{% if not forloop.last %}, {% endif %}</a>
{% endfor %}
</div>
</a>
</div>
{% endif %}
</div>
@ -92,16 +106,21 @@
{{update.description}}
</p>
{% if update.ticketattachment_set.all %}
<div class="w-full flex flex-wrap justify-end items-center gap-3">
<a>
<div class="flex items-center gap-1 text-secondosiblue hover:text-gray-500 duration-300 cursor-pointer text-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-4 h-4 text-secondosiblue">
<path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
</svg>
<p>Filename.docx</p>
</svg>
{% for file in update.ticketattachment_set.all %}
<a href="{{file.file.url}}">{{ file.file }}{% if not forloop.last %}, {% endif %}</a>
{% endfor %}
</div>
</a>
</div>
{% endif %}
</div>
@ -175,7 +194,7 @@
<div class="w-full flex flex-col s:flex-row justify-end items-center gap-3">
<div
class="w-full s:w-[50px] h-[50px] rounded-md bg-gray-50 shadow-md border border-gray-100 flex justify-center items-center p-2 cursor-pointer relative hover:scale-105 duration-300 transition-transform">
<input type="file" name="file" id="fileInput" class="opacity-0">
<input type="file" name="files" id="fileInput" class="opacity-0">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor"

@ -5,7 +5,7 @@
<div class="w-full px-5 s:px-9 flex flex-col gap-3">
<div class="w-full bg-white rounded-md h-fit shadow-md p-5">
<h1 class="text-secondosiblue text-[30px]">My Tickets</h1>
<h1 class="text-secondosiblue text-[25px]">My Tickets</h1>
<div class="w-full flex flex-col gap-3 mt-4">
@ -85,17 +85,17 @@
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">{{ ticket.last_updated }}</p>
<p class="text-secondosiblue">{{ticket.last_update_date_added}}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">{{ ticket.updated_by }}</p>
<p class="text-secondosiblue">{{ ticket.last_update_added_by }}</p>
</td>
<td class="px-6 py-4 text-center text-sm">
<div class="w-full flex justify-center items-center">
<div class="w-[40px] h-[40px] rounded-full border-2 border-secondosiblue flex justify-center items-center">
<p class="text-secondosiblue">2</p>
<p class="text-secondosiblue">{{ticket.unread_updates_count}}</p>
</div>
</div>
</td>
@ -164,11 +164,11 @@
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">{{ ticket.last_updated }}</p>
<p class="text-secondosiblue">{{ ticket.last_update_date_added }}</p>
</td>
<td class="px-6 py-4 text-center text-sm">
<p class="text-secondosiblue">{{ ticket.updated_by }}</p>
<p class="text-secondosiblue">{{ ticket.last_update_added_by }}</p>
</td>
</tr>
{% endfor %}

@ -13,7 +13,7 @@
<!-- PRICING ON DESKTOP -->
<div class="w-full hidden l:grid grid-cols-3 mt-24">
<div class="w-full h-full border border-gray-200 border-r-transparent p-9 flex flex-col gap-5 items-center">
<div class="w-full h-full border border-gray-200 border-r-transparent p-9 flex flex-col gap-5 items-center {% if active_order_item_basic %} bg-black bg-opacity-20 {% endif %}">
<p class="text-secondosiblue uppercase font-poppinsBold text-2xl text-center">Basic</p>
<div class="w-full flex flex-col justify-center items-center">
@ -21,12 +21,18 @@
<p class="text-gray-500">monthly/restaurant</p>
</div>
{% if not active_order_item_basic %}
<a href="{% url 'buyfreeosimenu' %}">
<button
class="w-fit px-9 py-2 bg-secondosiblue border border-secondosiblue text-white uppercase hover:bg-white hover:text-secondosiblue duration-300">Get
Started
</button>
</a>
{% else %}
<button
class="w-fit px-9 py-2 bg-white border border-green-700 text-green-700 uppercase cursor-default">Currently Subscribed</button>
{% endif %}
<div class="w-full flex flex-col gap-3 mt-5">
<h1 class="text-gray-500 text-[17px] font-light">Features:</h1>
@ -74,7 +80,7 @@
<p>Best Value</p>
</div>
<div class="border h-full border-osiblue w-full p-9 flex flex-col gap-5 items-center">
<div class="border h-full border-osiblue w-full p-9 flex flex-col gap-5 items-center {% if active_order_item_standard %} bg-black bg-opacity-20 {% endif %}">
<p class="text-secondosiblue uppercase font-poppinsBold text-2xl text-center">Standard</p>
<div class="w-full flex flex-col justify-center items-center">
@ -82,12 +88,17 @@
<p class="text-gray-500">monthly/restaurant</p>
</div>
{% if not active_order_item_standard %}
<a href="{% url 'payment' osimenu_standard.id %}">
<button
class="w-fit px-9 py-2 bg-secondosiblue border border-secondosiblue text-white uppercase hover:bg-white hover:text-secondosiblue duration-300">Get
Started
</button>
</a>
{% else %}
<button
class="w-fit px-9 py-2 bg-white border border-green-700 text-green-700 uppercase cursor-default">Currently Subscribed</button>
{% endif %}
<div class="w-full flex flex-col gap-3 mt-5">
<h1 class="text-gray-500 text-[17px] font-light">Features:</h1>
@ -131,7 +142,7 @@
</div>
<div class="w-full h-full border border-gray-200 p-9 flex flex-col gap-5 items-center">
<div class="w-full h-full border border-gray-200 p-9 flex flex-col gap-5 items-center {% if active_order_item_premium %} bg-black bg-opacity-20 {% endif %}">
<p class="text-secondosiblue uppercase font-poppinsBold text-2xl text-center">Premium</p>
<div class="w-full flex flex-col justify-center items-center">
@ -139,12 +150,17 @@
<p class="text-gray-500">monthly/restaurant</p>
</div>
{% if not active_order_item_premium %}
<a href="{% url 'payment' osimenu_premium.id %}">
<button
class="w-fit px-9 py-2 bg-secondosiblue border border-secondosiblue text-white uppercase hover:bg-white hover:text-secondosiblue duration-300">Get
Started
</button>
</a>
{% else %}
<button
class="w-fit px-9 py-2 bg-white border border-green-700 text-green-700 uppercase cursor-default">Currently Subscribed</button>
{% endif %}
<div class="w-full flex flex-col gap-3 mt-5">
<h1 class="text-gray-500 text-[17px] font-light">Features:</h1>

@ -11,6 +11,8 @@ import string
from billing.add.views import *
from .models import *
from django.db.models import Q
from datetime import date
# Create your views here.
@customer_login_required
@ -152,11 +154,43 @@ def customer_orders(request, *args, **kwargs):
return render(request, 'listing_pages/customer-orders.html', context)
@customer_login_required
def customer_tickets(request, *args, **kwargs):
open_tickets = Ticket.objects.filter(Q(status__in=['Open', 'Working On']) & Q(customer=request.user.customerprofile)).order_by('-id')
open_tickets = Ticket.objects.filter(Q(status__in=['Open', 'Working On']) & Q(customer=request.user.customerprofile))
closed_tickets = Ticket.objects.filter(Q(status__in=['Closed']) & Q(customer=request.user.customerprofile)).order_by('-id')
for ticket in open_tickets:
unread_updates_count = 0
last_update = ticket.ticketupdate_set.exclude(added_by=request.user).order_by('-date_added').first()
if last_update:
last_update_added_by = last_update.added_by
last_update_date_added = last_update.date_added
else:
last_update_added_by = None
last_update_date_added = None
for ticket_update in ticket.ticketupdate_set.exclude(added_by=request.user):
if not TicketRead.objects.filter(ticket_update=ticket_update, user=request.user, read=True).exists():
unread_updates_count += 1
ticket.unread_updates_count = unread_updates_count
ticket.last_update_added_by = last_update_added_by
ticket.last_update_date_added = last_update_date_added
for ticket in closed_tickets:
last_update = ticket.ticketupdate_set.order_by('-date_added').first()
if last_update:
last_update_added_by = last_update.added_by
last_update_date_added = last_update.date_added
else:
last_update_added_by = None
last_update_date_added = None
ticket.last_update_added_by = last_update_added_by
ticket.last_update_date_added = last_update_date_added
context = {
'open_tickets': open_tickets,
'closed_tickets': closed_tickets,
@ -174,9 +208,12 @@ def customer_ticket_details(request, ticket_number):
for update in TicketUpdate.objects.filter(ticket=ticket).exclude(added_by=request.user).order_by('id'):
if not TicketRead.objects.filter(ticket_update=update, user=request.user).exists():
TicketRead.objects.create(ticket_update=update, user=request.user, read=True)
last_ticket_status = TicketStatusUpdate.objects.all().last()
context = {
'ticket': ticket,
'ticket_updates': ticket_updates,
'last_ticket_status': last_ticket_status,
}
return render(request, 'details_templates/inner-customer-ticket.html', context)
@ -191,11 +228,19 @@ def osimenu_plans(request, *args, **kwargs):
osimenu_basic = Item.objects.filter(title='OSIMENU BASIC').first()
osimenu_standard = Item.objects.filter(title='OSIMENU STANDARD').first()
osimenu_premium = Item.objects.filter(title='OSIMENU PREMIUM').first()
active_order_item_basic = OrderItem.objects.filter(order__customer=request.user.customerprofile, item=osimenu_basic, active=True)
active_order_item_standard = OrderItem.objects.filter(order__customer=request.user.customerprofile, item=osimenu_standard, active=True)
active_order_item_premium = OrderItem.objects.filter(order__customer=request.user.customerprofile, item=osimenu_premium, active=True)
context = {
'osimenu_basic': osimenu_basic,
'osimenu_standard': osimenu_standard,
'osimenu_premium': osimenu_premium,
'active_order_item_basic': active_order_item_basic,
'active_order_item_standard': active_order_item_standard,
'active_order_item_premium': active_order_item_premium,
}
return render(request, 'products/osimenu-plans.html', context)

Binary file not shown.

@ -15,13 +15,9 @@
<div class="flex flex-col justify-center items-center shadow-md rounded-md cursor-pointer hover:scale-105 transition-transform duration-300 relative">
<div class="w-full py-8 px-3 bg-gray-50 flex justify-center items-center rounded-t-md relative h-[150px]">
{% if subscription.item.item_type.name == 'OSIMENU' %}
<img src="{% static 'images/ositcom_logos/osimenublue.png' %}" class="w-[80%] h-auto mt-10">
<img src="{% static 'images/ositcom_logos/osimenublue.png' %}" class="w-[80%] h-auto">
{% endif %}
<div class="w-fit px-5 py-2 bg-secondosiblue text-white rounded-r-md text-sm absolute top-3 left-0">
<p>Osimenu Basic</p>
</div>
</div>
<div class="w-[80%] absolute bottom-[-35px] left-1/2 -translate-x-1/2 bg-white border border-gray-100 shadow-md flex justify-center items-center p-3">
<p class="text-secondosiblue text-[17px]">Expires: <span class="font-semibold">{{subscription.end_at}}</span>
@ -108,11 +104,13 @@
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">20-2-2024</p>
<p class="text-secondosiblue">{{ticket.last_update_date_added}}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">Ositcom Ltd</p>
<p class="text-secondosiblue">{{ticket.last_update_added_by}}</p>
</td>
<td class="px-6 py-4 text-center items-center text-sm">

@ -416,7 +416,22 @@
<div class="w-full h-full flex flex-col justify-between items-center">
<div class="w-full flex justify-between items-center">
<p class="text-[22px] text-secondosiblue font-poppinsBold uppercase">Invoices</p>
<img src="{% static 'images/icons/invoice.png' %}" class="w-[50px]">
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 625.51 595.28" class="w-[55px]">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #20336b;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 12px;
}
</style>
</defs>
<path class="cls-1" d="M176.75,560.48c-3.59,3.77-7.55,7.19-11.68,10.24h326.35c43.13,0,77.63-31.81,82.66-74.22H206.23c-2.34,24.26-12.58,46.72-29.47,63.98Z"/>
<path class="cls-1" d="M502.56,144.47c-58.22,0-105.67,47.44-105.67,105.67v2.88c1.44,56.97,48.34,102.79,105.67,102.79s105.67-47.44,105.67-105.67-47.44-105.67-105.67-105.67ZM502.56,239.89c21.38,0,38.82,17.43,38.82,38.82,0,17.79-12.22,32.89-28.57,37.56v13.48c0,5.75-4.67,10.24-10.24,10.24s-10.24-4.67-10.24-10.24v-13.48c-16.53-4.49-28.57-19.59-28.57-37.56,0-5.75,4.67-10.24,10.24-10.24s10.24,4.67,10.24,10.24c0,10.06,8.27,18.33,18.33,18.33s18.33-8.27,18.33-18.33-8.27-18.33-18.33-18.33c-21.38,0-38.82-17.43-38.82-38.82,0-17.79,12.22-32.89,28.57-37.56v-13.48c0-5.75,4.67-10.24,10.24-10.24s10.24,4.67,10.24,10.24v13.48c16.53,4.49,28.57,19.59,28.57,37.56,0,5.75-4.67,10.24-10.24,10.24s-10.24-4.67-10.24-10.24c0-10.06-8.27-18.33-18.33-18.33s-18.33,8.27-18.33,18.33,8.09,18.33,18.33,18.33Z"/>
<path class="cls-1" d="M196.34,476.02h295.8v-99.92c-31.45-2.52-59.66-16.71-80.33-38.1H82.23c-5.75,0-10.24-4.67-10.24-10.24s4.67-10.24,10.24-10.24h313.41c-10.06-15.99-16.71-34.32-18.69-54.09H82.23c-5.75,0-10.24-4.67-10.24-10.24s4.67-10.24,10.24-10.24h294.18c1.08-19.59,6.83-37.92,15.81-54.09H82.23c-5.75,0-10.24-4.67-10.24-10.24s4.67-10.24,10.24-10.24h324.37c21.03-24.62,51.4-40.97,85.54-43.67V30.17H16.64v454.48c0,23,8.99,44.57,25.34,60.92,16.35,16.35,37.92,25.34,60.92,25.34,46.54,0,83.2-37.2,83.2-84.46,0-5.93,4.67-10.42,10.24-10.42ZM82.23,93.25h195.34c5.75,0,10.24,4.67,10.24,10.24s-4.67,10.24-10.24,10.24H82.23c-5.75,0-10.24-4.67-10.24-10.24-.18-5.57,4.49-10.24,10.24-10.24ZM82.23,412.77c-5.75,0-10.24-4.67-10.24-10.24s4.67-10.24,10.24-10.24h344.5c5.75,0,10.24,4.67,10.24,10.24s-4.67,10.24-10.24,10.24H82.23Z"/>
</svg>
</div>
<div
class="w-[60px] h-[60px] bg-white border-2 rounded-full border-secondosiblue flex justify-center items-center">
@ -429,7 +444,31 @@
<div class="w-full h-full flex flex-col justify-between items-center">
<div class="w-full flex justify-between items-center">
<p class="text-[22px] text-secondosiblue font-poppinsBold uppercase">Projects</p>
<img src="{% static 'images/icons/projects.png' %}" class="w-[50px]">
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 625.51 595.28" class="w-[60px]">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #20336b;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 12px;
}
</style>
</defs>
<circle class="cls-1" cx="267.98" cy="343.01" r="245.83" transform="translate(-164.05 289.95) rotate(-45)"/>
<circle class="cls-1" cx="267.98" cy="343.01" r="190.73"/>
<circle class="cls-1" cx="267.98" cy="343.01" r="128.57"/>
<circle class="cls-1" cx="267.98" cy="343.01" r="73.47" transform="translate(-71.91 71.08) rotate(-13.34)"/>
<circle class="cls-1" cx="267.98" cy="343.01" r="25.43"/>
<line class="cls-1" x1="267.98" y1="343.01" x2="581.63" y2="46.31"/>
<polygon class="cls-1" points="467.19 155.1 457.3 81.63 536.41 5.34 547.72 75.98 467.19 155.1"/>
<polyline class="cls-1" points="467.19 155.1 530.76 173.47 615.53 100 547.72 75.98"/>
<line class="cls-1" x1="506.75" y1="40.66" x2="516.64" y2="102.83"/>
<line class="cls-1" x1="481.31" y1="60.44" x2="491.2" y2="126.84"/>
<line class="cls-1" x1="495.44" y1="133.91" x2="554.78" y2="150.86"/>
<line class="cls-1" x1="522.29" y1="107.07" x2="588.69" y2="124.02"/>
</svg>
</div>
<div
class="w-[60px] h-[60px] bg-white border-2 rounded-full border-secondosiblue flex justify-center items-center">
@ -442,7 +481,24 @@
<div class="w-full h-full flex flex-col justify-between items-center">
<div class="w-full flex justify-between items-center">
<p class="text-[22px] text-secondosiblue font-poppinsBold uppercase">Tickets</p>
<img src="{% static 'images/icons/tickets.png' %}" class="w-[50px]">
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 625.51 595.28" class="w-[60px]">
<defs>
<style>
.cls-1 {
fill: none;
stroke: #20336b;
stroke-linecap: round;
stroke-linejoin: round;
stroke-width: 12px;
}
</style>
</defs>
<path class="cls-1" d="M592.85,237.81c-3.84,15.46-14.66,25.96-25.44,36.71-99.46,99.26-198.83,198.62-298.14,298.05-15.33,15.35-32.97,20.62-53.38,12.67-6.16-2.4-12.07-6.61-16.8-11.3-58.45-58.1-116.72-116.39-174.92-174.74-20.05-20.1-20.08-48.56-.09-68.57C127.43,227.18,230.88,123.84,334.18,20.36c7.48-7.49,16.48-11.74,26.2-15.02h17.09c15.26,3.79,25.66,14.42,36.27,25.08,51.98,52.23,104.06,104.35,156.33,156.29,10.23,10.17,19.73,20.48,22.78,35.14v15.95Z"/>
<path class="cls-1" d="M449.31,244.57c-3.71,9.02-7.09,17.22-10.89,26.45,2.6,2.5,5.96,5.5,9.07,8.75,6.93,7.25,7.1,17.86.52,24.56-6.75,6.88-17.06,6.96-24.5-.09-3.28-3.11-6.08-6.73-8.36-9.29-8.95,3.41-16.56,6.17-24.01,9.29-1.32.55-2.67,2.55-2.83,4.01-.42,3.75.01,7.59-.25,11.37-.68,9.69-7.7,16.37-16.98,16.41-9.15.04-17.89-6.95-16.97-16.19,1.18-11.9-3.08-17.48-14.34-19.62-3.93-.75-7.48-3.52-12.15-5.83-2.72,2.9-5.56,6.17-8.66,9.18-7.7,7.47-17.88,7.71-24.91.74-7.03-6.97-6.86-17.27.52-24.91,2.89-3,5.98-5.8,8.59-8.31-3.23-8.49-6.13-16.42-9.35-24.22-.46-1.11-2.47-2.1-3.84-2.23-3.58-.33-7.21-.02-10.81-.19-9.82-.45-16.98-7.67-17.07-17.07-.09-9.45,6.91-17.85,16.71-16.95,11.33,1.03,16.84-2.74,18.89-13.68.77-4.12,3.63-7.85,6.3-13.32-2.96-2.53-6.64-5.23-9.8-8.45-7.2-7.35-7.33-17.58-.62-24.49,6.72-6.92,17.52-6.82,24.94.4,3.12,3.04,5.93,6.39,8.41,9.08,9.27-3.86,17.62-7.34,26.65-11.1,0-3.89-.09-8.62.02-13.34.21-9.49,7.81-17.13,16.98-17.17,9.25-.04,16.88,7.46,17.13,16.99.12,4.73.02,9.46.02,13.59,8.92,3.67,17.28,7.12,26.87,11.07,2.34-2.56,5.23-6.06,8.48-9.19,7.29-7.02,17.76-7.08,24.54-.36,6.77,6.71,6.77,17.17-.19,24.55-3.11,3.3-6.57,6.27-9.21,8.77,3.86,9.28,7.33,17.64,11.07,26.64,4.28,0,9.2-.15,14.11.03,8.88.33,16.08,7.71,16.37,16.56.29,8.8-7.09,16.98-16.1,17.49-4.91.28-9.84.05-14.35.05ZM370.88,272.25c24.96.03,44.86-19.67,45-44.55.14-24.77-20.23-45.25-45.14-45.38-24.72-.12-44.74,19.88-44.8,44.75-.06,25.23,19.75,45.14,44.93,45.18Z"/>
<path class="cls-1" d="M219.11,279.64c7.76,1.4,13.03,4.55,16.03,11.04,2.99,6.47,2,12.58-2.31,18.13-1.27,1.64-2.86,3.04-4.33,4.52-28.2,28.2-56.4,56.4-84.6,84.6-5.22,5.22-11.23,7.62-18.59,5.11-6.79-2.31-10.57-7.2-11.61-14.13-.85-5.64,1.18-10.5,5.13-14.46,29.37-29.44,58.71-58.93,88.31-88.14,3.13-3.09,7.95-4.49,11.98-6.67Z"/>
<path class="cls-1" d="M318.5,378.97c-2.18,4.03-3.58,8.83-6.67,11.96-29.2,29.61-58.64,58.99-88.15,88.3-7.28,7.23-17.71,7.11-24.49.33-7.02-7.02-6.91-17.31.61-24.86,29.22-29.33,58.51-58.58,87.81-87.83,5.49-5.48,11.96-7.12,19.28-4.18,6.44,2.59,10.32,8.56,11.62,16.29Z"/>
<path class="cls-1" d="M173.66,444.32c-10.13-.41-15.6-3.72-18.36-10.53-2.82-6.94-1.61-13.48,3.69-18.91,7.83-8.01,15.81-15.88,23.74-23.8,13.16-13.16,26.3-26.34,39.49-39.47,8.22-8.18,18.42-8.68,25.72-1.35,7.28,7.31,6.78,17.47-1.47,25.75-20.64,20.72-41.24,41.48-62.16,61.92-3.42,3.34-8.4,5.08-10.65,6.39Z"/>
</svg>
</div>
<div
class="w-[60px] h-[60px] bg-white border-2 rounded-full border-secondosiblue flex justify-center items-center">

@ -5,7 +5,7 @@
<div class="w-full xxlg1:w-[75%]">
<div class="w-full h-fit bg-white rounded-md shadow-md p-5">
<h1 class="text-secondosiblue text-[25px]">Tags</h1>
<h1 class="text-secondosiblue text-[25px]">Tickets</h1>
<!-- FILTERING -->
<div

@ -2251,6 +2251,10 @@ video {
background-color: rgb(202 138 4 / var(--tw-bg-opacity));
}
.bg-opacity-10 {
--tw-bg-opacity: 0.1;
}
.bg-opacity-20 {
--tw-bg-opacity: 0.2;
}
@ -2776,6 +2780,11 @@ video {
color: rgb(75 85 99 / var(--tw-text-opacity));
}
.text-green-500 {
--tw-text-opacity: 1;
color: rgb(34 197 94 / var(--tw-text-opacity));
}
.text-green-600 {
--tw-text-opacity: 1;
color: rgb(22 163 74 / var(--tw-text-opacity));

@ -0,0 +1,17 @@
<?php
/**
* Front to the WordPress application. This file doesn't do anything, but loads
* wp-blog-header.php which does and tells WordPress to load the theme.
*
* @package WordPress
*/
/**
* Tells WordPress to load the WordPress theme and output it.
*
* @var bool
*/
define( 'WP_USE_THEMES', true );
/** Loads the WordPress Environment and Template */
require __DIR__ . '/wp-blog-header.php';

@ -0,0 +1,17 @@
<?php
/**
* Front to the WordPress application. This file doesn't do anything, but loads
* wp-blog-header.php which does and tells WordPress to load the theme.
*
* @package WordPress
*/
/**
* Tells WordPress to load the WordPress theme and output it.
*
* @var bool
*/
define( 'WP_USE_THEMES', true );
/** Loads the WordPress Environment and Template */
require __DIR__ . '/wp-blog-header.php';

@ -0,0 +1,17 @@
<?php
/**
* Front to the WordPress application. This file doesn't do anything, but loads
* wp-blog-header.php which does and tells WordPress to load the theme.
*
* @package WordPress
*/
/**
* Tells WordPress to load the WordPress theme and output it.
*
* @var bool
*/
define( 'WP_USE_THEMES', true );
/** Loads the WordPress Environment and Template */
require __DIR__ . '/wp-blog-header.php';
Loading…
Cancel
Save