emile 12 months ago
parent 7dbffeb7d9
commit e1cab198e3

BIN
.DS_Store vendored

Binary file not shown.

BIN
osinaweb/.DS_Store vendored

Binary file not shown.

Binary file not shown.

@ -6,11 +6,14 @@ urlpatterns = [
path('product', views.add_product, name='addproduct'),
path('service', views.add_service, name='addservice'),
path('order/<int:customer_id>/', views.add_order, name='addorder'),
path('invoice/<int:order_id>/', views.add_invoice_pdf, name='addinvoice'),
path('payment/<int:order_id>/', views.add_payment_pdf, name='addpayment'),
path('invoice-pdf/<int:order_id>/', views.add_invoice_pdf, name='addinvoice'),
path('payment-pdf/<int:order_id>/', views.add_payment_pdf, name='addpayment'),
path('service/<int:service_id>/<int:order_id>/', views.add_service_in_order, name='addserviceinorder'),
path('add_payment_comment', views.add_payment_comment_modal, name='add_payment_comment_modal'),
path('add_payment', views.add_payment_modal, name='add_payment_modal'),
path('payment_method_modal/', views.add_payment_method_modal, name='add_payment_method_modal'),
path('payment/<int:order_id>/', views.add_payment_modal, name='add_payment_modal'),
path('payment_comment/', views.add_payment_comment_modal, name='add_payment_comment_modal'),
]

@ -82,8 +82,8 @@ def add_order(request, customer_id):
services = Item.objects.filter(Q(type='Service') & (Q(customer=customer) | Q(customer__isnull=True)))
if request.method == 'POST':
status = request.POST.get('status')
business_id = request.POST.get('business')
date = request.POST.get('date')
if business_id:
business = get_object_or_404(Business, id=business_id)
else:
@ -92,10 +92,20 @@ def add_order(request, customer_id):
order = Order.objects.create(
customer=customer,
status=status,
business=business,
date=date
)
return redirect('orderdetails', order_id=order.id)
selected_services_ids = request.POST.getlist('service')
for service_id in selected_services_ids:
service = get_object_or_404(Item, id=service_id)
order_item = OrderItem.objects.create(
order=order,
item=service,
purchased_at=datetime.now()
)
return redirect('orderdetails', order_id=order.order_id)
context = {
'customer' : customer,
@ -118,28 +128,87 @@ def add_service_in_order(request, service_id, order_id):
purchased_at = datetime.now()
)
order_item.save()
return redirect('orderdetails', order_id=order.id)
return redirect('orderdetails', order_id=order.order_id)
@staff_login_required
def add_payment_comment_modal(request):
def add_payment_method_modal(request, *args, **kwargs):
if request.method == 'POST':
name = request.POST.get('name')
description = request.POST.get('description')
image = request.FILES.get('image')
payment_method = PaymentType(
name = name,
description = description,
image = image
)
payment_method.save()
# Reload the parent page
return HttpResponse('<script>window.top.location.reload();</script>')
return render(request, 'add_templates/add-payment-method-modal.html')
context = {
@staff_login_required
def add_payment_modal(request, order_id):
order = get_object_or_404(Order, id=order_id)
methods = PaymentType.objects.all().order_by('name')
if request.method == 'POST':
amount = request.POST.get('amount')
if request.POST.get('date_paid'):
date_paid = request.POST.get('date_paid')
else:
date_paid = None
if request.POST.get('date_due'):
date_due = request.POST.get('date_due')
else:
date_due = None
comment = request.POST.get('comment')
selected_methods = request.POST.getlist('methods')
payment = OrderPayment(
order = order,
amount = amount,
date_paid = date_paid,
date_due = date_due,
comment = comment
)
payment.save()
for method_id in selected_methods:
method = get_object_or_404(PaymentType, id=method_id)
payment.type.add(method)
# Reload the parent page
return HttpResponse('<script>window.top.location.reload();</script>')
context = {
'methods': methods,
'order': order
}
return render(request, 'add_templates/add-payment-comment-modal.html', context)
return render(request, 'add_templates/add-payment-modal.html', context)
@staff_login_required
def add_payment_modal(request):
def add_payment_comment_modal(request):
context = {
}
return render(request, 'add_templates/add-payment-modal.html', context)
return render(request, 'add_templates/add-payment-comment-modal.html', context)
@ -206,6 +275,10 @@ def add_invoice_pdf(request, order_id):
def add_payment_pdf(request, order_id):
order = get_object_or_404(Order, id=order_id)
payments = OrderPayment.objects.filter(order = order)
paid_amount = OrderPayment.objects.filter(order=order, date_paid__isnull=False).aggregate(total_paid=Sum('amount'))['total_paid'] or 0
cart_total = order.get_cart_total
remaining_amount = cart_total - paid_amount
invoice = order.invoice
@ -213,7 +286,7 @@ def add_payment_pdf(request, order_id):
invoice_template = get_template('details_templates/invoice-details.html')
payment_template = get_template('details_templates/payment-details.html')
invoice_html = invoice_template.render({'order': order})
payment_html = payment_template.render({'order': order, 'payments':payments})
payment_html = payment_template.render({'order': order, 'payments':payments, 'remaining_amount':remaining_amount,})
# Combine the HTML content of both templates
combined_html = f"{invoice_html}<div style='page-break-before: always;'></div>{payment_html}"
@ -249,5 +322,3 @@ def add_payment_pdf(request, order_id):

@ -6,6 +6,7 @@ from .models import *
admin.site.register(RecurringCycle)
admin.site.register(Item)
admin.site.register(Order)
admin.site.register(OrderStatus)
admin.site.register(OrderItem)
admin.site.register(Invoice)
admin.site.register(OrderPayment)

@ -3,5 +3,6 @@ from billing.delete import views
urlpatterns = [
path('orderitem<int:orderitem_id>/', views.delete_orderitem, name='deleteorderitem'),
path('orderitem/<int:orderitem_id>/', views.delete_orderitem, name='deleteorderitem'),
path('paymentmethod/<int:method_id>/', views.delete_payment_method_modal, name='deletepaymentmethodmodal')
]

@ -6,6 +6,22 @@ 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_id = order_item.order.order_id
order_item.delete()
return redirect('orderdetails', order_id=order_id)
@staff_login_required
def delete_payment_method_modal(request, method_id):
method = get_object_or_404(PaymentType, id=method_id)
if request.method == 'POST':
method.delete()
return redirect('paymentmethods')
context = {
'method': method
}
return render(request, "delete_templates/delete-payment-method-modal.html", context)

@ -3,5 +3,6 @@ from billing.edit import views
urlpatterns = [
path('edit_payment_modal', views.edit_payment_modal, name='edit_payment_modal'),
path('paymentmethod/<int:method_id>/', views.edit_payment_method, name='editpaymentmethod'),
path('payment/', views.edit_payment_modal, name='edit_payment_modal'),
]

@ -3,6 +3,28 @@ from osinacore.models import *
from billing.models import *
from osinacore.decorators import *
@staff_login_required
def edit_payment_method(request, method_id):
method = get_object_or_404(PaymentType, id=method_id)
if request.method == 'POST':
method.name = request.POST.get('name')
method.description = request.POST.get('description')
if request.FILES.get('image'):
method.image = request.FILES.get('image')
method.save()
return redirect('paymentmethods')
context = {
'method': method,
}
return render(request, 'edit_templates/edit-payment-method.html', context)
@staff_login_required
def edit_payment_modal(request):

@ -0,0 +1,23 @@
# Generated by Django 4.2.5 on 2024-05-09 13:47
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0046_remove_orderpayment_order_orderpayment_order'),
]
operations = [
migrations.AddField(
model_name='paymenttype',
name='description',
field=models.TextField(blank=True, null=True),
),
migrations.AddField(
model_name='paymenttype',
name='image',
field=models.ImageField(blank=True, null=True, upload_to=''),
),
]

