New changes.

main
nataly 12 months ago
parent b8d9bd29db
commit f80d2fecca

@ -6,6 +6,7 @@ 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-pdf/<int:order_id>/', views.add_invoice_pdf, name='addinvoice'),
path('payment-pdf/<int:order_id>/', views.add_payment_pdf, name='addpayment'),

@ -12,6 +12,7 @@ from weasyprint import HTML, CSS
@staff_login_required
def add_product(request, *args, **kwargs):
item_types = ProjectType.objects.all().order_by('name')
@ -272,6 +273,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)
@ -322,3 +327,10 @@ def add_payment_pdf(request, order_id):

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

@ -32,4 +32,15 @@ def edit_payment_modal(request):
}
return render(request, 'edit_templates/edit-payment-modal.html', context)
return render(request, 'edit_templates/edit-payment-modal.html', context)
@staff_login_required
def update_order_status(request):
context = {
}
return render(request, 'edit_templates/update-order-status-modal.html', context)

@ -0,0 +1,18 @@
# Generated by Django 5.0.4 on 2024-05-10 06:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('billing', '0051_alter_orderpayment_amount'),
]
operations = [
migrations.AlterField(
model_name='orderstatus',
name='status',
field=models.CharField(choices=[('Completed', 'Completed'), ('Pending', 'Pending'), ('Failed', 'Failed'), ('Cancelled', 'Cancelled'), ('In Progress', 'In Progress')], default='Pending', max_length=200),
),
]

@ -63,6 +63,7 @@ class OrderStatus(models.Model):
('Pending', 'Pending'),
('Failed', 'Failed'),
('Cancelled', 'Cancelled'),
('In Progress', 'In Progress')
)
order = models.ForeignKey(Order, on_delete=models.CASCADE)
status = models.CharField(max_length=200, choices=STATUS, default='Pending')

@ -8,7 +8,7 @@
<div class="w-full xxlg1:w-[75%]">
<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 xll:flex-row justify-center xll:justify-between items-center gap-3">
class="w-full bg-gray-50 px-3 py-3 border border-gray-100 shadow-md rounded-md flex flex-col md:flex-row justify-between items-center gap-3">
<div class="text-center xll:text-left">
<h1 class="text-secondosiblue text-xl">Order {{order.order_id}} - {{order.customer.user.first_name}}
{{order.customer.user.last_name}}</h1>
@ -18,40 +18,51 @@
<p>
<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>
<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>
<p class="text-secondosiblue font-poppinsBold">Remaining Balance: $<span>12.00</span></p>
</div>
<div class="w-full xll:w-fit flex flex-col s:flex-row justify-center xll:justify-end items-center gap-2">
<div
class="w-full bg-gray-50 px-3 py-3 border border-gray-100 shadow-md rounded-md flex flex-col md:flex-row justify-end items-center gap-2 mt-3">
<button data-modal-url="{% url 'add_payment_modal' order.id %}"
class="w-full md: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 addPaymentButton">
Add Payment
</button>
{% if not order.invoice %}
<a class="w-full md:w-fit">
<button
class="w-full md: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}}" class="w-full md:w-fit">
<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">
Add Payment
class="w-full md: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 %}
{% 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 data-modal-url="{% url 'update_order_status' %}"
class="w-full md: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 updateOrderStatusButton">
Update Status
</button>
<a href="{% url 'addinvoice' order.id %}" class="w-full md:w-fit">
<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
class="w-full md: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">
Order Statement
</button>
</div>
</a>
</div>
{% if order_items %}
@ -137,7 +148,7 @@
</button>
</div>
<div class="overflow-x-auto border border-gray-300 rounded-b-md">
<div class="overflow-x-auto border border-gray-300 rounded-t-md">
<table class="min-w-full divide-y">
<!-- TABLE HEADER -->
<thead class="bg-gray-50">
@ -179,9 +190,9 @@
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
{% if payment.date_due %}
<p class="text-secondosiblue">{{payment.date_due}}</p>
<p class="text-secondosiblue">{{payment.date_due}}</p>
{% else %}
<p class="text-secondosiblue">-</p>
<p class="text-secondosiblue">-</p>
{% endif %}
</td>

@ -0,0 +1,35 @@
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Osina</title>
<link rel="stylesheet" type="text/css" href='{% static "dist/output.css" %}'>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css">
</head>
<body class="font-poppinsLight">
<form id="hiddenContent" method="POST" action="">
{% csrf_token %}
<h1 class="text-secondosiblue text-2xl font-semibold text-center">Update Order Status</h1>
<select name="" id="" class="w-full h-[50px] py-1 px-3 border border-gray-300 outline-none rounded-md mt-4">
<option value="Completed">Completed</option>
<option value="Pending">Pending</option>
<option value="Failed">Failed</option>
<option value="Cancelled">Cancelled</option>
</select>
<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">Save</button>
</div>
</form>
</body>
</html>

@ -79,7 +79,7 @@
<td class="px-6 py-4">
<a href="{{invoice.pdf.url}}">
<a href="{{invoice.pdf.url}}" target="_blank">
<div class="w-full flex justify-center items-center">
<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.25m.75 12 3 3m0 0 3-3m-3 3v-6m-1.5-9H5.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" />

@ -64,7 +64,7 @@
<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.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 %}">
<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 {% elif order.orderstatus_set.all.last.status == 'Cancelled' %} bg-red-500 {% elif order.orderstatus_set.all.last.status == 'In Progress' %} bg-orange-500 {% else %} bg-gray-400 {% endif %}">
<p class="text-white">{{order.orderstatus_set.all.last.status}}</p>
</td>

@ -63,14 +63,11 @@ def order_details(request, 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')
payments = OrderPayment.objects.filter(order = order).order_by('-id')
context = {
'order' : order,
'order_items' : order_items,
'services': services,
'payments' : payments,
}

@ -0,0 +1,20 @@
from django.urls import path
from . import views
urlpatterns = [
path('whish-payment/<int:payment_id>/', views.whish_payment, name='whishpayment'),
path('cash-payment/<int:payment_id>/', views.cash_payment, name='cashpayment'),
path('checkout/<int:payment_id>/', views.card_payment, name='cardpayment'),
path('initiate-payment-checkout/', views.initiate_payment_checkout, name='initiatepaymentcheckout'),
path('buy-now/<int:item_id>/', views.buy_now, name='buynow'),
path('buy-now-checkout/', views.buy_now_checkout, name='buynowcheckout'),
path('check-order-status/<str:merchant_id>/<str:order_id>/', views.check_order_status, name='check_order_status'),
path('payment/osimenu-basic/', views.buy_free_osimenu, name='buyfreeosimenu'),
path('payment/osicard-basic/', views.buy_free_osicard, name='buyfreeosicard'),
]

@ -0,0 +1,354 @@
from django.shortcuts import render, get_object_or_404
from customercore.decorators import *
from customercore.models import *
from billing.add.views import *
from django.http import JsonResponse
import requests
import json
import base64
import json
import random
import string
def basic_auth_header(username, password):
credentials = f"merchant.{username}:{password}"
encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
return f"Basic {encoded_credentials}"
def whish_payment(request, payment_id):
payment = get_object_or_404(OrderPayment, id = payment_id)
context = {
'payment' : payment,
}
return render(request, 'payment_pages/whish-payment.html', context)
def cash_payment(request, payment_id):
payment = get_object_or_404(OrderPayment, id = payment_id)
context = {
'payment' : payment,
}
return render(request, 'payment_pages/cash-payment.html', context)
def card_payment(request, payment_id):
payment = get_object_or_404(OrderPayment, id = payment_id)
context = {
'payment' : payment,
}
return render(request, 'payment_pages/card-payment.html', context)
@customer_login_required
def initiate_payment_checkout(request):
api_username = 'merchant.TEST06127800'
api_password = '37846250a67c70e7fe9f82cf6ca81f93'
merchant_id = 'TEST06127800'
merchant_name = 'Ositcom Sal'
customer = request.user.customerprofile
data = json.loads(request.body)
payment_id = data.get('payment_id')
payment = get_object_or_404(OrderPayment, id=payment_id)
order = payment.order
order_id = order.order_id
price = order.get_cart_total
payload = {
'apiOperation': 'INITIATE_CHECKOUT',
'apiUsername': api_username,
'apiPassword': api_password,
'merchant': merchant_id,
'interaction.operation': 'PURCHASE',
'interaction.merchant.name': merchant_name,
'order.id': order_id,
'order.amount': price,
'order.currency': 'USD',
'order.description': 'description_of_order',
'order.notificationUrl' : 'https://newosina.osinode.com/webhooks/',
'interaction.returnUrl' : f"https://newosina.osinode.com/check-order-status/{merchant_id}/{order_id}/"
}
try:
response = requests.post('https://creditlibanais-netcommerce.gateway.mastercard.com/api/nvp/version/78', data=payload)
print('Response Content:', response.text)
if response.status_code == 200:
response_data = response.text
parsed_data = dict(item.split('=') for item in response_data.split('&'))
session_id = parsed_data.get('session.id')
return JsonResponse({'session_id': session_id}, status=200)
else:
print('Response Status Code:', response.status_code)
return JsonResponse({'error': 'Failed to initiate checkout'}, status=500)
except Exception as e:
print('Exception:', e)
return JsonResponse({'error': 'Internal Server Error'}, status=500)
@customer_login_required
def buy_now(request, item_id):
item = get_object_or_404(Item, id=item_id)
cycles = RecurringCycle.objects.filter(item = item)
selected_cycle = None
context = {
'item' : item,
'cycles' : cycles,
'selected_cycle': selected_cycle,
}
return render(request, 'payment_pages/buy-now.html', context)
@customer_login_required
def buy_now_checkout(request):
api_username = 'merchant.TEST06127800'
api_password = '37846250a67c70e7fe9f82cf6ca81f93'
merchant_id = 'TEST06127800'
merchant_name = 'Ositcom Sal'
customer = request.user.customerprofile
data = json.loads(request.body)
item_id = data.get('item_id')
cycle_id = data.get('cycle_id')
item = Item.objects.get(id=item_id)
cycle = RecurringCycle.objects.get(id = cycle_id)
price = cycle.cycle_price
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(customer=customer)
order.save()
order_id = order.order_id
order_item = OrderItem.objects.create(order=order, item=item, end_at = datetime.now() + timedelta(days=(cycle.months * 30)))
order_item.save()
payload = {
'apiOperation': 'INITIATE_CHECKOUT',
'apiUsername': api_username,
'apiPassword': api_password,
'merchant': merchant_id,
'interaction.operation': 'PURCHASE',
'interaction.merchant.name': merchant_name,
'order.id': order_id,
'order.amount': price,
'order.currency': 'USD',
'order.description': 'description_of_order',
'order.notificationUrl' : 'https://newosina.osinode.com/webhooks/',
'interaction.returnUrl' : f"https://osina.ositcom.com/check-order-status/{merchant_id}/{order_id}/"
}
try:
response = requests.post('https://creditlibanais-netcommerce.gateway.mastercard.com/api/nvp/version/78', data=payload)
print('Response Content:', response.text)
if response.status_code == 200:
response_data = response.text
parsed_data = dict(item.split('=') for item in response_data.split('&'))
session_id = parsed_data.get('session.id')
return JsonResponse({'session_id': session_id}, status=200)
else:
print('Response Status Code:', response.status_code)
return JsonResponse({'error': 'Failed to initiate checkout'}, status=500)
except Exception as e:
print('Exception:', e)
return JsonResponse({'error': 'Internal Server Error'}, status=500)
def check_order_status(request, merchant_id, order_id):
api_password = '37846250a67c70e7fe9f82cf6ca81f93'
url = f"https://creditlibanais-netcommerce.gateway.mastercard.com/api/rest/version/78/merchant/{merchant_id}/order/{order_id}"
order = Order.objects.get(order_id=order_id)
headers = {
'Content-Type': 'application/json',
'Authorization': basic_auth_header(merchant_id, api_password)
}
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
order_details = response.json()
if order_details.get('result') == 'SUCCESS':
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()
if order_item.item.item_type.name == 'OSIMENU':
api_url = 'https://osimenu.com/api/create-subscription/'
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
end_date_str = order_item.end_at.strftime('%Y-%m-%d')
api_data = {
'user': {
'username': request.user.email,
'email': request.user.email,
'password': random_string,
'first_name': request.user.first_name,
'last_name': request.user.last_name
},
'customer': {
'mobile_number': request.user.customerprofile.mobile_number
},
'subscription': {
'plan': order_item.item.title,
'end_date': end_date_str
}
}
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', order__customer=request.user.customerprofile)
for item in old_order_items:
item.active = False
item.save()
order_item.save()
add_invoice_pdf(request, order_id=order.id)
return redirect('customerorders')
error_message = 'Failed to retrieve order details: ' + response.text
return JsonResponse({'error': error_message}, status=500)
except Exception as e:
error_message = 'Exception: ' + str(e)
return JsonResponse({'error': error_message}, status=500)
def buy_free_osimenu(request):
api_url = 'https://osimenu.com/api/create-subscription/'
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
api_data = {
'user': {
'username': request.user.email,
'email': request.user.email,
'password': random_string,
'first_name': request.user.first_name,
'last_name': request.user.last_name
},
'customer': {
'mobile_number': request.user.customerprofile.mobile_number
},
'subscription': {
'plan': 'OSIMENU BASIC'
}
}
order = Order.objects.create(
customer = request.user.customerprofile,
)
order_status = OrderStatus.objects.create(
order=order,
status = 'Completed',
date = datetime.now()
)
order_item = OrderItem.objects.create(
order = order,
item = get_object_or_404(Item, title='OSIMENU BASIC'),
purchased_at = datetime.now(),
active = True,
)
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()
response = requests.post(api_url, json=api_data)
print(response)
return redirect('customerorders')
def buy_free_osicard(request):
api_url = 'https://mybusinesscardpage.com/api/create-subscription/'
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
api_data = {
'user': {
'username': request.user.email,
'email': request.user.email,
'password': random_string,
'first_name': request.user.first_name,
'last_name': request.user.last_name
},
'customer': {
'mobile_number': request.user.customerprofile.mobile_number
},
'subscription': {
'plan': 'OSICARD BASIC'
}
}
order = Order.objects.create(
customer = request.user.customerprofile,
)
order_status = OrderStatus.objects.create(
order=order,
status = 'Completed',
date = datetime.now()
)
order_item = OrderItem.objects.create(
order = order,
item = get_object_or_404(Item, title='OSICARD BASIC'),
purchased_at = datetime.now(),
active = True,
)
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()
response = requests.post(api_url, json=api_data)
print(response)
return redirect('customerorders')

@ -0,0 +1,151 @@
{% extends "customer_main.html" %}
{%load static%}
{% block content %}
<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 bg-gray-50 px-3 py-3 border border-gray-100 shadow-md rounded-md flex flex-col md:flex-row justify-between items-center gap-3">
<div class="text-center xll:text-left">
<h1 class="text-secondosiblue text-xl">Order {{order.order_id}}</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.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>
<p class="text-secondosiblue font-poppinsBold">Remaining Balance: $<span>12.00</span></p>
</div>
<div
class="w-full bg-gray-50 px-3 py-3 border border-gray-100 shadow-md rounded-md flex flex-col md:flex-row justify-end items-center gap-2 mt-3">
{% if order.invoice %}
<a class="w-full md:w-fit">
<button
class="w-full md: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 %}
<a class="w-full md:w-fit">
<button
class="w-full md: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 Statement
</button>
</a>
</div>
{% 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">
{% for item in order_items %}
<div class="w-full shadow-md border border-gray-200 flex flex-col justify-between rounded-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-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>
</div>
{% endfor %}
</div>
</div>
{% endif %}
<div class="overflow-x-auto border border-gray-300 rounded-t-md mt-10">
<table class="min-w-full divide-y">
<!-- TABLE HEADER -->
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Amount
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap ">
Date Due
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Date Paid
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Comment
</th>
<th scope="col" class="px-6 py-3 text-sm font-medium text-gray-500 uppercase whitespace-nowrap">
Payment
</th>
</tr>
</thead>
<!-- TABLE BODY -->
<tbody class="bg-white divide-y divide-gray-200">
{% for payment in payments %}
<tr>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">${{payment.amount}}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
{% 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">Comment</p>
</td>
<td class="px-6 py-4 text-center text-sm">
<div class="flex flex-col justify-center items-center gap-3">
{% for type in payment.type.all %}
<a
{% if type.name == 'Whish Money' %} href="{% url 'whishpayment' payment.id %}" {% endif %}
{% if type.name == 'Credit & Debit Card' %} href="{% url 'cardpayment' payment.id %}" {% endif %}
{% if type.name == 'Cash' %} href="{% url 'cashpayment' payment.id %}" {% endif %}>
<div
class="flex items-center gap-2 justify-center cursor-pointer text-secondosiblue hover:text-gray-400 duration-300">
<img src="{{type.image.url}}" class="w-[30px] rounded-md shadow-sm">
<p class="underline">{{type.name}}</p>
</div>
</a>
{% endfor %}
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endblock %}

@ -0,0 +1,274 @@
{% extends "customer_main.html" %}
{%load static%}
{% block content %}
<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 bg-orange-500 rounded-md flex flex-col justify-center items-center py-2 gap-2 mt-[50px]">
<div
class="w-[70px] s:w-[90px] h-[70px] s:h-[90px] rounded-full mt-[-50px] s:mt-[-63px] border border-gray-300 bg-white">
<img src="{% static 'images/ositcom_logos/full-logo.png' %}" alt="" class="w-full h-full object-cover rounded-full">
</div>
<div class="text-center">
<p class="text-xl text-white font-semibold">Winabig</p>
<p class="text-sm text-white font-light">P20302</p>
</div>
</div>
<div class="w-full flex flex-col gap-2 mt-3">
<div class="w-full rounded-md bg-white shadow-md h-[8px]">
<div class="w-[80%] h-full bg-green-700 rounded-md">
</div>
</div>
<div class="w-full flex justify-between items-center gap-3 text-gray-500 font-light text-sm">
<p>20-2-2023</p>
<p>20-2-2024</p>
</div>
</div>
<div class="w-full h-fit flex justify-end items-center bg-gray-100 shadow-md rounded-md px-3 py-3 mt-3">
<div class="w-full s:w-fit flex flex-col s:flex-row justify-end items-center gap-3">
<a href="" class="w-full s:w-fit">
<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">Add
Ticket</button>
</a>
<a href="" class="w-full s:w-fit">
<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">Add
User Story</button>
</a>
<a href="" class="w-full s:w-fit">
<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">Add
Credentials</button>
</a>
<a href="" class="w-full s:w-fit">
<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">Add
File</button>
</a>
</div>
</div>
<div class="w-full bg-gray-50 rounded-md shadow-md border border-gray-100 p-5 mt-10">
<p class="text-secondosiblue text-[17px] font-poppinsBold">Project Manager</p>
<div
class="w-[240px] px-5 py-2 bg-white shadow-md mt-3 rounded-md flex justify-start items-center gap-2 cursor-pointer">
<div class="w-[50px] h-[50px] rounded-full">
<img src="{% static 'images/default-user.png' %}"
class="w-full h-full object-cover shadow-md rounded-full">
</div>
<p class="text-gray-500 text-[16px]">Emile Elliye</p>
</div>
</div>
<div class="w-full bg-gray-50 rounded-md shadow-md border border-gray-100 p-5 mt-10">
<p class="text-secondosiblue text-[17px] font-poppinsBold">Project Details</p>
<p class="text-gray-500 font-light leading-7">There are many variations of passages of Lorem Ipsum
available, but the majority have suffered alteration
in some form, by injected humour, or randomised words which don't look even slightly believable. If you
are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden
in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks
as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200
Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks
reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or
non-characteristic words etc.</p>
</div>
<!-- USER STORIES -->
<div class="w-full mt-10">
<div class="overflow-x-auto border border-gray-300 rounded-md mt-5">
<div
class=" bg-gray-200 rounded-t-md flex justify-between items-center text-white text-xl font-bold h-[50px]">
<div class="px-3">
<p class="text-secondosiblue uppercase font-bold">User Stories</p>
</div>
<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 addUserStoryButton"
data-modal-url="">
<i class="fa fa-plus"></i>
</button>
</div>
<table class="min-w-full divide-y">
<!-- TABLE HEADER -->
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Story
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Related Task
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase whitespace-nowrap">
Actions
</th>
</tr>
</thead>
<!-- TABLE BODY -->
<tbody class="bg-white divide-y divide-gray-200">
<tr>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">requirements</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
dddd
</td>
<td class="px-6 py-4 text-center text-sm">
<div class="w-full flex justify-center items-center gap-3">
<a href="">
<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>
</a>
<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>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- CREDENTIALS -->
<div class="mt-10 relative">
<div
class=" bg-gray-200 rounded-t-md flex justify-between items-center text-white text-xl font-bold h-[50px]">
<div class="px-3">
<p class="text-secondosiblue uppercase font-bold">Credentials</p>
</div>
<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 addCredentialsButton"
data-modal-url="">
<i class="fa fa-plus"></i>
</button>
</div>
<div class="overflow-x-auto border border-gray-300 rounded-b-md">
<table class="min-w-full divide-y">
<!-- TABLE HEADER -->
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Email or Username
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Password
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase whitespace-nowrap">
Used For
</th>
</tr>
</thead>
<!-- TABLE BODY -->
<tbody class="bg-white divide-y divide-gray-200">
<!-- 1st row -->
<tr>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">{{credential.emailorusername}}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<a class="text-secondosiblue">{{credential.password}}</a>
</td>
<td class="px-6 py-4 text-center text-sm">
<a class="text-secondosiblue">{{credential.usedfor}}</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<!-- RELATED FILES -->
<div class="w-full mt-10">
<div class="overflow-x-auto border border-gray-300 rounded-md mt-5">
<div
class=" bg-gray-200 rounded-t-md flex justify-between items-center text-white text-xl font-bold h-[50px]">
<div class="px-3">
<p class="text-secondosiblue uppercase font-bold">Related files</p>
</div>
<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 addFileButton"
data-modal-url="">
<i class="fa fa-plus"></i>
</button>
</div>
<table class="min-w-full divide-y">
<!-- TABLE HEADER -->
<thead class="bg-gray-50">
<tr>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
File Name
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
File
</th>
<th scope="col"
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase whitespace-nowrap">
Date Entered
</th>
</tr>
</thead>
<!-- TABLE BODY -->
<tbody class="bg-white divide-y divide-gray-200">
<!-- 1st row -->
<tr>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<p class="text-secondosiblue">{{file.name}}</p>
</td>
<td class="px-6 py-4 text-center text-sm border-r border-gray-300">
<a class="text-secondosiblue">{{file.file}}</a>
</td>
<td class="px-6 py-4 text-center text-sm">
<a class="text-secondosiblue">{{file.date}}</a>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
{% endblock content %}

@ -70,7 +70,7 @@
<td class="px-6 py-4">
<div class="w-full flex justify-center items-center gap-3">
<a href="{{invoice.pdf.url}}">
<a href="{{invoice.pdf.url}}" target="_blank">
<button
class="w-fit px-3 py-2 rounded-md bg-gray-100 border border-gray-100 shadow-md cursor-pointer hover:scale-105 duration-300 transition-transform">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
@ -81,15 +81,12 @@
</button>
</a>
{% if not invoice.order.status == 'Completed' %}
<a href="{% url 'invoicepayment' invoice.id %}">
<a href="{% url 'customerorderdetails' invoice.order.id %}">
<button
class="flex justify-center items-center gap-2 px-3 py-1 rounded-md bg-osiblue text-white border border-osiblue cursor-pointer hover:bg-white hover:text-osiblue duration-300">
Pay Now
</button>
</a>
{% endif %}
</div>
</td>
</tr>

@ -29,9 +29,12 @@
class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Status
</th>
<th scope="col" class="px-6 py-3 text-sm font-medium text-gray-500 uppercase whitespace-nowrap">
<th scope="col" class="px-6 py-3 text-sm font-medium text-gray-500 uppercase border-r border-gray-300 whitespace-nowrap">
Date Created
</th>
<th scope="col" class="px-6 py-3 text-sm font-medium text-gray-500 uppercase whitespace-nowrap">
Actions
</th>
</tr>
</thead>
@ -54,13 +57,24 @@
<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 bg-green-700">
<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 {% elif order.orderstatus_set.all.last.status == 'Cancelled' %} bg-red-500 {% elif order.orderstatus_set.all.last.status == 'In Progress' %} bg-orange-500 {% else %} bg-gray-400 {% endif %}">
<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">
<p class="text-secondosiblue">{{order.get_purchased_date}}</p>
</td>
<td class="px-6 py-4 text-center text-sm">
<div class="w-full flex justify-center items-center gap-3">
<a href="{% url 'customerorderdetails' 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="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" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>

@ -0,0 +1,89 @@
{% extends "customer_main.html" %}
{%load static%}
{% block content %}
<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-[25px]">My Projects</h1>
<div class="w-full grid grid-cols-1 l:grid-cols-2 gap-5 mt-4">
<a href="{% url 'customerprojectdetails' %}">
<div
class="w-full rounded-md flex flex-col justify-between items-center gap-3 px-5 py-9 h-[250px] bg-gray-50 shadow-md relative hover:bg-gray-100 duration-300">
<div class="flex flex-col justify-center items-center gap-5">
<div
class="w-[70px] h-[70px] rounded-full shadow-md">
<img src="{% static 'images/ositcom_logos/full-logo.png' %}" class="w-full h-full rounded-full object-cover">
</div>
<div>
<p class="text-secondosiblue font-poppinsBold text-center text-xl">Geologist Safwat</p>
<p class="text-sm uppercase font-light text-secondosiblue text-center">P240022</p>
</div>
</div>
<div
class="bg-orange-500 px-3 py-2 rounded-r-md text-xs text-white font-light uppercase absolute top-5 left-0">
<p>Active</p>
</div>
<div class="w-full mt-5 flex flex-col gap-2">
<div class="w-full flex justify-between items-center gap-3 text-gray-500 font-light text-xs">
<p>20-2-2023</p>
<p>20-2-2024</p>
</div>
<div class="w-full rounded-md bg-white shadow-md h-[8px]">
<div class="w-[80%] h-full bg-green-700 rounded-md">
</div>
</div>
</div>
</div>
</a>
<a href="{% url 'customerprojectdetails' %}">
<div
class="w-full rounded-md flex flex-col justify-between items-center gap-3 px-5 py-9 h-[250px] bg-gray-50 shadow-md relative hover:bg-gray-100 duration-300">
<div class="flex flex-col justify-center items-center gap-5">
<div
class="w-[70px] h-[70px] rounded-full shadow-md">
<img src="{% static 'images/ositcom_logos/full-logo.png' %}" class="w-full h-full rounded-full object-cover">
</div>
<div>
<p class="text-secondosiblue font-poppinsBold text-center text-xl">Geologist Safwat</p>
<p class="text-sm uppercase font-light text-secondosiblue text-center">P240022</p>
</div>
</div>
<div
class="bg-yellow-500 px-3 py-2 rounded-r-md text-xs text-white font-light uppercase absolute top-5 left-0">
<p>Pending</p>
</div>
<div class="w-full mt-5 flex flex-col gap-2">
<div class="w-full flex justify-between items-center gap-3 text-gray-500 font-light text-xs">
<p>May,2,2023</p>
<p>June,30,2023</p>
</div>
<div class="w-full rounded-md bg-white shadow-md h-[8px]">
<div class="w-[90%] h-full bg-red-500 rounded-md">
</div>
</div>
</div>
</div>
</a>
</div>
</div>
</div>
{% endblock content %}

@ -91,10 +91,10 @@
<!-- TO CALL THE INITIATE CHECKOUT PAGE -->
<script type="text/javascript" src='{% static "js/customer_dashboard/payment.js" %}'></script>
<script type="text/javascript" src='{% static "js/customer_dashboard/payment/buy-now.js" %}'></script>
<!-- TO CHANGE THE PRICE BASED ON THE CHOSEN CYCLE FROM THE SELECT TAG -->
<script type="text/javascript" src='{% static "js/customer_dashboard/payment-pricing.js" %}'></script>
<script type="text/javascript" src='{% static "js/customer_dashboard/payment/payment-pricing.js" %}'></script>
{% endblock %}

@ -9,7 +9,7 @@
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<p id="invoiceId" class="hidden">{{invoice.id}}</p>
<p id="paymentId" class="hidden">{{payment.id}}</p>
<div class="w-full px-5 s:px-9 flex flex-col gap-5 mt-5 font-poppinsLight">
@ -38,15 +38,14 @@
<img src="{% static 'images/ositcom_logos/full-logo.png' %}" class="w-[120px]">
<div class="w-full bg-osiblue py-3 px-3 flex justify-between items-center text-white mt-5">
<p>Due Date: <span class="font-semibold">{{invoice.order.due_date}}</span></p>
<p>Invoice <span class="font-semibold">{{invoice.invoice_number}}</span></p>
<p>Due Date: <span class="font-semibold">{{payment.date_due}}</span></p>
</div>
<div class="w-full p-5">
<p class="font-poppinsBold text-secondosiblue text-[17px]">Bill To:</p>
<p class="text-secondosiblue font-light">{{invoice.order.customer.user.first_name}} {{invoice.order.customer.user.last_name}}</p>
<p class="text-secondosiblue font-light">{{invoice.order.customer.mobile_number}}</p>
<p class="text-secondosiblue font-light">{{invoice.order.customer.user.email}}</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.user.first_name}} {{payment.order.customer.user.last_name}}</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.mobile_number}}</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.user.email}}</p>
</div>
<div class="w-full bg-osiblue py-3 px-3 flex justify-start items-center text-white">
@ -56,13 +55,13 @@
<div class="w-full flex flex-col gap-5 p-5">
<ul class="text-secondosiblue font-light list-disc px-5">
{% for item in invoice.order.orderitem_set.all %}
{% for item in payment.order.orderitem_set.all %}
<li>{{item.item.title}}</li>
{% endfor %}
</ul>
<div class="w-full flex justify-end items-center text-secondosiblue text-xl">
<p>Total: <span class="font-semibold">${{invoice.order.get_cart_total}}</span></p>
<p>Total: <span class="font-semibold">${{payment.order.get_cart_total}}</span></p>
</div>
</div>
</div>
@ -112,6 +111,6 @@
<!-- TO CALL THE INITIATE CHECKOUT PAGE -->
<script type="text/javascript" src='{% static "js/customer_dashboard/invoice-payment.js" %}'></script>
<script type="text/javascript" src='{% static "js/customer_dashboard/payment/payment.js" %}'></script>
{% endblock %}