@ -0,0 +1,27 @@
# Generated by Django 4.2.5 on 2024-05-09 18:48
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('billing', '0047_paymenttype_description_paymenttype_image'),
]
operations = [
migrations.RemoveField(
model_name='order',
name='status',
),
migrations.CreateModel(
name='OrderStatus',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('status', models.CharField(choices=[('Completed', 'Completed'), ('Pending', 'Pending'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled')], default='Pending', max_length=200)),
('date', models.DateTimeField()),
('order', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='billing.order')),
],
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-05-09 18:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('billing', '0048_remove_order_status_orderstatus'),
]
operations = [
migrations.RenameField(
model_name='order',
old_name='due_date',
new_name='date',
),
]

@ -0,0 +1,22 @@
# Generated by Django 4.2.5 on 2024-05-09 19:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0049_rename_due_date_order_date'),
]
operations = [
migrations.RemoveField(
model_name='orderpayment',
name='type',
),
migrations.AddField(
model_name='orderpayment',
name='type',
field=models.ManyToManyField(null=True, to='billing.paymenttype'),
),
]

@ -0,0 +1,18 @@
# Generated by Django 4.2.5 on 2024-05-09 19:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0050_remove_orderpayment_type_orderpayment_type'),
]
operations = [
migrations.AlterField(
model_name='orderpayment',
name='amount',
field=models.FloatField(null=True),
),
]

@ -32,18 +32,10 @@ class RecurringCycle(models.Model):
class Order(models.Model):
STATUS = (
('Completed', 'Completed'),
('Failed', 'Failed'),
('Cancelled', 'Cancelled'),
('None', 'None'),
('Pending', 'Pending'),
)
customer = models.ForeignKey(CustomerProfile, on_delete=models.CASCADE)
business = models.ForeignKey(Business, on_delete=models.SET_NULL, null=True, blank=True)
status = models.CharField(max_length=200, choices=STATUS, default='None')
order_id = models.CharField(max_length=100, null=True, blank=True)
due_date = models.DateField(null=True, blank=True)
date = models.DateField(null=True, blank=True)
@property
def get_cart_total(self):
orderitems = self.orderitem_set.all()
@ -64,6 +56,19 @@ class Order(models.Model):
super(Order, self).save(*args, **kwargs)
class OrderStatus(models.Model):
STATUS = (
('Completed', 'Completed'),
('Pending', 'Pending'),
('Failed', 'Failed'),
('Cancelled', 'Cancelled'),
)
order = models.ForeignKey(Order, on_delete=models.CASCADE)
status = models.CharField(max_length=200, choices=STATUS, default='Pending')
date = models.DateTimeField()
class OrderItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.CASCADE)
item = models.ForeignKey(Item, on_delete=models.CASCADE)
@ -104,16 +109,18 @@ class Invoice(models.Model):
class PaymentType(models.Model):
name = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
image = models.ImageField(null=True, blank=True)
def __str__(self):
return self.name
class OrderPayment(models.Model):
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
amount = models.DecimalField(decimal_places=3, max_digits = 5)
amount = models.FloatField(null=True)
date_paid = models.DateField(null=True,blank=True)
date_due = models.DateField(null=True, blank=True)
type = models.ForeignKey(PaymentType, null=True, on_delete=models.SET_NULL)
type = models.ManyToManyField(PaymentType, null=True)
comment = models.TextField(null=True, blank=True)
def __str__(self):
return f"Payment for {self.order}"