@ -0,0 +1,78 @@
{% extends "customer_main.html" %}
{%load static%}
{% block modules_section %}
<!-- This block is intentionally left empty to exclude the modules section -->
{% endblock modules_section %}
{% block content %}
<div class="w-full px-5 s:px-9 flex flex-col gap-5 mt-5 font-poppinsLight">
<div class="w-full h-fit bg-white rounded-md shadow-md">
<div class="grid grid-cols-1 xxlg1:grid-cols-2">
<div
class="w-full pb-9 flex flex-col items-center justify-between rounded-lb-none xxlg1:rounded-l-md h-full rounded-tl-md rounded-tr-md xxlg1:rounded-tr-none gap-10 overflow-hidden relative bg-gradient-to-b from-gray-400 via-gray-500 to-blue-950">
<div
class="absolute top-0 left-32 w-[400px] h-[400px] bg-slate-800 filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div
class="absolute top-20 right-0 w-[400px] h-[400px] bg-osiblue filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div
class="absolute bottom-0 left-10 w-[400px] h-[400px] bg-blue-900 filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div
class="absolute bottom-0 right-36 w-[400px] h-[400px] bg-gray-400 filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div></div>
<!-- INVOICE DETAILS -->
<div class="w-[90%] s:w-[85%] xll:w-[75%] h-fit flex flex-col gap-5 justify-center items-center py-9 bg-white rounded-md z-10 shadow-md duration-300">
<img src="{% static 'images/ositcom_logos/full-logo.png' %}" class="w-[120px]">
<div class="w-full bg-osiblue py-3 px-3 flex justify-between items-center text-white mt-5">
<p>Due Date: <span class="font-semibold">{{payment.date_due}}</span></p>
</div>
<div class="w-full p-5">
<p class="font-poppinsBold text-secondosiblue text-[17px]">Bill To:</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.user.first_name}} {{payment.order.customer.user.last_name}}</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.mobile_number}}</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.user.email}}</p>
</div>
<div class="w-full bg-osiblue py-3 px-3 flex justify-start items-center text-white">
<p>Invoice Items</p>
</div>
<div class="w-full flex flex-col gap-5 p-5">
<ul class="text-secondosiblue font-light list-disc px-5">
{% for item in payment.order.orderitem_set.all %}
<li>{{item.item.title}}</li>
{% endfor %}
</ul>
<div class="w-full flex justify-end items-center text-secondosiblue text-xl">
<p>Total: <span class="font-semibold">${{payment.order.get_cart_total}}</span></p>
</div>
</div>
</div>
</div>
<!-- CASH PAYMENT CONTAINER -->
<div class="w-full flex flex-col justify-between items-center gap-5">
</div>
</div>
</div>
</div>
{% endblock %}

@ -0,0 +1,88 @@
{% extends "customer_main.html" %}
{%load static%}
{% block modules_section %}
<!-- This block is intentionally left empty to exclude the modules section -->
{% endblock modules_section %}
{% block content %}
<div class="w-full px-5 s:px-9 flex flex-col gap-5 mt-5 font-poppinsLight">
<div class="w-full h-fit bg-white rounded-md shadow-md">
<div class="grid grid-cols-1 xxlg1:grid-cols-2">
<div
class="w-full pb-9 flex flex-col items-center justify-between rounded-lb-none xxlg1:rounded-l-md h-full rounded-tl-md rounded-tr-md xxlg1:rounded-tr-none gap-10 overflow-hidden relative bg-gradient-to-b from-gray-400 via-gray-500 to-blue-950">
<div
class="absolute top-0 left-32 w-[400px] h-[400px] bg-slate-800 filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div
class="absolute top-20 right-0 w-[400px] h-[400px] bg-osiblue filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div
class="absolute bottom-0 left-10 w-[400px] h-[400px] bg-blue-900 filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div
class="absolute bottom-0 right-36 w-[400px] h-[400px] bg-gray-400 filter blur-xl opacity-70 animate-blob animation-delay-2000">
</div>
<div></div>
<!-- INVOICE DETAILS -->
<div class="w-[90%] s:w-[85%] xll:w-[75%] h-fit flex flex-col gap-5 justify-center items-center py-9 bg-white rounded-md z-10 shadow-md duration-300">
<img src="{% static 'images/ositcom_logos/full-logo.png' %}" class="w-[120px]">
<div class="w-full bg-osiblue py-3 px-3 flex justify-between items-center text-white mt-5">
<p>Due Date: <span class="font-semibold">{{payment.date_due}}</span></p>
</div>
<div class="w-full p-5">
<p class="font-poppinsBold text-secondosiblue text-[17px]">Bill To:</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.user.first_name}} {{payment.order.customer.user.last_name}}</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.mobile_number}}</p>
<p class="text-secondosiblue font-light">{{payment.order.customer.user.email}}</p>
</div>
<div class="w-full bg-osiblue py-3 px-3 flex justify-start items-center text-white">
<p>Invoice Items</p>
</div>
<div class="w-full flex flex-col gap-5 p-5">
<ul class="text-secondosiblue font-light list-disc px-5">
{% for item in payment.order.orderitem_set.all %}
<li>{{item.item.title}}</li>
{% endfor %}
</ul>
<div class="w-full flex justify-end items-center text-secondosiblue text-xl">
<p>Total: <span class="font-semibold">${{payment.order.get_cart_total}}</span></p>
</div>
</div>
</div>
</div>
<!-- WHISH CONTAINER -->
<div class="w-full flex flex-col justify-between items-center gap-5">
<div class="w-full flex flex-col justify-center items-center gap-8 p-9">
<p class="text-center text-secondosiblue font-poppinsBold text-xl s:text-2xl">Scan this QR on Your Whish Mobile Application<br> to Proceed Payment</p>
<img src="{% static 'images/whish-qr.jpeg' %}" class="w-[300px]">
</div>
<div class="w-full flex justify-center items-center gap-2 border-t border-gray-100 pt-5 py-5 px-9">
<img src="{% static 'images/whishlogo.png' %}" class="w-[35px] rounded-md">
<p class="text-secondosiblue text-sm font-light">Payment By Whish Money</p>
</div>
</div>
</div>
</div>
</div>
{% endblock %}