Binary file not shown.

@ -18,6 +18,8 @@
<form method="POST" action="{% url 'addorder' customer.id %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="w-full flex flex-col gap-5">
<div class="w-full">
<label class="text-gray-500">Business:</label>
<select name="business"
@ -30,8 +32,16 @@
</div>
<div class="w-full">
<label class="text-gray-500">Date:</label>
<input required name="date" type="date" id="date" name="date"
class="w-full p-3 border border-gray-300 rounded-md bg-transparent outline-none mt-1">
</div>
<select name="service" id="servicesSelectTag"
class="w-full h-[100px] py-1 px-3 border border-gray-300 outline-none rounded-md text-gray-500 mt-1"
class="w-full h-[100px] py-1 px-3 border border-gray-300 outline-none rounded-md text-gray-500 mt-1 hidden"
multiple required>
</select>

@ -14,28 +14,28 @@
</head>
<body class="font-poppinsLight">
<form id="hiddenContent" method="POST" action="{% url 'addtagmodal' %}">
<form id="hiddenContent" method="POST" enctype="multipart/form-data" action="{% url 'add_payment_method_modal' %}">
{% csrf_token %}
<h1 class="text-secondosiblue text-2xl font-semibold text-center">Add Payment Method</h1>
<div class="w-full mt-4">
<label class="text-gray-500">Payment Method:</label>
<input name="title" type="text"
<input name="name" type="text" required
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1">
</div>
<div class="w-full mt-4">
<label class="text-gray-500">Description:</label>
<textarea name="" type="text" placeholder="Description"
<textarea name="description" type="text" placeholder="Description"
class="w-full p-3 border border-gray-300 rounded-md bg-transparent outline-none mt-1 resize-none"
rows="4" required></textarea>
rows="4" ></textarea>
</div>
<div class="w-full mt-4">
<label class="text-gray-500">Payment Logo:</label>
<label class="text-gray-500">Image:</label>
<div class="inbox-box border border-gray-300 w-full rounded-md px-3 mt-1">
<div class="flex items-center justify-between">
<input name="logo" required type="file" class="file-input" accept="image/*" hidden />
<input name="image" type="file" class="file-input" accept="image/*" hidden />
<span class="file-name text-gray-500 text-base focus:outline-none outline-none">Upload
Logo</span>
<label

@ -20,26 +20,29 @@
<div class="w-full mt-4">
<label class="text-gray-500">Amount:</label>
<input name="title" type="decimal"
<input name="amount" type="number" step="0.01"
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1" placeholder="Amount" required>
</div>
<div class="w-full mt-4">
<label class="text-gray-500">Date Paid:</label>
<input name="title" type="date"
<input name="date_paid" type="date"
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1">
</div>
<div class="w-full mt-4">
<label class="text-gray-500">Date Due:</label>
<input name="title" type="date"
<input name="date_due" type="date"
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1">
</div>
<div class="w-full mt-4">
<label class="text-gray-500">Payment Method:</label>
<select name="" id="" class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1">
<option value="" selected disabled>Payment Method</option>
<label class="text-gray-500">Payment Method(s):</label>
<select required name="methods" id="" multiple class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1">
<option value="" selected disabled>Select Payment Method</option>
{% for method in methods %}
<option value="{{method.id}}" >{{method.name}}</option>
{% endfor %}
</select>
</div>

@ -13,7 +13,7 @@
</head>
<body class="font-poppinsLight">
<form id="hiddenContent" method="post" action="" target="_parent">
<form id="hiddenContent" method="post" action="{% url 'deletepaymentmethodmodal' method.id %}" target="_parent">
{% csrf_token %}
<div class="h-[140px] flex flex-col justify-center items-center">

@ -161,7 +161,7 @@
</div>
<div class="companyDetails">
<h1>Date: 2024-03-03</h1>
<h1>Date: {{order.date}}</h1>
<p>OSITCOM LTD</p>
<p>CR. 2014057</p>