@ -100,7 +100,7 @@
</div>
{% if not active_order_item_standard %}
<a href="{% url 'payment' osimenu_standard.id %}">
<a href="{% url 'buynow' 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
@ -174,7 +174,7 @@
</div>
{% if not active_order_item_premium %}
<a href="{% url 'payment' osimenu_premium.id %}">
<a href="{% url 'buynow' 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
@ -250,7 +250,7 @@
<p class="text-gray-500">monthly/restaurant</p>
</div>
<a href="{% url 'payment' osimenu_basic.id %}">
<a href="{% url 'buynow' osimenu_basic.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
@ -314,7 +314,7 @@
<p class="text-gray-500">monthly/restaurant</p>
</div>
<a href="{% url 'payment' osimenu_standard.id %}">
<a href="{% url 'buynow' 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
@ -374,7 +374,7 @@
<p class="text-gray-500">monthly/restaurant</p>
</div>
<a href="{% url 'payment' osimenu_premium.id %}">
<a href="{% url 'buynow' 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

@ -1,8 +1,10 @@
from django.urls import path
from django.urls import path, include
from . import views
urlpatterns = [
path('payment/', include('customercore.payment.urls')),
path('redirect-osimenu/', views.redirect_osimenu, name='redirectosimenu'),
path('redirect-osicard/', views.redirect_osicard, name='redirectosicard'),
@ -16,11 +18,14 @@ urlpatterns = [
path('products/', views.all_products, name='products'),
path('my-orders/', views.customer_orders, name='customerorders'),
path('my-tickets/', views.customer_tickets, name='customertickets'),
path('my-projects/', views.customer_projects, name='customerprojects'),
# 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'),
path('my-orders/<int:order_id>/', views.customer_order_details, name='customerorderdetails'),
path('my-projects-details/', views.customer_project_details, name='customerprojectdetails'),
# PRODUCTS URL
@ -29,15 +34,4 @@ urlpatterns = [
path('shared-hosting-plans/', views.shared_hosting_plans, name='sharedhostingplans'),
path('cloud-vps-hosting-plans/', views.cloud_vps_hosting_plans, name='cloudvpshostingplans'),
path('dedicated-servers-plans/', views.dedicated_servers_plans, name='dedicatedserversplans'),
# PAYMENT
path('payment/<int:item_id>/', views.payment, name='payment'),
path('initiate_checkout/', views.initiate_checkout, name='initiatecheckout'),
path('check-order-status/<str:merchant_id>/<str:order_id>/', views.check_order_status, name='check_order_status'),
path('payment/osimenu-basic/', views.buy_free_osimenu, name='buyfreeosimenu'),
path('payment/osicard-basic/', views.buy_free_osicard, name='buyfreeosicard'),
path('invoice-payment/<int:invoice_id>/', views.invoice_payment, name='invoicepayment'),
path('initiate_invoice_checkout/', views.initiate_invoice_checkout, name='initiateinvoicecheckout'),
]

@ -1,13 +1,6 @@
from django.shortcuts import render, get_object_or_404
from django.contrib.auth.decorators import login_required
from django.http import JsonResponse
import requests
from .decorators import *
from billing.models import *
import base64
import json
import random
import string
from billing.add.views import *
from .models import *
from django.db.models import Q
@ -188,6 +181,25 @@ def customer_orders(request, *args, **kwargs):
return render(request, 'listing_pages/customer-orders.html', context)
def customer_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')
payments = OrderPayment.objects.filter(order = order).order_by('-id')
context = {
'order' : order,
'order_items' : order_items,
'services': services,
'payments' : payments,
}
return render(request, 'details_templates/inner-customer-order.html', context)
@customer_login_required
@ -374,343 +386,24 @@ def dedicated_servers_plans(request, *args, **kwargs):
return render(request, 'products/dedicated-servers-plans.html', context)
#Products Payment views
@customer_login_required
def payment(request, item_id):
item = get_object_or_404(Item, id=item_id)
cycles = RecurringCycle.objects.filter(item = item)
def customer_projects(request, *args, **kwargs):
selected_cycle = None
context = {
'item' : item,
'cycles' : cycles,
'selected_cycle': selected_cycle,
}
return render(request, 'payment.html', context)
def basic_auth_header(username, password):
credentials = f"merchant.{username}:{password}"
encoded_credentials = base64.b64encode(credentials.encode('utf-8')).decode('utf-8')
return f"Basic {encoded_credentials}"
@customer_login_required
def initiate_checkout(request):
api_username = 'merchant.TEST06127800'
api_password = '37846250a67c70e7fe9f82cf6ca81f93'
merchant_id = 'TEST06127800'
merchant_name = 'Ositcom Sal'
customer = request.user.customerprofile
data = json.loads(request.body)
item_id = data.get('item_id')
cycle_id = data.get('cycle_id')
item = Item.objects.get(id=item_id)
cycle = RecurringCycle.objects.get(id = cycle_id)
price = cycle.cycle_price
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(customer=customer)
order.save()
order_id = order.order_id
order_item = OrderItem.objects.create(order=order, item=item, end_at = datetime.now() + timedelta(days=(cycle.months * 30)))
order_item.save()
payload = {
'apiOperation': 'INITIATE_CHECKOUT',
'apiUsername': api_username,
'apiPassword': api_password,
'merchant': merchant_id,
'interaction.operation': 'PURCHASE',
'interaction.merchant.name': merchant_name,
'order.id': order_id,
'order.amount': price,
'order.currency': 'USD',
'order.description': 'description_of_order',
'order.notificationUrl' : 'https://newosina.osinode.com/webhooks/',
'interaction.returnUrl' : f"https://osina.ositcom.com/check-order-status/{merchant_id}/{order_id}/"
}
try:
response = requests.post('https://creditlibanais-netcommerce.gateway.mastercard.com/api/nvp/version/78', data=payload)
print('Response Content:', response.text)
if response.status_code == 200:
response_data = response.text
parsed_data = dict(item.split('=') for item in response_data.split('&'))
session_id = parsed_data.get('session.id')
return JsonResponse({'session_id': session_id}, status=200)
else:
print('Response Status Code:', response.status_code)
return JsonResponse({'error': 'Failed to initiate checkout'}, status=500)
except Exception as e:
print('Exception:', e)
return JsonResponse({'error': 'Internal Server Error'}, status=500)
def check_order_status(request, merchant_id, order_id):
api_password = '37846250a67c70e7fe9f82cf6ca81f93'
url = f"https://creditlibanais-netcommerce.gateway.mastercard.com/api/rest/version/78/merchant/{merchant_id}/order/{order_id}"
order = Order.objects.get(order_id=order_id)
headers = {
'Content-Type': 'application/json',
'Authorization': basic_auth_header(merchant_id, api_password)
}
try:
response = requests.get(url, headers=headers)
if response.status_code == 200:
order_details = response.json()
if order_details.get('result') == 'SUCCESS':
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()
if order_item.item.item_type.name == 'OSIMENU':
api_url = 'https://osimenu.com/api/create-subscription/'
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
end_date_str = order_item.end_at.strftime('%Y-%m-%d')
api_data = {
'user': {
'username': request.user.email,
'email': request.user.email,
'password': random_string,
'first_name': request.user.first_name,
'last_name': request.user.last_name
},
'customer': {
'mobile_number': request.user.customerprofile.mobile_number
},
'subscription': {
'plan': order_item.item.title,
'end_date': end_date_str
}
}
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', order__customer=request.user.customerprofile)
for item in old_order_items:
item.active = False
item.save()
order_item.save()
add_invoice_pdf(request, order_id=order.id)
return redirect('customerorders')
error_message = 'Failed to retrieve order details: ' + response.text
return JsonResponse({'error': error_message}, status=500)
except Exception as e:
error_message = 'Exception: ' + str(e)
return JsonResponse({'error': error_message}, status=500)
def buy_free_osimenu(request):
api_url = 'https://osimenu.com/api/create-subscription/'
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
api_data = {
'user': {
'username': request.user.email,
'email': request.user.email,
'password': random_string,
'first_name': request.user.first_name,
'last_name': request.user.last_name
},
'customer': {
'mobile_number': request.user.customerprofile.mobile_number
},
'subscription': {
'plan': 'OSIMENU BASIC'
}
}
order = Order.objects.create(
customer = request.user.customerprofile,
)
order_status = OrderStatus.objects.create(
order=order,
status = 'Completed',
date = datetime.now()
)
order_item = OrderItem.objects.create(
order = order,
item = get_object_or_404(Item, title='OSIMENU BASIC'),
purchased_at = datetime.now(),
active = True,
)
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()
response = requests.post(api_url, json=api_data)
print(response)
return redirect('customerorders')
def buy_free_osicard(request):
api_url = 'https://mybusinesscardpage.com/api/create-subscription/'
random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=10))
api_data = {
'user': {
'username': request.user.email,
'email': request.user.email,
'password': random_string,
'first_name': request.user.first_name,
'last_name': request.user.last_name
},
'customer': {
'mobile_number': request.user.customerprofile.mobile_number
},
'subscription': {
'plan': 'OSICARD BASIC'
}
}
order = Order.objects.create(
customer = request.user.customerprofile,
)
order_status = OrderStatus.objects.create(
order=order,
status = 'Completed',
date = datetime.now()
)
order_item = OrderItem.objects.create(
order = order,
item = get_object_or_404(Item, title='OSICARD BASIC'),
purchased_at = datetime.now(),
active = True,
)
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()
response = requests.post(api_url, json=api_data)
print(response)
return redirect('customerorders')
return render(request, 'listing_pages/customer-projects.html', context)
#Invoice Payment views
@customer_login_required
def invoice_payment(request, invoice_id):
invoice = get_object_or_404(Invoice, id = invoice_id)
def customer_project_details(request, *args, **kwargs):
context = {
'invoice' : invoice,
}
return render(request, 'invoice-payment.html', context)
return render(request, 'details_templates/inner-customer-project.html', context)
@customer_login_required
def initiate_invoice_checkout(request):
api_username = 'merchant.TEST06127800'
api_password = '37846250a67c70e7fe9f82cf6ca81f93'
merchant_id = 'TEST06127800'
merchant_name = 'Ositcom Sal'
customer = request.user.customerprofile
data = json.loads(request.body)
invoice_id = data.get('invoice_id')
invoice = get_object_or_404(Invoice, id=invoice_id)
order = invoice.order
order_id = order.order_id
price = order.get_cart_total
payload = {
'apiOperation': 'INITIATE_CHECKOUT',
'apiUsername': api_username,
'apiPassword': api_password,
'merchant': merchant_id,
'interaction.operation': 'PURCHASE',
'interaction.merchant.name': merchant_name,
'order.id': order_id,
'order.amount': price,
'order.currency': 'USD',
'order.description': 'description_of_order',
'order.notificationUrl' : 'https://newosina.osinode.com/webhooks/',
'interaction.returnUrl' : f"https://newosina.osinode.com/check-order-status/{merchant_id}/{order_id}/"
}
try:
response = requests.post('https://creditlibanais-netcommerce.gateway.mastercard.com/api/nvp/version/78', data=payload)
print('Response Content:', response.text)
if response.status_code == 200:
response_data = response.text
parsed_data = dict(item.split('=') for item in response_data.split('&'))
session_id = parsed_data.get('session.id')
return JsonResponse({'session_id': session_id}, status=200)
else:
print('Response Status Code:', response.status_code)
return JsonResponse({'error': 'Failed to initiate checkout'}, status=500)
except Exception as e:
print('Exception:', e)
return JsonResponse({'error': 'Internal Server Error'}, status=500)

Binary file not shown.

@ -87,7 +87,7 @@ def add_business(request):
business_types = BusinessType.objects.all().order_by('-id')
if request.method == 'POST':
customer_id = request.POST.get('type')
customer_id = request.POST.get('customer')
customer = get_object_or_404(CustomerProfile, id=customer_id)
name = request.POST.get('name')
@ -717,21 +717,5 @@ 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()
# Reload the parent page
return HttpResponse('<script>window.top.location.reload();</script>')
return render(request, 'add_templates/add-payment-method-modal.html', context)

@ -275,7 +275,7 @@
<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>
<p class="text-white">Payment Methods</p>
</div>
</a>
@ -567,7 +567,7 @@
<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>
<p class="text-white">Payment Methods</p>
</div>
</a>

@ -43,12 +43,14 @@
<div
class="w-full flex flex-col items-center px-5 h-[400px] xxlg1:h-[500px] overflow-hidden overflow-y-auto mt-5">
<div class="w-full flex justify-start items-center gap-3 border-b border-slate-600 py-3 text-white">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-[25px] text-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6.878V6a2.25 2.25 0 0 1 2.25-2.25h7.5A2.25 2.25 0 0 1 18 6v.878m-12 0c.235-.083.487-.128.75-.128h10.5c.263 0 .515.045.75.128m-12 0A2.25 2.25 0 0 0 4.5 9v.878m13.5-3A2.25 2.25 0 0 1 19.5 9v.878m0 0a2.246 2.246 0 0 0-.75-.128H5.25c-.263 0-.515.045-.75.128m15 0A2.25 2.25 0 0 1 21 12v6a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18v-6c0-.98.626-1.813 1.5-2.122" />
</svg>
<p class="">Projects</p>
</div>
<a href="{% url 'customerprojects' %}" class="w-full">
<div class="w-full flex justify-start items-center gap-3 border-b border-slate-600 py-3 text-white">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-[25px] text-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6.878V6a2.25 2.25 0 0 1 2.25-2.25h7.5A2.25 2.25 0 0 1 18 6v.878m-12 0c.235-.083.487-.128.75-.128h10.5c.263 0 .515.045.75.128m-12 0A2.25 2.25 0 0 0 4.5 9v.878m13.5-3A2.25 2.25 0 0 1 19.5 9v.878m0 0a2.246 2.246 0 0 0-.75-.128H5.25c-.263 0-.515.045-.75.128m15 0A2.25 2.25 0 0 1 21 12v6a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18v-6c0-.98.626-1.813 1.5-2.122" />
</svg>
<p class="">Projects</p>
</div>
</a>
<a href="{% url 'customertickets' %}" class="w-full">
<div class="w-full flex justify-start items-center gap-3 border-b border-slate-600 py-3 text-white">
@ -198,16 +200,19 @@
<!-- MOBILE MENU ITEMS -->
<div class="w-full flex flex-col items-center px-3 h-[400px] overflow-hidden overflow-y-auto">
<div class="w-full">
<div class="w-full flex justify-between items-center border-b border-slate-600 py-3">
<div class="w-full flex justify-start items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-[25px] text-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6.878V6a2.25 2.25 0 0 1 2.25-2.25h7.5A2.25 2.25 0 0 1 18 6v.878m-12 0c.235-.083.487-.128.75-.128h10.5c.263 0 .515.045.75.128m-12 0A2.25 2.25 0 0 0 4.5 9v.878m13.5-3A2.25 2.25 0 0 1 19.5 9v.878m0 0a2.246 2.246 0 0 0-.75-.128H5.25c-.263 0-.515.045-.75.128m15 0A2.25 2.25 0 0 1 21 12v6a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18v-6c0-.98.626-1.813 1.5-2.122" />
</svg>
<p class="text-white">Projects</p>
<a href="{% url 'customerprojects' %}" class="w-full">
<div class="w-full">
<div class="w-full flex justify-between items-center border-b border-slate-600 py-3">
<div class="w-full flex justify-start items-center gap-3">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-[25px] text-white">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 6.878V6a2.25 2.25 0 0 1 2.25-2.25h7.5A2.25 2.25 0 0 1 18 6v.878m-12 0c.235-.083.487-.128.75-.128h10.5c.263 0 .515.045.75.128m-12 0A2.25 2.25 0 0 0 4.5 9v.878m13.5-3A2.25 2.25 0 0 1 19.5 9v.878m0 0a2.246 2.246 0 0 0-.75-.128H5.25c-.263 0-.515.045-.75.128m15 0A2.25 2.25 0 0 1 21 12v6a2.25 2.25 0 0 1-2.25 2.25H5.25A2.25 2.25 0 0 1 3 18v-6c0-.98.626-1.813 1.5-2.122" />
</svg>
<p class="text-white">Projects</p>
</div>
</div>
</div>
</div>
</a>
<a href="{% url 'customertickets' %}" class="w-full">