@ -13,11 +13,16 @@
<h1 class="text-secondosiblue text-xl">Order {{order.order_id}} - {{order.customer.user.first_name}}
{{order.customer.user.last_name}}</h1>
<p class="text-gray-500"></p>
{% if order.orderstatus.set_all.last.status %}
<p>
<span
class="uppercase text-green-700 font-semibold text-sm {% if order.status == 'Completed' %} text-green-700 {% elif order.status == 'Pending' %} text-yellow-500 {% elif order.status == 'Failed' %} text-red-500 {% endif %}">{{order.status}}</span>
<span class="text-sm font-light text-gray-500"> - 12-02-2024</span>
class="uppercase text-green-700 font-semibold text-sm {% if order.orderstatus.set_all.last.status == 'Completed' %} text-green-700 {% elif order.orderstatus.set_all.last.status == 'Pending' %} text-yellow-500 {% elif order.orderstatus.set_all.last.status == 'Failed' %} text-red-500 {% endif %}">{{order.status}}</span>
<span class="text-sm font-light text-gray-500"> {{order.orderstatus.set_all.last.status}} - {{order.orderstatus.set_all.last.date}}</span>
</p>
{% endif %}
</div>
@ -26,10 +31,22 @@
class="w-full s:w-fit text-base px-3 py-2 bg-osiblue text-white outline-none border border-osiblue rounded-md cursor-pointer hover:bg-white hover:text-osiblue duration-300">
Add Payment
</button>
<button
class="w-full s:w-fit text-base px-3 py-2 bg-osiblue text-white outline-none border border-osiblue rounded-md cursor-pointer hover:bg-white hover:text-osiblue duration-300">
Generate Invoice
</button>
{% if not order.invoice %}
<a href="{% url 'addinvoice' order.id %}">
<button
class="w-full s:w-fit text-base px-3 py-2 bg-osiblue text-white outline-none border border-osiblue rounded-md cursor-pointer hover:bg-white hover:text-osiblue duration-300">
Generate Invoice
</button>
</a>
{% else %}
<a href="{{order.invoice.pdf.url}}">
<button
class="w-full s:w-fit text-base px-3 py-2 bg-osiblue text-white outline-none border border-osiblue rounded-md cursor-pointer hover:bg-white hover:text-osiblue duration-300">
Download Invoice
</button>
</a>
{% endif %}
<button
class="w-full s:w-fit text-base px-3 py-2 bg-osiblue text-white outline-none border border-osiblue rounded-md cursor-pointer hover:bg-white hover:text-osiblue duration-300">
Update Status
@ -115,7 +132,7 @@
<button
class="h-full rounded-tr-md px-4 bg-secondosiblue text-gray-200 text-[18px] outline-none border-none cursor-pointer flex justify-center items-center addPaymentButton"
data-modal-url="{% url 'add_payment_modal' %}">
data-modal-url="{% url 'add_payment_modal' order.id %}">
<i class="fa fa-plus"></i>
</button>
</div>
@ -161,15 +178,25 @@
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">{{payment.date_due}}</p>
{% if payment.date_due %}
<p class="text-secondosiblue">{{payment.date_due}}</p>
{% else %}
<p class="text-secondosiblue">-</p>
{% endif %}
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
{% if payment.date_paid %}
<p class="text-secondosiblue">{{payment.date_paid}}</p>
{% else %}
<p class="text-red-500">UNPAID</p>
{% endif %}
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">{{payment.type}}</p>
{% for type in payment.type.all %}
<p class="text-secondosiblue">{{type.name}}</p>
{% endfor %}
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">

@ -148,7 +148,7 @@
<div class="invoiceNumberBar">
<p>Payment</p>
<p>Payments</p>
</div>
@ -165,9 +165,27 @@
{% for payment in payments %}
<tr>
<td class="payment-cell">${{payment.amount}}</td>
<td class="payment-cell">{{payment.date_due}}</td>
<td class="payment-cell">{{payment.date_paid}}</td>
<td class="payment-cell payment-cell-last">{{payment.type}}</td>
<td class="payment-cell">
{% if payment.date_due %}
{{payment.date_due}}
{% else %}
-
{% endif %}
</td>
<td class="payment-cell">
{% if payment.date_paid %}
{{payment.date_paid}}
{% else %}
<p style="color: #ef4444;">UNPAID</p>
{% endif %}
</td>
<td class="payment-cell payment-cell-last">
{% for type in payment.type.all %}
{{type.name}}
{% endfor %}
</td>
</tr>
{% endfor %}
</tbody>
@ -176,7 +194,7 @@
<div class="remainingAmountContainer">
<p>Remaining Amount: <span>$0.00</span></p>
<p>Remaining Amount: <span>${{remaining_amount}}</span></p>
</div>
</div>