@ -7,25 +7,25 @@
<div class="w-full xxlg1:w-[74.5%] bg-white h-fit rounded-md shadow-md p-5">
{% if project.status == 'Completed' %}
<div class="w-full bg-green-700 rounded-t-md flex flex-col justify-center items-center py-2">
<div class="w-full bg-green-700 rounded-md flex flex-col justify-center items-center py-2">
<h1 class="text-xl md:text-3xl text-white font-semibold text-center">{{project.name}}</h1>
<p class="text-white text-center text-base">{{project.project_id}}</p>
</div>
{% endif %}
{% if project.status == 'Cancelled' %}
<div class="w-full bg-red-500 rounded-t-md flex flex-col justify-center items-center py-2">
<div class="w-full bg-red-500 rounded-md flex flex-col justify-center items-center py-2">
<h1 class="text-xl md:text-3xl text-white font-semibold text-center">{{project.name}}</h1>
<p class="text-white text-center text-base">{{project.project_id}}</p>
</div>
{% endif %}
{% if project.status == 'Active' %}
<div class="w-full bg-orange-500 rounded-t-md flex flex-col justify-center items-center py-2">
<div class="w-full bg-orange-500 rounded-md flex flex-col justify-center items-center py-2">
<h1 class="text-xl md:text-3xl text-white font-semibold text-center">{{project.name}}</h1>
<p class="text-white text-center text-base">{{project.project_id}}</p>
</div>
{% endif %}
{% if project.status == 'Pending' %}
<div class="w-full bg-yellow-400 rounded-t-md flex flex-col justify-center items-center py-2">
<div class="w-full bg-yellow-400 rounded-md flex flex-col justify-center items-center py-2">
<h1 class="text-xl md:text-3xl text-white font-semibold text-center">{{project.name}}</h1>
<p class="text-white text-center text-base">{{project.project_id}}</p>
</div>
@ -45,8 +45,14 @@
<p id="projectId" class="hidden">{{project.id}}</p>
<!-- PROJECT PROGRESS BAR -->
<div class="w-full h-[10px] bg-gray-100 shadow-md mt-3 xll:mt-1 mainBar rounded-md xll:rounded-none">
<div class="h-full rounded-md xll:rounded-none progressBar">
<div class="flex flex-col gap-1 mt-3">
<div class="w-full h-[10px] bg-gray-100 shadow-md mainBar rounded-md">
<div class="h-full rounded-md progressBar">
</div>
</div>
<div class="w-full flex justify-between items-center gap-3 text-gray-500 font-light text-sm">
<p class="startDate">{{project.start_date}}</p>
<p class="endDate">{{project.end_date}}</p>
</div>
</div>
@ -84,28 +90,62 @@
</div>
<div class="w-full flex flex-col gap-5 mt-5">
<!-- CLIENT -->
<div>
<p class="text-gray-500 text-xl">Client: <span
class="text-secondosiblue text-xl font-semibold">{{project.customer.user.first_name}}
{{project.customer.user.last_name}}</span></p>
</div>
<!-- PROJECT MANAGER -->
<div>
<p class="text-gray-500 text-xl">Project Manager: <span
class="text-secondosiblue text-xl font-semibold">{{project.manager.user.first_name}}
{{project.manager.last_name}}</span></p>
</div>
<p class="text-gray-500 text-xl">Project Manager:</p>
<div
class="w-fit flex justify-start items-center gap-1 px-9 py-3 bg-gray-100 rounded-md shadow-md mt-2">
<div class="w-[50px] h-[50px] rounded-full bg-white">
{% if project.manager.image %}
<img src="{{project.manager.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<img src="{% static 'images/default-user.png' %}"
class="w-full h-full rounded-full object-cover">
{% endif %}
</div>
<p class="text-secondosiblue font-light">{{project.manager.user.first_name}}
{{project.manager.user.last_name}}</p>
</div>
</div>
<!-- MEMBERS -->
<div>
<p class="text-gray-500 text-xl">Member(s):
<p class="text-gray-500 text-xl">Member(s):</p>
<div class="w-full flex justify-start items-center gap-3 flex-wrap mt-2">
{% for member in project.members.all %}
<span class="text-secondosiblue text-xl font-semibold">
{{member.user.first_name}} {{member.user.last_name}} {% if not forloop.last %}, {% endif %}
{% endfor %}</span>
</p>
<div
class="w-fit flex flex-col justify-center items-center gap-3 px-9 py-3 bg-gray-100 rounded-md shadow-md">
<div class="flex justify-start items-center gap-1">
<div class="w-[50px] h-[50px] rounded-full bg-white">
{% if member.image %}
<img src="{{member.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<img src="{% static 'images/default-user.png' %}"
class="w-full h-full rounded-full object-cover">
{% endif %}
</div>
<p class="text-secondosiblue font-light">{{member.user.first_name}} {{member.user.last_name}}
</p>
</div>
<p class="text-secondosiblue font-poppinsBold text-sm">Working Hours: 0hr 0min 0sec</p>
</div>
{% endfor %}
</div>
</div>
<!-- TYPE -->
<div>
<p class="text-gray-500 text-xl">Type:
{% for type in project.project_type.all %}
@ -115,6 +155,8 @@
</p>
</div>
<!-- STATUS -->
<div>
<p class="text-gray-500 text-xl">Status:
{% if project.status == 'Cancelled' %}
@ -132,15 +174,6 @@
</p>
</div>
<div>
<p class="text-gray-500 text-xl">Start Date: <span
class="text-secondosiblue text-xl font-semibold startDate">{{project.start_date}}</span></p>
</div>
<div>
<p class="text-gray-500 text-xl">End Date: <span
class="text-secondosiblue text-xl font-semibold endDate">{{project.end_date}}</span></p>
</div>
<!-- PROJECT DETAILS -->
<div>
@ -409,7 +442,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 addProjectNoteButton"
data-modal-url="{% url 'addprojectnotemodal' project.project_id %}">
data-modal-url="{% url 'addprojectnotemodal' project.project_id %}">
<i class="fa fa-plus"></i>
</button>
</div>

@ -729,7 +729,7 @@
<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>
<p class="text-white">Payment Methods</p>
</div>
</a>

@ -42,9 +42,9 @@ urlpatterns = [
path('tickets/', views.tickets, name='tickets'),
path('businesses/', views.businesses, name='businesses'),
path('staffs/', views.staffs, name='users'),
path('my-projects/', views.my_projects, name='my-projects'),
path('my-tasks/', views.my_tasks, name='my-tasks'),
path('my-notes/', views.my_notes, name='my-notes'),
path('projects/', views.my_projects, name='my-projects'),
path('tasks/', views.my_tasks, name='my-tasks'),
path('notes/', views.my_notes, name='my-notes'),
path('dailyreports/', views.daily_reports, name='dailyreports'),
path('departments/', views.departments, name='departments'),
path('projecttypes/', views.project_types, name='projecttypes'),

@ -1210,6 +1210,38 @@ video {
height: 100vh;
}
.h-\[5px\] {
height: 5px;
}
.h-9 {
height: 2.25rem;
}
.h-\[300px\] {
height: 300px;
}
.h-\[250px\] {
height: 250px;
}
.h-\[3\] {
height: 3;
}
.h-7 {
height: 1.75rem;
}
.h-\[65px\] {
height: 65px;
}
.h-\[\] {
height: ;
}
.max-h-\[50px\] {
max-height: 50px;
}
@ -1411,6 +1443,34 @@ video {
width: 100%;
}
.w-\[80px\] {
width: 80px;
}
.w-7 {
width: 1.75rem;
}
.w-\[550px\] {
width: 550px;
}
.w-\[65px\] {
width: 65px;
}
.w-\[200px\] {
width: 200px;
}
.w-\[230px\] {
width: 230px;
}
.w-\[240px\] {
width: 240px;
}
.min-w-full {
min-width: 100%;
}
@ -2258,6 +2318,16 @@ video {
background-color: rgb(202 138 4 / var(--tw-bg-opacity));
}
.bg-pink-700 {
--tw-bg-opacity: 1;
background-color: rgb(190 24 93 / var(--tw-bg-opacity));
}
.bg-yellow-300 {
--tw-bg-opacity: 1;
background-color: rgb(253 224 71 / var(--tw-bg-opacity));
}
.bg-opacity-10 {
--tw-bg-opacity: 0.1;
}
@ -2453,6 +2523,10 @@ video {
padding: 2.25rem;
}
.p-0 {
padding: 0px;
}
.px-0 {
padding-left: 0px;
padding-right: 0px;
@ -2606,6 +2680,10 @@ video {
padding-top: 2.25rem;
}
.pb-9 {
padding-bottom: 2.25rem;
}
.text-left {
text-align: left;
}
@ -2871,6 +2949,10 @@ video {
color: rgb(234 179 8 / var(--tw-text-opacity));
}
.text-\[xl\] {
color: xl;
}
.underline {
text-decoration-line: underline;
}
@ -3267,6 +3349,12 @@ video {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:scale-100:hover {
--tw-scale-x: 1;
--tw-scale-y: 1;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.hover\:bg-gray-50:hover {
--tw-bg-opacity: 1;
background-color: rgb(249 250 251 / var(--tw-bg-opacity));
@ -3291,6 +3379,11 @@ video {
background-color: rgb(255 255 255 / var(--tw-bg-opacity));
}
.hover\:bg-gray-100:hover {
--tw-bg-opacity: 1;
background-color: rgb(243 244 246 / var(--tw-bg-opacity));
}
.hover\:bg-opacity-60:hover {
--tw-bg-opacity: 0.6;
}
@ -3309,6 +3402,11 @@ video {
color: rgb(243 244 246 / var(--tw-text-opacity));
}
.hover\:text-gray-400:hover {
--tw-text-opacity: 1;
color: rgb(156 163 175 / var(--tw-text-opacity));
}
.hover\:text-gray-500:hover {
--tw-text-opacity: 1;
color: rgb(107 114 128 / var(--tw-text-opacity));
@ -3477,10 +3575,18 @@ video {
flex-direction: row;
}
.s\:items-center {
align-items: center;
}
.s\:justify-end {
justify-content: flex-end;
}
.s\:justify-between {
justify-content: space-between;
}
.s\:gap-0 {
gap: 0px;
}
@ -3851,27 +3957,10 @@ video {
width: 75%;
}
.xll\:w-fit {
width: -moz-fit-content;
width: fit-content;
}
.xll\:grid-cols-4 {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
.xll\:flex-row {
flex-direction: row;
}
.xll\:justify-end {
justify-content: flex-end;
}
.xll\:justify-between {
justify-content: space-between;
}
.xll\:rounded-none {
border-radius: 0px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -11,7 +11,7 @@ function initiateCheckout(item_id, selectedCycleId) {
paymentLoader.classList.remove('hidden');
fetch('/initiate_checkout/', {
fetch('/payment/buy-now-checkout/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',

@ -1,22 +1,22 @@
const paymentContent = document.getElementById('paymentContent');
const paymentLoader = document.getElementById('paymentLoader');
const invoice_id = document.getElementById('invoiceId').textContent;
const payment_id = document.getElementById('paymentId').textContent;
function initiateInvoiceCheckout(invoice_id) {
function initiatePaymentCheckout(payment_id) {
const csrftoken = getCookie('csrftoken');
console.log(invoice_id);
console.log(payment_id);
console.log('CSRF Token:', csrftoken);
paymentLoader.classList.remove('hidden');
fetch('/initiate_invoice_checkout/', {
fetch('/payment/initiate-payment-checkout/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRFToken': csrftoken,
},
body: JSON.stringify({ invoice_id: invoice_id }),
body: JSON.stringify({ payment_id: payment_id }),
})
.then(response => {
if (!response.ok) {
@ -60,4 +60,4 @@ function getCookie(name) {
return cookieValue;
}
initiateInvoiceCheckout(invoice_id);
initiatePaymentCheckout(payment_id);

@ -71,6 +71,8 @@ document.addEventListener("DOMContentLoaded", function () {
addButtonClickListener("editPaymentButton", "500px", "400px");
addButtonClickListener("addPaymentMethodButton", "500px", "400px");
addButtonClickListener("updateOrderStatusButton", "400px", "160px");

@ -123,6 +123,12 @@ menuContainers.forEach((menuContainer) => {
document.querySelectorAll('.menuDropdownItems').forEach(item => {
if (item !== menuDropdownItems) {
item.classList.remove('active');
// Update the arrow icons for all other dropdowns
const otherAngleDown = item.parentElement.querySelector('.angleDown');
const otherAngleUp = item.parentElement.querySelector('.angleUp');
otherAngleDown.style.display = 'inline';
otherAngleUp.style.display = 'none';
}
});

Loading…
Cancel
Save