@ -8,28 +8,30 @@
Edit Payment Method
</h1>
<form id="hiddenContent" method="POST" action="">
<form id="hiddenContent" method="POST" action="{% url 'editpaymentmethod' method.id %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="w-full flex flex-col gap-5 justify-center items-center">
<div class="w-full">
<label class="text-gray-500">Payment Method:</label>
<input name="title" type="text" value="shdhd"
<input name="name" type="text" value="{{method.name}}"
class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-1">
</div>
<div class="w-full">
<label class="text-gray-500">Description:</label>
<textarea name="" type="text" placeholder="Description"
<textarea name="description" type="text" placeholder="Description"
class="w-full p-3 border border-gray-300 rounded-md bg-transparent outline-none mt-1 resize-none"
rows="4" required>dfsdfds dd</textarea>
rows="4" required>{{method.description}}</textarea>
</div>
<div class="w-full">
<label class="text-gray-500">Payment Logo:</label>
<label class="text-gray-500">Current Image:
<a href="{{method.image.url}}" class="text-gray-400 cursor-pointer hover:text-slate-800" style="word-wrap: break-word;" target="_blank">{{method.image}}</a>
</label>
<div class="inbox-box border border-gray-300 w-full rounded-md px-3 mt-1">
<div class="flex items-center justify-between">
<input name="logo" required type="file" class="file-input" accept="image/*" hidden />
<input name="image" type="file" class="file-input" accept="image/*" hidden />
<span class="file-name text-gray-500 text-base focus:outline-none outline-none">Upload
Logo</span>
<label
@ -39,6 +41,12 @@
</div>
</div>
</div>
<div class="w-full flex justify-center items-center mt-4">
<button type="submit"
class="w-fit bg-osiblue border border-osiblue rounded-md text-white text-xl px-5 py-1 hover:bg-white hover:text-osiblue duration-300">Save</button>
</div>
</div>
</form>

@ -64,13 +64,13 @@
<p class="text-secondosiblue">${{order.get_cart_total}}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300 {% if order.status == 'Completed' %} bg-green-700 {% elif order.status == 'Pending' %} bg-yellow-500 {% else %} bg-gray-400 {% endif %}">
<p class="text-white">{{order.status}}</p>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300 {% if order.orderstatus_set.all.last.status == 'Completed' %} bg-green-700 {% elif order.orderstatus_set.all.last.status == 'Pending' %} bg-yellow-500 {% else %} bg-gray-400 {% endif %}">
<p class="text-white">{{order.orderstatus_set.all.last.status}}</p>
</td>
<td class="px-6 py-4">
<div class="w-full flex justify-center items-center gap-3">
{% if order.status == 'Pending' and not order.invoice %}
{% if order.orderstatus_set.all.last.status == 'Pending' and not order.invoice %}
<a href="{% url 'addinvoice' order.id %}">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-[18px] text-fifthosiblue hover:scale-110 duration-500 transition-transform">
<path stroke-linecap="round" stroke-linejoin="round" d="M19.5 14.25v-2.625a3.375 3.375 0 0 0-3.375-3.375h-1.5A1.125 1.125 0 0 1 13.5 7.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 0 0-9-9Z" />
@ -86,7 +86,7 @@
</a>
{% endif %}
<a href="{% url 'orderdetails' order.id %}">
<a href="{% url 'orderdetails' order.order_id %}">
<div class="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-[18px] text-fifthosiblue hover:scale-110 duration-500 transition-transform">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" />

@ -48,28 +48,31 @@
<!-- TABLE BODY -->
<tbody class="bg-white divide-y divide-gray-200">
{% for method in methods %}
<tr>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<div class="w-full flex justify-center items-center gap-2 text-gray-500 text-sm">
<img src="{% static 'images/icons/whishlogo.png' %}" class="w-[30px] rounded-md">
<p>Whish</p>
{% if method.image %}
<img src="{{method.image.url}}" class="w-[30px] rounded-md">
{% endif %}
<p>{{method.name}}</p>
</div>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">Helo this is a description</p>
<p class="text-secondosiblue">{{method.description}}</p>
</td>
<td class="px-6 py-4 text-center text-sm">
<div class="flex justify-center items-center gap-3">
<a href="{% url 'editpaymentmethod' %}">
<a href="{% url 'editpaymentmethod' method.id %}">
<div class="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-[18px] text-fifthosiblue hover:scale-110 duration-500 transition-transform">
<path stroke-linecap="round" stroke-linejoin="round" d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10" />
</svg>
</div>
</a>
<div data-modal-url="{% url 'deletepaymentmethodmodal' %}" class="deletePaymentMethodButton">
<div data-modal-url="{% url 'deletepaymentmethodmodal' method.id %}" class="deletePaymentMethodButton">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-[18px] text-red-500 hover:scale-110 duration-500 transition-transform">
<path stroke-linecap="round" stroke-linejoin="round" d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0" />
</svg>
@ -77,6 +80,7 @@
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

@ -1,4 +1,3 @@
from django.urls import path, include
from billing import views
@ -6,16 +5,16 @@ from billing import views
urlpatterns = [
path('add/', include('billing.add.urls')),
path('delete/', include('billing.delete.urls')),
path('delete/', include('billing.edit.urls')),
path('edit/', include('billing.edit.urls')),
# LISTING
path('items/', views.items, name='items'),
path('orders/', views.orders, name='orders'),
path('invoices/', views.invoices, name='invoices'),
path('paymentmethods/', views.payment_methods, name='paymentmethods'),
# DETAILS
path('invoices/<int:order_id>/', views.invoice_details, name='invoicedetails'),
path('orders/<int:order_id>/', views.order_details, name='orderdetails'),
path('paymyent/<int:order_id>/', views.payment_details, name='paymentdetails'),
path('orders/<str:order_id>/', views.order_details, name='orderdetails'),
path('payments/<int:order_id>/', views.payment_details, name='paymentdetails'),
]

@ -34,6 +34,19 @@ def invoices(request, *args, **kwargs):
return render(request, 'listing_pages/invoices.html', context)
def payment_methods(request):
methods = PaymentType.objects.all().order_by('-id')
context = {
'methods': methods,
}
return render(request, 'listing_pages/payment-methods.html', context)
#DETAILS
def invoice_details(request, order_id):
order = get_object_or_404(Order, id=order_id)
@ -46,7 +59,7 @@ def invoice_details(request, order_id):
def order_details(request, order_id):
order = get_object_or_404(Order, id=order_id)
order = get_object_or_404(Order, 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)

@ -7,4 +7,4 @@ admin.site.register(TicketStatusUpdate)
admin.site.register(TicketRead)
admin.site.register(TicketUpdate)
admin.site.register(TicketAttachment)
admin.site.register(TicketReaction)
admin.site.register(TicketUpdateReaction)

@ -9,7 +9,6 @@ def utilities(request):
customer_open_tickets_count = None
customer_open_projects = None
customer_open_projects_count = None
customer_open_invoices = None
customer_open_invoices_count = None
if request.user.is_authenticated and CustomerProfile.objects.filter(user=request.user):
customer = request.user.customerprofile
@ -18,8 +17,9 @@ def utilities(request):
customer_open_projects = Project.objects.filter(Q(status='Active') | Q(status='Pending'), customer=customer)
customer_open_projects_count = customer_open_projects.count()
customer_open_invoices = Invoice.objects.filter(order__status='Open')
customer_open_invoices_count = customer_open_invoices.count()
orders_with_invoice = Order.objects.filter(customer=customer, invoice__isnull=False)
orders_without_completed_status = orders_with_invoice.exclude(orderstatus__status='Completed')
customer_open_invoices_count = orders_without_completed_status.count()
customer_open_tickets = Ticket.objects.filter(
Q(status__in=['Open', 'Working On']) & Q(customer=request.user.customerprofile)
@ -42,6 +42,4 @@ def utilities(request):
ticket.last_update_date_added = None
customer_open_invoices_count = customer_open_invoices.count()
customer_open_invoices_count = customer_open_invoices.count()
return {'active_subscriptions': active_subscriptions, 'customer_open_tickets': customer_open_tickets, 'customer_open_tickets_count':customer_open_tickets_count, 'customer_open_projects_count': customer_open_projects_count, 'customer_open_invoices_count': customer_open_invoices_count}

@ -0,0 +1,17 @@
# Generated by Django 4.2.5 on 2024-05-09 18:54
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('customercore', '0009_alter_ticketattachment_ticket'),
]
operations = [
migrations.RenameModel(
old_name='TicketReaction',
new_name='TicketUpdateReaction',
),
]

@ -0,0 +1,21 @@
# Generated by Django 4.2.5 on 2024-05-09 18:56
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('customercore', '0010_rename_ticketreaction_ticketupdatereaction'),
]
operations = [
migrations.AddField(
model_name='ticketupdatereaction',
name='customer',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
]

@ -70,11 +70,12 @@ class TicketAttachment(models.Model):
file = models.FileField()
class TicketReaction(models.Model):
class TicketUpdateReaction(models.Model):
REACTION_CHOICES = (
('Happy', 'Happy'),
('Indifferent', 'Indifferent'),
('Sad', 'Sad'),
)
customer = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
reaction = models.CharField(max_length=50, choices=REACTION_CHOICES, null=True)
ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE)

@ -130,20 +130,26 @@
did I do?</a>
<div class="flex justify-start items-center gap-2">
<div class="w-fit h-fit rounded-full border-2 border-secondosiblue">
<img src="{% static 'images/icons/happy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
<div class="w-fit h-fit rounded-full">
<img src="{% static 'images/icons/neutral-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
<div class="w-fit h-fit rounded-full">
<img src="{% static 'images/icons/unhappy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
<a href="{% url 'customeraddticketupdatereaction' 'Happy' update.id %}">
<div class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Happy' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/happy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
<a href="{% url 'customeraddticketupdatereaction' 'Indifferent' update.id %}">
<div class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Indifferent' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/neutral-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
<a href="{% url 'customeraddticketupdatereaction' 'Sad' update.id %}">
<div class="w-fit h-fit rounded-full {% if update.last_customer_reaction == 'Sad' %} border-2 border-secondosiblue {% endif %}">
<img src="{% static 'images/icons/unhappy-icon.png' %}"
class="w-[30px] h-[30px] rounded-full cursor-pointer hover:scale-105 duration-300 transition-transform">
</div>
</a>
</div>
</div>
{% endif %}

@ -30,7 +30,7 @@
Status
</th>
<th scope="col" class="px-6 py-3 text-sm font-medium text-gray-500 uppercase whitespace-nowrap">
Date purchased
Date Created
</th>
</tr>
</thead>
@ -55,7 +55,7 @@
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300 bg-green-700">
<p class="text-white">{{order.status}}</p>
<p class="text-white">{{ order.orderstatus_set.last.status }}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">

@ -20,6 +20,7 @@ urlpatterns = [
# DETAILS
path('my-tickets/<int:ticket_number>/', views.customer_ticket_details, name='customerticketdetails'),
path('addticketupdatereaction/<str:reaction_type>/<int:ticketupdate_id>/', views.customer_add_ticket_update_reaction, name='customeraddticketupdatereaction'),
# PRODUCTS URL

@ -12,6 +12,7 @@ from billing.add.views import *
from .models import *
from django.db.models import Q
from django.http import Http404
from django.db.models import OuterRef, Subquery
# Create your views here.
@ -88,6 +89,7 @@ def customer_add_ticket(request, *args, **kwargs):
return render(request, 'add_templates/customer-add-ticket.html', context)
@customer_login_required
def customer_add_ticket_update(request, ticket_id):
ticket = get_object_or_404(Ticket, id=ticket_id)
@ -118,6 +120,38 @@ def customer_add_ticket_update(request, ticket_id):
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)
# LISTING
@customer_login_required
def customer_invoices(request, *args, **kwargs):
@ -144,7 +178,7 @@ def all_products(request, *args, **kwargs):
@customer_login_required
def customer_orders(request, *args, **kwargs):
customer = request.user.customerprofile
orders = Order.objects.filter(customer=customer, status='Completed').order_by('-order_id')
orders = Order.objects.filter(customer=customer, orderstatus__isnull=False).order_by('-order_id')
context = {
@ -211,7 +245,15 @@ def customer_ticket_details(request, ticket_number):
if ticket.customer != request.user.customerprofile:
raise Http404("Page not found.")
ticket_updates = TicketUpdate.objects.filter(ticket=ticket).order_by('id')
# Subquery to get the last reaction added by the logged-in customer for each ticket update
last_reaction_subquery = TicketUpdateReaction.objects.filter(
ticket_update=OuterRef('pk'),
customer=request.user
).order_by('-id').values('reaction')[:1]
ticket_updates = TicketUpdate.objects.filter(ticket=ticket).annotate(
last_customer_reaction=Subquery(last_reaction_subquery)
).order_by('id')
# Mark updates as read for the current user
for update in TicketUpdate.objects.filter(ticket=ticket).exclude(added_by=request.user).order_by('id'):
@ -233,6 +275,35 @@ def customer_ticket_details(request, ticket_number):
return render(request, 'details_templates/inner-customer-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)
# PRODUCTS
@ -357,14 +428,14 @@ def initiate_checkout(request):
price = cycle.cycle_price
existing_order = Order.objects.filter(customer=customer, status='None').first()
existing_order = Order.objects.filter(customer=customer, orderstatus__isnull=True).first()
if existing_order:
existing_order.orderitem_set.all().delete()
order = existing_order
else:
order = Order.objects.create(status='None', customer=customer)
order = Order.objects.create(customer=customer)
order.save()
order_id = order.order_id
@ -425,8 +496,11 @@ def check_order_status(request, merchant_id, order_id):
if response.status_code == 200:
order_details = response.json()
if order_details.get('result') == 'SUCCESS':
order.status = 'Completed'
order.save()
OrderStatus.objects.create(
order = order,
status = 'Completed',
date = datetime.now()
)
order_items = OrderItem.objects.filter(order=order)
for order_item in order_items:
order_item.purchased_at = datetime.now()
@ -452,7 +526,7 @@ def check_order_status(request, merchant_id, order_id):
}
response = requests.post(api_url, json=api_data)
order_item.active = True
old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSIMENU')
old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSIMENU', order__customer=request.user.customerprofile)
for item in old_order_items:
item.active = False
item.save()
@ -489,7 +563,12 @@ def buy_free_osimenu(request):
order = Order.objects.create(
customer = request.user.customerprofile,
status = 'Completed'
)
order_status = OrderStatus.objects.create(
order=order,
status = 'Completed',
date = datetime.now()
)
order_item = OrderItem.objects.create(
@ -499,7 +578,7 @@ def buy_free_osimenu(request):
active = True,
)
old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSIMENU')
old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSIMENU', order__customer=request.user.customerprofile)
for item in old_order_items:
item.active = False
item.save()
@ -532,7 +611,12 @@ def buy_free_osicard(request):
order = Order.objects.create(
customer = request.user.customerprofile,
status = 'Completed'
)
order_status = OrderStatus.objects.create(
order=order,
status = 'Completed',
date = datetime.now()
)
order_item = OrderItem.objects.create(
@ -542,7 +626,7 @@ def buy_free_osicard(request):
active = True,
)
old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSICARD')
old_order_items = OrderItem.objects.exclude(order__id=order.id).filter(item__item_type__name='OSICARD', order__customer=request.user.customerprofile)
for item in old_order_items:
item.active = False
item.save()

Binary file not shown.

Binary file not shown.

@ -31,5 +31,4 @@ urlpatterns = [
path('ticket/<int:customer_id>/', views.add_ticket, name='addticket'),
path('ticketupdate/<int:ticket_id>/', views.add_ticket_update, name='addticketupdate'),
path('add_payment_method_modal', views.add_payment_method_modal, name='add_payment_method_modal'),
]

@ -719,11 +719,17 @@ def add_ticket_update(request, ticket_id):
@staff_login_required
def add_payment_method_modal(request, *args, **kwargs):
if request.method == 'POST':
name = request.POST.get('name')
image = request.POST.get('method')
payment = Paymw(
name = name,
)
tag.save()
context = {
}
# Reload the parent page
return HttpResponse('<script>window.top.location.reload();</script>')
return render(request, 'add_templates/add-payment-method-modal.html', context)

@ -16,6 +16,5 @@ urlpatterns = [
path('taskpoint/<int:point_id>/<str:task_id>/', views.delete_task_point_modal, name='deletetaskpointmodal'),
path('note/<int:note_id>', views.delete_note_modal, name='deletenotemodal'),
path('payment/', views.delete_payment_modal, name='deletepaymentmodal'),
path('paymentmethod/', views.delete_payment_method_modal, name='deletepaymentmethodmodal')
]

@ -164,12 +164,3 @@ def delete_payment_modal(request):
return render(request, "delete_templates/delete-payment-modal.html", context)
@staff_login_required
def delete_payment_method_modal(request):
context = {
}
return render(request, "delete_templates/delete-payment-method-modal.html", context)

@ -17,7 +17,6 @@ urlpatterns = [
path('businesstype/<int:businesstype_id>/', views.edit_business_type, name='editbusinesstype'),
path('reference/<int:reference_id>/', views.edit_reference, name='editreference'),
path('tag/<int:tag_id>/', views.edit_tag, name='edittag'),
path('editpaymentmethod/', views.edit_payment_method, name='editpaymentmethod'),
#Mark Points

@ -378,18 +378,6 @@ def edit_tag(request, tag_id):
@staff_login_required
def edit_payment_method(request, *args, **kwargs):
context = {
}
return render(request, 'edit_templates/edit-payment-method.html', context)
#Mark points
@staff_login_required

Binary file not shown.

@ -272,7 +272,7 @@
</div>
</a>
<a href="{% url 'payment_methods' %}">
<a href="{% url 'paymentmethods' %}">
<div
class="w-full flex justify-start items-center gap-3 text-white border-b border-slate-600 py-2 cursor-pointer">
<p class="text-white">Payment Methods/p>
@ -564,7 +564,7 @@
</div>
</a>
<a href="{% url 'payment_methods' %}">
<a href="{% url 'paymentmethods' %}">
<div
class="w-full flex justify-start items-center gap-3 text-white border-b border-slate-600 py-2 cursor-pointer">
<p class="text-white">Payment Methods/p>

@ -425,7 +425,7 @@
</a>
<a href="{% url 'payment_methods' %}">
<a href="{% url 'paymentmethods' %}">
<div
class="w-full flex justify-start items-center gap-3 text-white border-b border-slate-600 py-2 cursor-pointer">
<p class="text-white">Payment Methods</p>
@ -726,7 +726,7 @@
</div>
</a>
<a href="{% url 'payment_methods' %}">
<a href="{% url 'paymentmethods' %}">
<div
class="w-full flex justify-start items-center gap-3 text-white border-b border-slate-600 py-2 cursor-pointer">
<p class="text-white">Payment Methods/p>

@ -52,7 +52,7 @@ urlpatterns = [
path('businesstypes/', views.business_types, name='businesstypes'),
path('references/', views.references, name='references'),
path('tags/', views.tags, name='tags'),
path('payment_methods/', views.payment_methods, name='payment_methods'),
#Details Templates

@ -107,7 +107,7 @@ def signup(request):
})
send_mail(mail_subject, message, settings.EMAIL_HOST_USER, [user.email], html_message=message)
return render(request, 'email-confirmation-sent.html', context)
return render(request, 'email-confirmation-sent.html')
else:
print('Form is not valid. Errors:')
@ -475,16 +475,6 @@ def tags(request):
@staff_login_required
def payment_methods(request):
context = {
}
return render(request, 'listing_pages/payment-methods.html', context)

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -67,7 +67,7 @@ document.addEventListener("DOMContentLoaded", function () {
addButtonClickListener("reactionDetailsButton", "400px", "300px");
addButtonClickListener("addPaymentCommentButton", "500px", "250px");
addButtonClickListener("addPaymentButton", "500px", "400px");
addButtonClickListener("addPaymentButton", "500px", "600px");
addButtonClickListener("editPaymentButton", "500px", "400px");
addButtonClickListener("addPaymentMethodButton", "500px", "400px");

Loading…
Cancel
Save