update
This commit is contained in:
commit
8a073d8df8
16
inventory/management/commands/set_custom_permissions.py
Normal file
16
inventory/management/commands/set_custom_permissions.py
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
from inventory import models
|
||||||
|
from django.contrib.auth.models import Permission
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
from django_ledger.models import EstimateModel,BillModel,AccountModel,LedgerModel
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
def handle(self, *args, **kwargs):
|
||||||
|
Permission.objects.get_or_create(name="Can view crm",codename="can_view_crm",content_type=ContentType.objects.get_for_model(models.Lead))
|
||||||
|
Permission.objects.get_or_create(name="Can view sales",codename="can_view_sales",content_type=ContentType.objects.get_for_model(EstimateModel))
|
||||||
|
Permission.objects.get_or_create(name="Can view reports",codename="can_view_reports",content_type=ContentType.objects.get_for_model(LedgerModel))
|
||||||
|
Permission.objects.get_or_create(name="Can view inventory",codename="can_view_inventory",content_type=ContentType.objects.get_for_model(models.Car))
|
||||||
|
Permission.objects.get_or_create(name="Can approve bill",codename="can_approve_billmodel",content_type=ContentType.objects.get_for_model(BillModel))
|
||||||
|
Permission.objects.get_or_create(name="Can view financials",codename="can_view_financials",content_type=ContentType.objects.get_for_model(AccountModel))
|
||||||
|
Permission.objects.get_or_create(name="Can approve estimate",codename="can_approve_estimatemodel",content_type=ContentType.objects.get_for_model(EstimateModel))
|
||||||
@ -2550,15 +2550,6 @@ class CustomGroup(models.Model):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def set_default_permissions(self):
|
def set_default_permissions(self):
|
||||||
Permission.objects.get_or_create(name="Can approve estimate",codename="can_approve_estimatemodel",content_type=ContentType.objects.get_for_model(EstimateModel))
|
|
||||||
Permission.objects.get_or_create(name="Can approve bill",codename="can_approve_billmodel",content_type=ContentType.objects.get_for_model(BillModel))
|
|
||||||
|
|
||||||
Permission.objects.get_or_create(name="Can view inventory",codename="can_view_inventory",content_type=ContentType.objects.get_for_model(Car))
|
|
||||||
Permission.objects.get_or_create(name="Can view sales",codename="can_view_sales",content_type=ContentType.objects.get_for_model(EstimateModel))
|
|
||||||
Permission.objects.get_or_create(name="Can view crm",codename="can_view_crm",content_type=ContentType.objects.get_for_model(Lead))
|
|
||||||
Permission.objects.get_or_create(name="Can view financials",codename="can_view_financials",content_type=ContentType.objects.get_for_model(AccountModel))
|
|
||||||
Permission.objects.get_or_create(name="Can view reports",codename="can_view_reports",content_type=ContentType.objects.get_for_model(LedgerModel))
|
|
||||||
|
|
||||||
self.clear_permissions()
|
self.clear_permissions()
|
||||||
######################################
|
######################################
|
||||||
######################################
|
######################################
|
||||||
@ -2698,7 +2689,6 @@ class CustomGroup(models.Model):
|
|||||||
"view_carcolors",
|
"view_carcolors",
|
||||||
"view_cartransfer",
|
"view_cartransfer",
|
||||||
"view_saleorder",
|
"view_saleorder",
|
||||||
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
self.set_permissions(
|
self.set_permissions(
|
||||||
@ -2966,4 +2956,22 @@ class ExtraInfo(models.Model):
|
|||||||
verbose_name_plural = "Extra Info"
|
verbose_name_plural = "Extra Info"
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"ExtraInfo for {self.content_object} ({self.content_type})"
|
return f"ExtraInfo for {self.content_object} ({self.content_type})"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_sale_orders(cls, staff=None,is_dealer=False):
|
||||||
|
if not staff and not is_dealer:
|
||||||
|
return []
|
||||||
|
if is_dealer:
|
||||||
|
qs = ExtraInfo.objects.filter(
|
||||||
|
content_type=ContentType.objects.get_for_model(EstimateModel),
|
||||||
|
related_content_type=ContentType.objects.get_for_model(Staff),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
qs = ExtraInfo.objects.filter(
|
||||||
|
content_type=ContentType.objects.get_for_model(EstimateModel),
|
||||||
|
related_content_type=ContentType.objects.get_for_model(Staff),
|
||||||
|
related_object_id=staff.pk,
|
||||||
|
)
|
||||||
|
|
||||||
|
return [x.content_object.sale_orders.first for x in qs]
|
||||||
|
|||||||
@ -1,10 +1,11 @@
|
|||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
from django_ledger.io import roles
|
from django_ledger.io import roles
|
||||||
from django.core.mail import send_mail
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from inventory.models import DealerSettings, Dealer
|
|
||||||
from django_q.tasks import async_task
|
from django_q.tasks import async_task
|
||||||
|
from django.core.mail import send_mail
|
||||||
|
from appointment.models import StaffMember
|
||||||
|
from django.contrib.auth.models import User,Group, Permission
|
||||||
|
from inventory.models import DealerSettings, Dealer
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
def create_settings(pk):
|
def create_settings(pk):
|
||||||
instance = Dealer.objects.get(pk=pk)
|
instance = Dealer.objects.get(pk=pk)
|
||||||
@ -1459,8 +1460,26 @@ def send_email(from_, to_, subject, message):
|
|||||||
async_task(send_mail,subject, message, from_email, recipient_list)
|
async_task(send_mail,subject, message, from_email, recipient_list)
|
||||||
|
|
||||||
|
|
||||||
# @background
|
def create_user_dealer(email, password, name, arabic_name, phone, crn, vrn, address):
|
||||||
def long_running_task(duration):
|
with transaction.atomic():
|
||||||
"""Example background task"""
|
user = User.objects.create(username=email, email=email)
|
||||||
print("Task completed")
|
user.set_password(password)
|
||||||
return True
|
user.save()
|
||||||
|
group = Group.objects.create(name=f"{user.pk}-Admin")
|
||||||
|
user.groups.add(group)
|
||||||
|
for perm in Permission.objects.filter(
|
||||||
|
content_type__app_label__in=["inventory", "django_ledger"]
|
||||||
|
):
|
||||||
|
group.permissions.add(perm)
|
||||||
|
|
||||||
|
StaffMember.objects.create(user=user)
|
||||||
|
Dealer.objects.create(
|
||||||
|
user=user,
|
||||||
|
name=name,
|
||||||
|
arabic_name=arabic_name,
|
||||||
|
crn=crn,
|
||||||
|
vrn=vrn,
|
||||||
|
phone_number=phone,
|
||||||
|
address=address,
|
||||||
|
)
|
||||||
|
|
||||||
|
|||||||
@ -1,33 +1,29 @@
|
|||||||
import json
|
import json
|
||||||
|
import secrets
|
||||||
import datetime
|
import datetime
|
||||||
from plans.models import AbstractOrder
|
|
||||||
from django.contrib.auth.models import Group, Permission
|
|
||||||
from django.db import transaction
|
|
||||||
from django.urls import reverse
|
|
||||||
import requests
|
import requests
|
||||||
from decimal import Decimal
|
from decimal import Decimal
|
||||||
|
from inventory import models
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.conf import settings
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from django_ledger.io import roles
|
from django_ledger.io import roles
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django_q.tasks import async_task
|
||||||
from django_ledger.models.journal_entry import JournalEntryModel
|
|
||||||
from django_ledger.models.transactions import TransactionModel
|
|
||||||
from inventory import models
|
|
||||||
from django.conf import settings
|
|
||||||
from django.core.mail import send_mail
|
from django.core.mail import send_mail
|
||||||
from django.utils.translation import gettext_lazy as _
|
from plans.models import AbstractOrder
|
||||||
from django_ledger.models.items import ItemModel
|
|
||||||
from django_ledger.models import (
|
from django_ledger.models import (
|
||||||
InvoiceModel,
|
InvoiceModel,
|
||||||
BillModel,
|
BillModel,
|
||||||
VendorModel,
|
VendorModel,
|
||||||
)
|
)
|
||||||
|
from django_ledger.models.items import ItemModel
|
||||||
from django.utils.translation import get_language
|
from django.utils.translation import get_language
|
||||||
from appointment.models import StaffMember
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.contrib.auth.models import User
|
from django.utils.translation import gettext_lazy as _
|
||||||
from django_q.tasks import async_task
|
from django_ledger.models.transactions import TransactionModel
|
||||||
import secrets
|
from django_ledger.models.journal_entry import JournalEntryModel
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
logger=logging.getLogger(__name__)
|
logger=logging.getLogger(__name__)
|
||||||
@ -1370,31 +1366,6 @@ def create_make_accounts(dealer):
|
|||||||
active=True,
|
active=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def create_user_dealer(email, password, name, arabic_name, phone, crn, vrn, address):
|
|
||||||
with transaction.atomic():
|
|
||||||
user = User.objects.create(username=email, email=email)
|
|
||||||
user.set_password(password)
|
|
||||||
user.save()
|
|
||||||
group = Group.objects.create(name=f"{user.pk}-Admin")
|
|
||||||
user.groups.add(group)
|
|
||||||
for perm in Permission.objects.filter(
|
|
||||||
content_type__app_label__in=["inventory", "django_ledger"]
|
|
||||||
):
|
|
||||||
group.permissions.add(perm)
|
|
||||||
|
|
||||||
StaffMember.objects.create(user=user)
|
|
||||||
models.Dealer.objects.create(
|
|
||||||
user=user,
|
|
||||||
name=name,
|
|
||||||
arabic_name=arabic_name,
|
|
||||||
crn=crn,
|
|
||||||
vrn=vrn,
|
|
||||||
phone_number=phone,
|
|
||||||
address=address,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def handle_payment(request, order):
|
def handle_payment(request, order):
|
||||||
url = "https://api.moyasar.com/v1/payments"
|
url = "https://api.moyasar.com/v1/payments"
|
||||||
callback_url = request.build_absolute_uri(reverse("payment_callback", args=[request.dealer.slug]))
|
callback_url = request.build_absolute_uri(reverse("payment_callback", args=[request.dealer.slug]))
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
# Standard
|
# Standard
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import io
|
import io
|
||||||
import csv
|
import csv
|
||||||
import cv2
|
import cv2
|
||||||
@ -48,8 +49,7 @@ from django.shortcuts import HttpResponse
|
|||||||
|
|
||||||
from django.db.models import Sum, F, Count
|
from django.db.models import Sum, F, Count
|
||||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User,Group
|
||||||
from django.contrib.auth.models import Group
|
|
||||||
from django.db.models import Value
|
from django.db.models import Value
|
||||||
from django.urls import reverse, reverse_lazy
|
from django.urls import reverse, reverse_lazy
|
||||||
from django.utils import timezone, translation
|
from django.utils import timezone, translation
|
||||||
@ -186,7 +186,6 @@ from .services import (
|
|||||||
)
|
)
|
||||||
from .utils import (
|
from .utils import (
|
||||||
CarFinanceCalculator,
|
CarFinanceCalculator,
|
||||||
create_user_dealer,
|
|
||||||
get_car_finance_data,
|
get_car_finance_data,
|
||||||
get_item_transactions,
|
get_item_transactions,
|
||||||
handle_payment,
|
handle_payment,
|
||||||
@ -197,11 +196,11 @@ from .utils import (
|
|||||||
set_invoice_payment,
|
set_invoice_payment,
|
||||||
CarTransfer,
|
CarTransfer,
|
||||||
)
|
)
|
||||||
from .tasks import create_accounts_for_make, send_email
|
from .tasks import create_accounts_for_make, create_user_dealer, send_email
|
||||||
|
|
||||||
# djago easy audit log
|
# djago easy audit log
|
||||||
from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent
|
from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent
|
||||||
|
from django_q.tasks import async_task
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
@ -293,38 +292,35 @@ def dealer_signup(request):
|
|||||||
or failure.
|
or failure.
|
||||||
:rtype: Union[django.http.HttpResponse, django.http.JsonResponse]
|
:rtype: Union[django.http.HttpResponse, django.http.JsonResponse]
|
||||||
"""
|
"""
|
||||||
form1 = forms.WizardForm1()
|
|
||||||
form2 = forms.WizardForm2()
|
|
||||||
form3 = forms.WizardForm3()
|
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if "Hx-Request" in request.headers:
|
|
||||||
form1 = forms.WizardForm1(request.POST)
|
|
||||||
return render(
|
|
||||||
request,
|
|
||||||
"account/signup-wizard.html",
|
|
||||||
{"form1": form1, "form2": form2, "form3": form3},
|
|
||||||
)
|
|
||||||
|
|
||||||
data = json.loads(request.body)
|
data = json.loads(request.body)
|
||||||
wf1 = data.get("wizardValidationForm1")
|
|
||||||
wf2 = data.get("wizardValidationForm2")
|
email = data.get("email")
|
||||||
wf3 = data.get("wizardValidationForm3")
|
password = data.get("password")
|
||||||
email = wf1.get("email")
|
password_confirm = data.get("confirm_password")
|
||||||
password = wf1.get("password")
|
name = data.get("name")
|
||||||
password_confirm = wf1.get("confirm_password")
|
arabic_name = data.get("arabic_name")
|
||||||
name = wf2.get("name")
|
phone = data.get("phone_number")
|
||||||
arabic_name = wf2.get("arabic_name")
|
crn = data.get("crn")
|
||||||
phone = wf2.get("phone_number")
|
vrn = data.get("vrn")
|
||||||
crn = wf3.get("crn")
|
address = data.get("address")
|
||||||
vrn = wf3.get("vrn")
|
|
||||||
address = wf3.get("address")
|
if User.objects.filter(email=email).exists():
|
||||||
|
return JsonResponse({"error": _("Email already exists")}, status=400)
|
||||||
|
if not re.match(
|
||||||
|
r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$",
|
||||||
|
email,
|
||||||
|
):
|
||||||
|
return JsonResponse({"error": _("Please enter a valid email address")}, status=400)
|
||||||
|
|
||||||
|
if len(password) < 8:
|
||||||
|
return JsonResponse({"error": _("Password must be at least 8 characters")}, status=400)
|
||||||
|
|
||||||
if password != password_confirm:
|
if password != password_confirm:
|
||||||
return JsonResponse({"error": _("Passwords do not match")}, status=400)
|
return JsonResponse({"error": _("Passwords do not match")}, status=400)
|
||||||
try:
|
try:
|
||||||
#Todo make this a django-q task
|
async_task(create_user_dealer(
|
||||||
create_user_dealer(
|
|
||||||
email, password, name, arabic_name, phone, crn, vrn, address
|
email, password, name, arabic_name, phone, crn, vrn, address
|
||||||
)
|
)
|
||||||
logger.info(f"Delear created succesfully with emailID {email}")
|
logger.info(f"Delear created succesfully with emailID {email}")
|
||||||
@ -335,7 +331,6 @@ def dealer_signup(request):
|
|||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
"account/signup-wizard.html",
|
"account/signup-wizard.html",
|
||||||
{"form1": form1, "form2": form2, "form3": form3},
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -1197,7 +1192,7 @@ def inventory_stats_view(request, dealer_slug):
|
|||||||
"inventory/inventory_stats.html" template.
|
"inventory/inventory_stats.html" template.
|
||||||
:rtype: HttpResponse
|
:rtype: HttpResponse
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Base queryset for cars belonging to the dealer
|
# Base queryset for cars belonging to the dealer
|
||||||
cars = models.Car.objects.filter(dealer=request.dealer)
|
cars = models.Car.objects.filter(dealer=request.dealer)
|
||||||
|
|
||||||
@ -4206,18 +4201,26 @@ def sales_list_view(request, dealer_slug):
|
|||||||
"""
|
"""
|
||||||
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
||||||
entity = dealer.entity
|
entity = dealer.entity
|
||||||
|
staff = getattr(request.user.staffmember, "staff", None)
|
||||||
sale_orders = models.SaleOrder.objects.filter(
|
qs = []
|
||||||
dealer=dealer,
|
try:
|
||||||
)
|
if dealer:
|
||||||
paginator = Paginator(sale_orders, 30)
|
qs = models.ExtraInfo.get_sale_orders(staff=staff,is_dealer=True)
|
||||||
|
else:
|
||||||
|
qs = models.ExtraInfo.get_sale_orders(staff=staff)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
print(qs)
|
||||||
|
# sale_orders = models.SaleOrder.objects.filter(
|
||||||
|
# dealer=dealer,
|
||||||
|
# )
|
||||||
|
paginator = Paginator(qs, 30)
|
||||||
page_number = request.GET.get("page")
|
page_number = request.GET.get("page")
|
||||||
page_obj = paginator.get_page(page_number)
|
page_obj = paginator.get_page(page_number)
|
||||||
|
|
||||||
context = {"txs": page_obj, "page_obj": page_obj}
|
context = {"txs": page_obj, "page_obj": page_obj}
|
||||||
return render(request, "sales/sales_list.html", context)
|
return render(request, "sales/sales_list.html", context)
|
||||||
|
|
||||||
|
|
||||||
class SaleOrderDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView):
|
class SaleOrderDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView):
|
||||||
model = models.SaleOrder
|
model = models.SaleOrder
|
||||||
template_name = "sales/saleorder_detail.html"
|
template_name = "sales/saleorder_detail.html"
|
||||||
@ -4239,6 +4242,15 @@ class SaleOrderDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView
|
|||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
sale_order = self.get_object()
|
||||||
|
status = request.POST.get("status")
|
||||||
|
if status:
|
||||||
|
sale_order.status = status
|
||||||
|
sale_order.save()
|
||||||
|
messages.success(request, _("Sale order status updated"))
|
||||||
|
return redirect("order_detail", dealer_slug=sale_order.dealer.slug, pk=sale_order.pk)
|
||||||
|
|
||||||
|
|
||||||
# Estimates
|
# Estimates
|
||||||
class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||||
|
|||||||
@ -26,6 +26,8 @@ python3 manage.py tenhal_plan
|
|||||||
|
|
||||||
python3 manage.py set_vat
|
python3 manage.py set_vat
|
||||||
|
|
||||||
|
python3 manage.py set_custom_permissions
|
||||||
|
|
||||||
python3 manage.py initial_services_offered
|
python3 manage.py initial_services_offered
|
||||||
|
|
||||||
echo "Done"
|
echo "Done"
|
||||||
BIN
static/images/customers/sun_mountain.jpg
Normal file
BIN
static/images/customers/sun_mountain.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
287
templates/account/signup-wizard-copy.html
Normal file
287
templates/account/signup-wizard-copy.html
Normal file
@ -0,0 +1,287 @@
|
|||||||
|
{% extends "welcome_base.html" %}
|
||||||
|
{% load crispy_forms_filters %}
|
||||||
|
{% load i18n static %}
|
||||||
|
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<section class="main my-2">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row form-container" id="form-container">
|
||||||
|
|
||||||
|
<div class="col-12 "><a class="d-flex flex-center text-decoration-none mb-4" href="{% url 'home' %}">
|
||||||
|
<div class="d-flex align-items-center fw-bolder fs-3 d-inline-block">
|
||||||
|
<img class="d-dark-none" src="{% static 'images/logos/logo-d.png' %}" alt="{% trans 'home' %}" width="58" />
|
||||||
|
<img class="d-light-none" src="{% static 'images/logos/logo.png' %}" alt="{% trans 'home' %}" width="58" />
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="text-center">
|
||||||
|
<h3 class="text-body-highlight">{% trans 'Sign Up' %}</h3>
|
||||||
|
<p class="text-body-tertiary fs-9">{% trans 'Create your account today' %}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card theme-wizard" data-theme-wizard="data-theme-wizard">
|
||||||
|
<div class="card-header pt-3 pb-2 ">
|
||||||
|
<ul class="nav justify-content-between nav-wizard nav-wizard-success" role="tablist">
|
||||||
|
<li class="nav-item" role="presentation"><a class="nav-link active fw-semibold" href="#bootstrap-wizard-validation-tab1" data-bs-toggle="tab" data-wizard-step="1" aria-selected="true" role="tab">
|
||||||
|
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-lock"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Access' %}</span></div>
|
||||||
|
</a></li>
|
||||||
|
<li class="nav-item" role="presentation"><a class="nav-link fw-semibold" href="#bootstrap-wizard-validation-tab2" data-bs-toggle="tab" data-wizard-step="2" aria-selected="false" tabindex="-1" role="tab">
|
||||||
|
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-user"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Account' %}</span></div>
|
||||||
|
</a></li>
|
||||||
|
<li class="nav-item" role="presentation"><a class="nav-link fw-semibold" href="#bootstrap-wizard-validation-tab3" data-bs-toggle="tab" data-wizard-step="3" aria-selected="false" tabindex="-1" role="tab">
|
||||||
|
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><svg class="fa fa-file-lines"></svg></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Extra' %}</span></div>
|
||||||
|
</a></li>
|
||||||
|
<li class="nav-item" role="presentation"><a class="nav-link fw-semibold" href="#bootstrap-wizard-validation-tab4" data-bs-toggle="tab" data-wizard-step="4" aria-selected="false" tabindex="-1" role="tab">
|
||||||
|
<div class="text-center d-inline-block"><span class="nav-item-circle-parent"><span class="nav-item-circle"><span class="fa fa-check"></span></span></span><span class="d-none d-md-block mt-1 fs-9">{% trans 'Done' %}</span></div>
|
||||||
|
</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="card-body pt-4 pb-0">
|
||||||
|
<div class="tab-content">
|
||||||
|
<div class="tab-pane active" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab1" id="bootstrap-wizard-validation-tab1">
|
||||||
|
<form class="needs-validation" id="wizardValidationForm1" novalidate="novalidate" data-wizard-form="1">
|
||||||
|
{{form1|crispy}}
|
||||||
|
<a class="fs-10 text-decoration-none" href="{% url 'terms_and_privacy' %}" target="_blank">{{ _("Read Terms of Service and Privacy Policy")}}</a>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab2" id="bootstrap-wizard-validation-tab2">
|
||||||
|
<form class="needs-validation" id="wizardValidationForm2" novalidate="novalidate" data-wizard-form="2">
|
||||||
|
{{form2|crispy}}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab3" id="bootstrap-wizard-validation-tab3">
|
||||||
|
<form class="needs-validation" id="wizardValidationForm3" novalidate="novalidate" data-wizard-form="3">
|
||||||
|
{{form3|crispy}}
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab4" id="bootstrap-wizard-validation-tab4">
|
||||||
|
<div class="row flex-center pb-8 pt-4 gx-3 gy-4">
|
||||||
|
<div class="col-12 col-sm-auto">
|
||||||
|
<div class="text-center text-sm-start"><img class="d-dark-none" src="{% static 'images/spot-illustrations/38.webp' %}" alt="" width="220"><img class="d-light-none" src="{% static 'images/spot-illustrations/dark_38.webp' %}" alt="" width="220"></div>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-auto">
|
||||||
|
<div class="text-center text-sm-start">
|
||||||
|
<h5 class="mb-3">{% trans 'You are all set!' %}</h5>
|
||||||
|
<p class="text-body-emphasis fs-9">{% trans 'Now you can access your account' %}<br>{% trans 'anytime' %} {% trans 'anywhere' %}</p><button class="btn btn-primary px-6" id='submit_btn'>{% trans 'Submit' %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer border-top-0" data-wizard-footer="data-wizard-footer">
|
||||||
|
<div class="d-flex pager wizard list-inline mb-0">
|
||||||
|
<button class="d-none btn btn-link ps-0" type="button" data-wizard-prev-btn="data-wizard-prev-btn">{% trans 'Previous' %}</button>
|
||||||
|
<div class="flex-1 text-end">
|
||||||
|
<button class="btn btn-phoenix-primary px-6 px-sm-6 next" type="submit" data-wizard-next-btn="data-wizard-next-btn">{% trans 'Next' %}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<section class="pt-lg-0 pt-xl-8">
|
||||||
|
{% include 'footer.html' %}
|
||||||
|
</section>
|
||||||
|
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||||
|
{% endblock content %}
|
||||||
|
|
||||||
|
{% block customJS %}
|
||||||
|
<script src="https://unpkg.com/just-validate@latest/dist/just-validate.production.min.js"></script>
|
||||||
|
<script>
|
||||||
|
const validator = new JustValidate('#wizardValidationForm1', {
|
||||||
|
validateBeforeSubmitting: true,
|
||||||
|
});
|
||||||
|
const validator1 = new JustValidate('#wizardValidationForm2', {
|
||||||
|
validateBeforeSubmitting: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
validator1.addField('#wizardValidationForm2 [name="phone_number"]', [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: 'customRegexp',
|
||||||
|
value: /^05\d{8}$/,
|
||||||
|
errorMessage: '{% trans 'Please enter a valid phone number' %}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: 'maxLength',
|
||||||
|
value: 10,
|
||||||
|
errorMessage: '{% trans 'Please enter a valid phone number' %}',
|
||||||
|
},
|
||||||
|
|
||||||
|
]);
|
||||||
|
|
||||||
|
validator.addField('#wizardValidationForm1 [name="email"]', [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: 'email',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
validator.addField('#wizardValidationForm1 [name="password"]', [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: 'minLength',
|
||||||
|
value: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
]);
|
||||||
|
validator.addField('#wizardValidationForm1 [name="confirm_password"]', [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rule: 'minLength',
|
||||||
|
value: 6,
|
||||||
|
errorMessage: '{% trans 'Password does not match' %}',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (value, context) => {
|
||||||
|
if (value !== document.querySelector('#id_password').value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
validator.addField('#wizardValidationForm1 [name="terms"]', [
|
||||||
|
{
|
||||||
|
rule: 'required',
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
validator = new JustValidate('#wizardValidationForm1', {
|
||||||
|
rules: {
|
||||||
|
email: {
|
||||||
|
required: true,
|
||||||
|
email: true,
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
required: true,
|
||||||
|
min: 6,
|
||||||
|
},
|
||||||
|
confirm_password: {
|
||||||
|
required: true,
|
||||||
|
min: 6,
|
||||||
|
equalTo: '#password',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
name: {
|
||||||
|
required: 'Please enter your name',
|
||||||
|
},
|
||||||
|
email: {
|
||||||
|
required: 'Please enter your email',
|
||||||
|
email: 'Please enter a valid email address',
|
||||||
|
},
|
||||||
|
password: {
|
||||||
|
required: 'Please enter your password',
|
||||||
|
min: 'Password must be at least 6 characters',
|
||||||
|
},
|
||||||
|
confirm_password: {
|
||||||
|
required: 'Please confirm your password',
|
||||||
|
min: 'Password must be at least 6 characters',
|
||||||
|
equalTo: 'Passwords do not match',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
const url = "{% url 'account_signup' %}";
|
||||||
|
let submit_btn = document.getElementById('submit_btn');
|
||||||
|
const csrftoken = getCookie('csrftoken');
|
||||||
|
|
||||||
|
submit_btn.addEventListener('click', async () => {
|
||||||
|
const allFormData = getAllFormData();
|
||||||
|
|
||||||
|
try {
|
||||||
|
showLoading();
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': csrftoken,
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(allFormData),
|
||||||
|
});
|
||||||
|
hideLoading();
|
||||||
|
const data = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
notify("success","Account created successfully");
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = "{% url 'account_login' %}";
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
notify("error",data.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notify("error",error);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
function getAllFormData() {
|
||||||
|
const forms = document.querySelectorAll('form');
|
||||||
|
const formData = {};
|
||||||
|
|
||||||
|
forms.forEach((form, index) => {
|
||||||
|
const formId = form.id || `form${index + 1}`;
|
||||||
|
formData[formId] = {};
|
||||||
|
|
||||||
|
const formElements = form.elements;
|
||||||
|
for (let element of formElements) {
|
||||||
|
if (element.name) {
|
||||||
|
formData[formId][element.name] = element.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return formData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showLoading() {
|
||||||
|
Swal.fire({
|
||||||
|
title: "{% trans 'Please Wait' %}",
|
||||||
|
text: "{% trans 'Loading' %}...",
|
||||||
|
allowOutsideClick: false,
|
||||||
|
didOpen: () => {
|
||||||
|
Swal.showLoading();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideLoading() {
|
||||||
|
Swal.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function notify(tag,msg){
|
||||||
|
Swal.fire({
|
||||||
|
icon: tag,
|
||||||
|
titleText: msg
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function getCookie(name) {
|
||||||
|
let cookieValue = null;
|
||||||
|
if (document.cookie && document.cookie !== "") {
|
||||||
|
const cookies = document.cookie.split(";");
|
||||||
|
for (let cookie of cookies) {
|
||||||
|
cookie = cookie.trim();
|
||||||
|
if (cookie.substring(0, name.length + 1) === name + "=") {
|
||||||
|
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cookieValue;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock customJS %}
|
||||||
@ -19,7 +19,19 @@
|
|||||||
<p class="text-body-tertiary fs-9">{% trans 'Create your account today' %}</p>
|
<p class="text-body-tertiary fs-9">{% trans 'Create your account today' %}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card theme-wizard" data-theme-wizard="data-theme-wizard">
|
<div
|
||||||
|
data-signals="{
|
||||||
|
form1:{email:'',password:'',confirm_password:''},
|
||||||
|
form2:{name:'',arabic_name:'',phone_number:''},
|
||||||
|
form3:{crn:'',vrn:'',address:''},
|
||||||
|
form1_valid:true,
|
||||||
|
form2_valid:true,
|
||||||
|
form3_valid:true,
|
||||||
|
email_valid:true,
|
||||||
|
password_valid:true,
|
||||||
|
phone_number_valid:true
|
||||||
|
}"
|
||||||
|
class="card theme-wizard" data-theme-wizard="data-theme-wizard">
|
||||||
<div class="card-header pt-3 pb-2 ">
|
<div class="card-header pt-3 pb-2 ">
|
||||||
<ul class="nav justify-content-between nav-wizard nav-wizard-success" role="tablist">
|
<ul class="nav justify-content-between nav-wizard nav-wizard-success" role="tablist">
|
||||||
<li class="nav-item" role="presentation"><a class="nav-link active fw-semibold" href="#bootstrap-wizard-validation-tab1" data-bs-toggle="tab" data-wizard-step="1" aria-selected="true" role="tab">
|
<li class="nav-item" role="presentation"><a class="nav-link active fw-semibold" href="#bootstrap-wizard-validation-tab1" data-bs-toggle="tab" data-wizard-step="1" aria-selected="true" role="tab">
|
||||||
@ -37,21 +49,101 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body pt-4 pb-0">
|
<div class="card-body pt-4 pb-0">
|
||||||
<div class="tab-content">
|
<div class="tab-content" data-signals-current_form="1">
|
||||||
<div class="tab-pane active" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab1" id="bootstrap-wizard-validation-tab1">
|
<div class="tab-pane active" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab1" id="bootstrap-wizard-validation-tab1">
|
||||||
<form class="needs-validation" id="wizardValidationForm1" novalidate="novalidate" data-wizard-form="1">
|
<form class="needs-validation" id="wizardValidationForm1" novalidate="novalidate" data-wizard-form="1" data-ref-f1>
|
||||||
{{form1|crispy}}
|
<div class="mb-3">
|
||||||
<a class="fs-10 text-decoration-none" href="{% url 'terms_and_privacy' %}" target="_blank">{{ _("Read Terms of Service and Privacy Policy")}}</a>
|
<label
|
||||||
|
for="email"
|
||||||
|
data-class="{'text-danger':!$email_valid}"
|
||||||
|
class="form-label">{% trans "Email"%}
|
||||||
|
<span
|
||||||
|
data-show="!$email_valid"
|
||||||
|
class="text-danger">*</span>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
data-on-input="$email_valid = validateEmail($form1.email)"
|
||||||
|
data-on-blur="$email_valid = validateEmail($form1.email)"
|
||||||
|
data-bind-form1.email
|
||||||
|
data-class="{'is-invalid': !$email_valid , 'is-valid': ($email_valid && $form1.email)}"
|
||||||
|
type="email"
|
||||||
|
class="form-control"
|
||||||
|
id="email"
|
||||||
|
name="email"
|
||||||
|
required>
|
||||||
|
<div class="invalid-feedback" data-show="!$email_valid">
|
||||||
|
{% trans "Please enter a valid email address" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="password" class="form-label">{% trans "Password" %}</label>
|
||||||
|
<input
|
||||||
|
data-bind-form1.password
|
||||||
|
type="password"
|
||||||
|
data-on-input="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||||
|
data-on-blur="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||||
|
class="form-control"
|
||||||
|
data-class="{'is-invalid':($form1.password.length && $form1.password.length < 8),'is-valid':$form1.password.length > 8 }"
|
||||||
|
id="password"
|
||||||
|
name="password"
|
||||||
|
required>
|
||||||
|
<div class="invalid-feedback" data-show="!$password_valid">
|
||||||
|
{% trans "Password does not match. or length is less than 8 characters." %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="confirm_password" class="form-label">{% trans "Confirm Password" %}</label><span class="text-danger" data-show="!$password_valid">*</span>
|
||||||
|
<input
|
||||||
|
data-bind-form1.confirm_password
|
||||||
|
data-on-input="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||||
|
data-on-blur="$password_valid = validatePassword($form1.password,$form1.confirm_password)"
|
||||||
|
type="password"
|
||||||
|
class="form-control"
|
||||||
|
data-class="{'is-invalid':!$password_valid,'is-valid':($password_valid&& $form1.confirm_password)}"
|
||||||
|
id="confirm_password"
|
||||||
|
name="confirm_password"
|
||||||
|
required>
|
||||||
|
<div class="invalid-feedback" data-show="!$password_valid">
|
||||||
|
{% trans "Password does not match. or length is less than 8 characters." %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab2" id="bootstrap-wizard-validation-tab2">
|
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab2" id="bootstrap-wizard-validation-tab2">
|
||||||
<form class="needs-validation" id="wizardValidationForm2" novalidate="novalidate" data-wizard-form="2">
|
<form class="needs-validation" id="wizardValidationForm2" novalidate="novalidate" data-wizard-form="2" data-ref-f2>
|
||||||
{{form2|crispy}}
|
<div class="mb-3">
|
||||||
|
<label for="name" class="form-label">{% trans "Name" %}</label>
|
||||||
|
<input data-bind-form2.name type="text" class="form-control" id="name" name="name" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="arabic_name" class="form-label">{% trans "Arabic Name" %}</label>
|
||||||
|
<input data-bind-form2.arabic_name type="text" class="form-control" id="arabic_name" name="arabic_name" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="phone_number" class="form-label">{% trans "Phone Number" %}</label><span data-show="!$phone_number_valid" class="text-danger">*</span>
|
||||||
|
<input data-bind-form2.phone_number type="tel" data-class="{'is-invalid':!$phone_number_valid}" class="form-control" id="phone_number" name="phone_number" required
|
||||||
|
data-on-input="$phone_number_valid = validate_sa_phone_number($form2.phone_number)"
|
||||||
|
>
|
||||||
|
<div class="invalid-feedback" data-show="!$phone_number_valid">
|
||||||
|
{% trans "Please enter a valid phone number" %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab3" id="bootstrap-wizard-validation-tab3">
|
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab3" id="bootstrap-wizard-validation-tab3">
|
||||||
<form class="needs-validation" id="wizardValidationForm3" novalidate="novalidate" data-wizard-form="3">
|
<form class="needs-validation" id="wizardValidationForm3" novalidate="novalidate" data-wizard-form="3" data-ref-f3>
|
||||||
{{form3|crispy}}
|
<div class="mb-3">
|
||||||
|
<label for="crn" class="form-label">{% trans "CRN" %}</label>
|
||||||
|
<input data-bind-form3.crn type="text" class="form-control" id="crn" name="crn" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="vrn" class="form-label">{% trans "VRN" %}</label>
|
||||||
|
<input data-bind-form3.vrn type="text" class="form-control" id="vrn" name="vrn" required>
|
||||||
|
</div>
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="address" class="form-label">{% trans "Address" %}</label>
|
||||||
|
<textarea data-bind-form3.address class="form-control" id="address" name="address" required></textarea>
|
||||||
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab4" id="bootstrap-wizard-validation-tab4">
|
<div class="tab-pane" role="tabpanel" aria-labelledby="bootstrap-wizard-validation-tab4" id="bootstrap-wizard-validation-tab4">
|
||||||
@ -62,18 +154,18 @@
|
|||||||
<div class="col-12 col-sm-auto">
|
<div class="col-12 col-sm-auto">
|
||||||
<div class="text-center text-sm-start">
|
<div class="text-center text-sm-start">
|
||||||
<h5 class="mb-3">{% trans 'You are all set!' %}</h5>
|
<h5 class="mb-3">{% trans 'You are all set!' %}</h5>
|
||||||
<p class="text-body-emphasis fs-9">{% trans 'Now you can access your account' %}<br>{% trans 'anytime' %} {% trans 'anywhere' %}</p><button class="btn btn-primary px-6" id='submit_btn'>{% trans 'Submit' %}</button>
|
<p class="text-body-emphasis fs-9">{% trans 'Now you can access your account' %}<br>{% trans 'anytime' %} {% trans 'anywhere' %}</p><button data-on-click="sendFormData()" class="btn btn-primary px-6" id='submit_btn'>{% trans 'Submit' %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer border-top-0" data-wizard-footer="data-wizard-footer">
|
<div data-computed-form1_valid="validatePassword($form1.password,$form1.confirm_password) && validateEmail($form1.email)" class="card-footer border-top-0" data-wizard-footer="data-wizard-footer">
|
||||||
<div class="d-flex pager wizard list-inline mb-0">
|
<div class="d-flex pager wizard list-inline mb-0">
|
||||||
<button class="d-none btn btn-link ps-0" type="button" data-wizard-prev-btn="data-wizard-prev-btn">{% trans 'Previous' %}</button>
|
<button class="d-none btn btn-link ps-0" type="button" data-wizard-prev-btn="data-wizard-prev-btn">{% trans 'Previous' %}</button>
|
||||||
<div class="flex-1 text-end">
|
<div class="flex-1 text-end">
|
||||||
<button class="btn btn-phoenix-primary px-6 px-sm-6 next" type="submit" data-wizard-next-btn="data-wizard-next-btn">{% trans 'Next' %}</button>
|
<button data-attr-disabled="!$form1_valid" data-attr-disabled="!$phone_number_valid" class="btn btn-phoenix-primary px-6 px-sm-6 next" type="button" id="next_btn" data-wizard-next-btn="data-wizard-next-btn">{% trans 'Next' %}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -84,167 +176,43 @@
|
|||||||
</section>
|
</section>
|
||||||
<section class="pt-lg-0 pt-xl-8">
|
<section class="pt-lg-0 pt-xl-8">
|
||||||
{% include 'footer.html' %}
|
{% include 'footer.html' %}
|
||||||
</section>
|
</section>
|
||||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||||
{% endblock content %}
|
{% endblock content %}
|
||||||
|
|
||||||
{% block customJS %}
|
{% block customJS %}
|
||||||
<script src="https://unpkg.com/just-validate@latest/dist/just-validate.production.min.js"></script>
|
<script src="{% static 'js/main.js' %}"></script>
|
||||||
|
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||||
|
|
||||||
|
<script type="module" src="https://cdn.jsdelivr.net/gh/starfederation/datastar@v1.0.0-beta.11/bundles/datastar.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const validator = new JustValidate('#wizardValidationForm1', {
|
function validatePassword(password, confirmPassword) {
|
||||||
validateBeforeSubmitting: true,
|
return password === confirmPassword && password.length > 7 && password !== '';
|
||||||
});
|
}
|
||||||
const validator1 = new JustValidate('#wizardValidationForm2', {
|
function validateEmail(email) {
|
||||||
validateBeforeSubmitting: true,
|
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||||
});
|
return emailRegex.test(email) && email !== '';
|
||||||
|
}
|
||||||
validator1.addField('#wizardValidationForm2 [name="phone_number"]', [
|
function validateform2(name,arabic_name,phone_number) {
|
||||||
{
|
if (name === '' || arabic_name === '' || phone_number === '' || phone_number.length < 10 || !phone_number.startsWith('056')) {
|
||||||
rule: 'required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rule: 'customRegexp',
|
|
||||||
value: /^05\d{8}$/,
|
|
||||||
errorMessage: '{% trans 'Please enter a valid phone number' %}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rule: 'maxLength',
|
|
||||||
value: 10,
|
|
||||||
errorMessage: '{% trans 'Please enter a valid phone number' %}',
|
|
||||||
},
|
|
||||||
|
|
||||||
]);
|
|
||||||
|
|
||||||
validator.addField('#wizardValidationForm1 [name="email"]', [
|
|
||||||
{
|
|
||||||
rule: 'required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rule: 'email',
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
validator.addField('#wizardValidationForm1 [name="password"]', [
|
|
||||||
{
|
|
||||||
rule: 'required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rule: 'minLength',
|
|
||||||
value: 6,
|
|
||||||
},
|
|
||||||
|
|
||||||
]);
|
|
||||||
validator.addField('#wizardValidationForm1 [name="confirm_password"]', [
|
|
||||||
{
|
|
||||||
rule: 'required',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rule: 'minLength',
|
|
||||||
value: 6,
|
|
||||||
errorMessage: '{% trans 'Password does not match' %}',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
validator: (value, context) => {
|
|
||||||
if (value !== document.querySelector('#id_password').value) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
validator.addField('#wizardValidationForm1 [name="terms"]', [
|
|
||||||
{
|
|
||||||
rule: 'required',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
validator = new JustValidate('#wizardValidationForm1', {
|
|
||||||
rules: {
|
|
||||||
email: {
|
|
||||||
required: true,
|
|
||||||
email: true,
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
required: true,
|
|
||||||
min: 6,
|
|
||||||
},
|
|
||||||
confirm_password: {
|
|
||||||
required: true,
|
|
||||||
min: 6,
|
|
||||||
equalTo: '#password',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
messages: {
|
|
||||||
name: {
|
|
||||||
required: 'Please enter your name',
|
|
||||||
},
|
|
||||||
email: {
|
|
||||||
required: 'Please enter your email',
|
|
||||||
email: 'Please enter a valid email address',
|
|
||||||
},
|
|
||||||
password: {
|
|
||||||
required: 'Please enter your password',
|
|
||||||
min: 'Password must be at least 6 characters',
|
|
||||||
},
|
|
||||||
confirm_password: {
|
|
||||||
required: 'Please confirm your password',
|
|
||||||
min: 'Password must be at least 6 characters',
|
|
||||||
equalTo: 'Passwords do not match',
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
})
|
return true
|
||||||
*/
|
}
|
||||||
const url = "{% url 'account_signup' %}";
|
function validate_sa_phone_number(phone_number) {
|
||||||
let submit_btn = document.getElementById('submit_btn');
|
const phone_numberRegex = /^056[0-9]{7}$/;
|
||||||
const csrftoken = getCookie('csrftoken');
|
return phone_numberRegex.test(phone_number) && phone_numberRegex !== '';
|
||||||
|
}
|
||||||
submit_btn.addEventListener('click', async () => {
|
|
||||||
const allFormData = getAllFormData();
|
|
||||||
|
|
||||||
try {
|
|
||||||
showLoading();
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'X-CSRFToken': csrftoken,
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify(allFormData),
|
|
||||||
});
|
|
||||||
hideLoading();
|
|
||||||
const data = await response.json();
|
|
||||||
if (response.ok) {
|
|
||||||
notify("success","Account created successfully");
|
|
||||||
setTimeout(() => {
|
|
||||||
window.location.href = "{% url 'account_login' %}";
|
|
||||||
}, 1000);
|
|
||||||
} else {
|
|
||||||
notify("error",data.error);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
notify("error",error);
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
function getAllFormData() {
|
function getAllFormData() {
|
||||||
const forms = document.querySelectorAll('form');
|
const forms = document.querySelectorAll('.needs-validation');
|
||||||
const formData = {};
|
const formData = {};
|
||||||
|
forms.forEach(form => {
|
||||||
forms.forEach((form, index) => {
|
const fields = form.querySelectorAll('input,textarea,select');
|
||||||
const formId = form.id || `form${index + 1}`;
|
fields.forEach(field => {
|
||||||
formData[formId] = {};
|
formData[field.name] = field.value;
|
||||||
|
});
|
||||||
const formElements = form.elements;
|
});
|
||||||
for (let element of formElements) {
|
return formData;
|
||||||
if (element.name) {
|
|
||||||
formData[formId][element.name] = element.value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return formData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function showLoading() {
|
function showLoading() {
|
||||||
@ -268,7 +236,7 @@
|
|||||||
titleText: msg
|
titleText: msg
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getCookie(name) {
|
function getCookie(name) {
|
||||||
let cookieValue = null;
|
let cookieValue = null;
|
||||||
if (document.cookie && document.cookie !== "") {
|
if (document.cookie && document.cookie !== "") {
|
||||||
const cookies = document.cookie.split(";");
|
const cookies = document.cookie.split(";");
|
||||||
@ -282,6 +250,33 @@
|
|||||||
}
|
}
|
||||||
return cookieValue;
|
return cookieValue;
|
||||||
}
|
}
|
||||||
|
async function sendFormData() {
|
||||||
|
const formData = getAllFormData();
|
||||||
|
const url = "{% url 'account_signup' %}";
|
||||||
|
const csrftoken = getCookie('csrftoken');
|
||||||
|
try {
|
||||||
|
showLoading();
|
||||||
|
const response = await fetch(url, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': '{{csrf_token}}',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body: JSON.stringify(formData),
|
||||||
|
});
|
||||||
|
hideLoading();
|
||||||
|
const data = await response.json();
|
||||||
|
if (response.ok) {
|
||||||
|
notify("success","Account created successfully");
|
||||||
|
setTimeout(() => {
|
||||||
|
window.location.href = "{% url 'account_login' %}";
|
||||||
|
}, 1000);
|
||||||
|
} else {
|
||||||
|
notify("error",data.error);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
notify("error",error);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{% endblock customJS %}
|
{% endblock customJS %}
|
||||||
@ -26,7 +26,6 @@
|
|||||||
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
||||||
<meta name="theme-color" content="#ffffff">
|
<meta name="theme-color" content="#ffffff">
|
||||||
|
|
||||||
|
|
||||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||||
<script src="{% static 'js/config.js' %}"></script>
|
<script src="{% static 'js/config.js' %}"></script>
|
||||||
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
<script src="{% static 'js/sweetalert2.all.min.js' %}"></script>
|
||||||
@ -51,7 +50,6 @@
|
|||||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
<script src="{% static 'js/jquery.min.js' %}"></script>
|
||||||
<script src="{% static 'js/echarts.js' %}"></script>
|
<script src="{% static 'js/echarts.js' %}"></script>
|
||||||
|
|
||||||
|
|
||||||
{% block customCSS %}
|
{% block customCSS %}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@ -96,7 +94,7 @@
|
|||||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
||||||
<!--2-->
|
<!--2-->
|
||||||
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
<script src="{% static 'vendors/fontawesome/all.min.js' %}"></script>
|
||||||
|
|
||||||
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
|
<script src="{% static 'vendors/lodash/lodash.min.js' %}"></script>
|
||||||
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
|
<script src="{% static 'vendors/list.js/list.min.js' %}"></script>
|
||||||
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
<script src="{% static 'vendors/feather-icons/feather.min.js' %}"></script>
|
||||||
|
|||||||
@ -5,125 +5,60 @@
|
|||||||
<div class="collapse navbar-collapse" id="navbarVerticalCollapse">
|
<div class="collapse navbar-collapse" id="navbarVerticalCollapse">
|
||||||
<div class="navbar-vertical-content">
|
<div class="navbar-vertical-content">
|
||||||
<ul class="navbar-nav flex-column" id="navbarVerticalNav">
|
<ul class="navbar-nav flex-column" id="navbarVerticalNav">
|
||||||
|
|
||||||
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<p class="navbar-vertical-label">Apps</p>
|
<p class="navbar-vertical-label">Apps</p>
|
||||||
<hr class="navbar-vertical-line" />
|
<hr class="navbar-vertical-line" />
|
||||||
{% if perms.inventory.can_view_inventory %}
|
{% if perms.inventory.can_view_inventory %}
|
||||||
<div class="nav-item-wrapper">
|
<div class="nav-item-wrapper">
|
||||||
<a id="inventory-nav" class="nav-link dropdown-indicator label-1 inventory-nav" href="#nv-inventory" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-inventory">
|
<a id="inventory-nav" class="nav-link dropdown-indicator label-1 inventory-nav" href="#nv-inventory" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-inventory">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
|
||||||
<span class="nav-link-icon"><span class="fas fa-warehouse"></span></span><span class="nav-link-text">{% trans "Inventory"|capfirst %}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
<div class="parent-wrapper label-1">
|
|
||||||
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-inventory">
|
|
||||||
<li class="collapsed-nav-item-title d-none">{% trans "Inventory"|capfirst %}</li>
|
|
||||||
{% if perms.inventory.add_car %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a id="btn-add-car" class="nav-link btn-add-car" href="{% url 'car_add' request.dealer.slug %}">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="nav-link-icon"><span class="fas fa-plus-circle"></span></span><span class="nav-link-text">{% trans "add car"|capfirst %}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if perms.inventory.view_car%}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'inventory_stats' request.dealer.slug %}">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="nav-link-icon"><span class="fas fa-car-side"></span></span><span class="nav-link-text">{% trans 'Cars'|capfirst %}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'car_list' request.dealer.slug %}">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="nav-link-icon"><span class="fas fa-car-side"></span></span><span class="nav-link-text">{% trans 'Stock'|capfirst %}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if perms.inventory.add_car %}
|
|
||||||
<li class="nav-item">
|
|
||||||
<a class="nav-link" href="{% url 'upload_cars' request.dealer.slug %}">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="nav-link-icon"><span class="fas fa-file-import"></span></span><span class="nav-link-text">{% trans "Bulk Upload"|capfirst %}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% if perms.django_ledger.can_view_sales %}
|
|
||||||
<div class="nav-item-wrapper">
|
|
||||||
<a class="nav-link dropdown-indicator label-1" href="#nv-sales" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-sales">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
||||||
<span class="nav-link-icon"><span data-feather="shopping-cart"></span></span><span class="nav-link-text">{% trans 'sales'|capfirst %}</span>
|
<span class="nav-link-icon"><span class="fas fa-warehouse"></span></span><span class="nav-link-text">{% trans "Inventory"|capfirst %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
<div class="parent-wrapper label-1">
|
<div class="parent-wrapper label-1">
|
||||||
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-sales">
|
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-inventory">
|
||||||
<li class="collapsed-nav-item-title d-none">{% trans 'sales'|capfirst %}</li>
|
<li class="collapsed-nav-item-title d-none">{% trans "Inventory"|capfirst %}</li>
|
||||||
{% if perms.django_ledger.add_estimatemodel %}
|
{% if perms.inventory.add_car %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'estimate_create' request.dealer.slug %}">
|
<a id="btn-add-car" class="nav-link btn-add-car" href="{% url 'car_add' request.dealer.slug %}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="nav-link-icon"><span class="fas fa-handshake"></span></span><span class="nav-link-text">{% trans "create quotation"|capfirst %}</span>
|
<span class="nav-link-icon"><span class="fas fa-plus-circle"></span></span><span class="nav-link-text">{% trans "add car"|capfirst %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.django_ledger.view_estimatemodel %}
|
|
||||||
<li class="nav-item">
|
{% if perms.inventory.view_car%}
|
||||||
<a class="nav-link" href="{% url 'estimate_list' request.dealer.slug %}">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="nav-link-icon"><span class="fas fa-handshake"></span></span><span class="nav-link-text">{% trans "quotations"|capfirst %}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
{%if perms.inventory.view_saleorder%}
|
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'sales_list' request.dealer.slug %}">
|
<a class="nav-link" href="{% url 'inventory_stats' request.dealer.slug %}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="nav-link-icon"><span class="fas fa-money-check"></span></span><span class="nav-link-text">{% trans "Sales Orders"|capfirst %}</span>
|
<span class="nav-link-icon"><span class="fas fa-car-side"></span></span><span class="nav-link-text">{% trans 'Cars'|capfirst %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'car_list' request.dealer.slug %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span class="fas fa-car-side"></span></span><span class="nav-link-text">{% trans 'Stock'|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if perms.django_ledger.view_invoicemodel %}
|
{% if perms.inventory.add_car %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'invoice_list' request.dealer.slug %}">
|
<a class="nav-link" href="{% url 'upload_cars' request.dealer.slug %}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="nav-link-icon"><span class="fas fa-file-invoice"></span></span><span class="nav-link-text">{% trans "invoices"|capfirst %}</span>
|
<span class="nav-link-icon"><span class="fas fa-file-import"></span></span><span class="nav-link-text">{% trans "Bulk Upload"|capfirst %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.inventory.view_payment %}
|
</ul>
|
||||||
<li class="nav-item">
|
</div>
|
||||||
<a class="nav-link" href="{% url 'payment_list' request.dealer.slug %}">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<span class="nav-link-icon"><span class="fas fa-money-check"></span></span><span class="nav-link-text">{% trans "payments"|capfirst %}</span>
|
|
||||||
</div>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if perms.inventory.can_view_crm %}
|
{% if perms.inventory.can_view_crm %}
|
||||||
@ -200,6 +135,69 @@
|
|||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
{% if perms.django_ledger.can_view_sales %}
|
||||||
|
<div class="nav-item-wrapper">
|
||||||
|
<a class="nav-link dropdown-indicator label-1" href="#nv-sales" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-sales">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<div class="dropdown-indicator-icon-wrapper"><span class="fas fa-caret-right dropdown-indicator-icon"></span></div>
|
||||||
|
<span class="nav-link-icon"><span data-feather="shopping-cart"></span></span><span class="nav-link-text">{% trans 'sales'|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<div class="parent-wrapper label-1">
|
||||||
|
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-sales">
|
||||||
|
<li class="collapsed-nav-item-title d-none">{% trans 'sales'|capfirst %}</li>
|
||||||
|
{% if perms.django_ledger.add_estimatemodel %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'estimate_create' request.dealer.slug %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span class="fas fa-handshake"></span></span><span class="nav-link-text">{% trans "create quotation"|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.django_ledger.view_estimatemodel %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'estimate_list' request.dealer.slug %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span class="fas fa-handshake"></span></span><span class="nav-link-text">{% trans "quotations"|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{%if perms.inventory.view_saleorder%}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'sales_list' request.dealer.slug %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span class="fas fa-money-check"></span></span><span class="nav-link-text">{% trans "Sales Orders"|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if perms.django_ledger.view_invoicemodel %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'invoice_list' request.dealer.slug %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span class="fas fa-file-invoice"></span></span><span class="nav-link-text">{% trans "invoices"|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
{% if perms.inventory.view_payment %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{% url 'payment_list' request.dealer.slug %}">
|
||||||
|
<div class="d-flex align-items-center">
|
||||||
|
<span class="nav-link-icon"><span class="fas fa-money-check"></span></span><span class="nav-link-text">{% trans "payments"|capfirst %}</span>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if perms.django_ledger.can_view_financials %}
|
{% if perms.django_ledger.can_view_financials %}
|
||||||
<div class="nav-item-wrapper">
|
<div class="nav-item-wrapper">
|
||||||
<a class="nav-link dropdown-indicator label-1" href="#nv-financial" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-financial">
|
<a class="nav-link dropdown-indicator label-1" href="#nv-financial" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-financial">
|
||||||
@ -225,11 +223,11 @@
|
|||||||
<a class="nav-link" href="{% url 'bank_account_list' request.dealer.slug %}">
|
<a class="nav-link" href="{% url 'bank_account_list' request.dealer.slug %}">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<span class="nav-link-icon"><span data-feather="credit-card"></span></span><span class="nav-link-text">{% trans 'Bank Accounts'|capfirst %}</span>
|
<span class="nav-link-icon"><span data-feather="credit-card"></span></span><span class="nav-link-text">{% trans 'Bank Accounts'|capfirst %}</span>
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if perms.django_ledger.view_journalentrymodel %}
|
{% if perms.django_ledger.view_journalentrymodel %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
<a class="nav-link" href="{% url 'ledger_list' request.dealer.slug %}">
|
<a class="nav-link" href="{% url 'ledger_list' request.dealer.slug %}">
|
||||||
@ -290,7 +288,7 @@
|
|||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if perms.django_ledger.can_view_reports %}
|
{% if perms.django_ledger.can_view_reports %}
|
||||||
<div class="nav-item-wrapper">
|
<div class="nav-item-wrapper">
|
||||||
<a class="nav-link dropdown-indicator label-1" href="#nv-reports" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-reports">
|
<a class="nav-link dropdown-indicator label-1" href="#nv-reports" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-reports">
|
||||||
|
|||||||
@ -164,6 +164,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Custom Card" %}</th>
|
<th>{% trans "Custom Card" %}</th>
|
||||||
<td>
|
<td>
|
||||||
|
{% if perms.inventory.add_customcard %}
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-phoenix-success"
|
class="btn btn-sm btn-phoenix-success"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
@ -172,6 +173,7 @@
|
|||||||
hx-target=".main-modal-body"
|
hx-target=".main-modal-body"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
>{% trans 'Add' %}</button>
|
>{% trans 'Add' %}</button>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -190,15 +192,16 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Registration" %}</th>
|
<th>{% trans "Registration" %}</th>
|
||||||
<td>
|
<td>
|
||||||
|
{% if perms.inventory.add_carregistration %}
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-sm btn-phoenix-success"
|
class="btn btn-sm btn-phoenix-success"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#mainModal"
|
data-bs-target="#mainModal"
|
||||||
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
|
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
|
||||||
hx-target=".main-modal-body"
|
hx-target=".main-modal-body"
|
||||||
hx-swap="innerHTML"
|
hx-swap="innerHTML"
|
||||||
>{% trans 'Add' %}</button>
|
>{% trans 'Add' %}</button>
|
||||||
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -212,14 +215,18 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
{{ car.location.showroom.get_local_name }}
|
{{ car.location.showroom.get_local_name }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<a href="{% url 'update_car_location' car.slug car.location.pk %}"
|
{% if perms.inventory.add_cartransfer %}
|
||||||
class="btn btn-phoenix-danger btn-sm">
|
<a href="{% url 'update_car_location' car.slug car.location.pk %}"
|
||||||
{% trans "transfer"|capfirst %}
|
class="btn btn-phoenix-danger btn-sm">
|
||||||
</a>
|
{% trans "transfer"|capfirst %}
|
||||||
|
</a>
|
||||||
|
{% endif %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% trans "No location available." %}
|
{% trans "No location available." %}
|
||||||
<a href="{% url 'add_car_location' car.slug %}"
|
{% if perms.inventory.add_carlocation %}
|
||||||
class="btn btn-phoenix-success btn-sm ms-2">{% trans "Add" %}</a>
|
<a href="{% url 'add_car_location' car.slug %}"
|
||||||
|
class="btn btn-phoenix-success btn-sm ms-2">{% trans "Add" %}</a>
|
||||||
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
@ -243,11 +250,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<div class="col-lg-6 col-xl-6">
|
<div class="col-lg-6 col-xl-6">
|
||||||
|
|
||||||
{% if perms.inventory.view_carfinance%}
|
{% if perms.inventory.view_carfinance%}
|
||||||
<div class="card rounded shadow d-flex align-content-center {% if car.get_transfer %}transfer{% endif %}">
|
<div class="card rounded shadow d-flex align-content-center {% if car.get_transfer %}transfer{% endif %}">
|
||||||
<p class="card-header rounded-top fw-bold">{% trans 'Financial Details' %}</p>
|
<p class="card-header rounded-top fw-bold">{% trans 'Financial Details' %}</p>
|
||||||
@ -296,7 +300,7 @@
|
|||||||
<a href="{% url 'car_finance_update' request.dealer.slug car.finances.pk %}" class="btn btn-phoenix-warning btn-sm mb-3">{% trans "Edit" %}</a>
|
<a href="{% url 'car_finance_update' request.dealer.slug car.finances.pk %}" class="btn btn-phoenix-warning btn-sm mb-3">{% trans "Edit" %}</a>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span>
|
<span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -342,7 +346,7 @@
|
|||||||
style="background-color: rgb({{ car.colors.interior.rgb }})"></div>
|
style="background-color: rgb({{ car.colors.interior.rgb }})"></div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{% if perms.inventory.change_carcolors%}
|
{% if perms.inventory.change_carcolors%}
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="2">
|
<td colspan="2">
|
||||||
@ -433,7 +437,7 @@
|
|||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</table>
|
</table>
|
||||||
@ -501,7 +505,7 @@
|
|||||||
alt="">
|
alt="">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Custom Card Modal -->
|
<!-- Custom Card Modal -->
|
||||||
<div class="modal fade"
|
<div class="modal fade"
|
||||||
id="customCardModal"
|
id="customCardModal"
|
||||||
|
|||||||
@ -19,7 +19,7 @@
|
|||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<!-- Basic Information -->
|
<!-- Basic Information -->
|
||||||
<div class="row mb-4">
|
<div class="row mb-4">
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<h4>{% trans "Customer Information" %}</h4>
|
<h4>{% trans "Customer Information" %}</h4>
|
||||||
<p>
|
<p>
|
||||||
<strong>{% trans "Name" %}:</strong> {{ sale_order.full_name }}<br>
|
<strong>{% trans "Name" %}:</strong> {{ sale_order.full_name }}<br>
|
||||||
@ -30,7 +30,7 @@
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-md-6">
|
<div class="col-md-4">
|
||||||
<h4>{% trans "Order Details" %}</h4>
|
<h4>{% trans "Order Details" %}</h4>
|
||||||
<p>
|
<p>
|
||||||
<strong>{% trans "Order Date" %}:</strong> {{ sale_order.order_date|date }}<br>
|
<strong>{% trans "Order Date" %}:</strong> {{ sale_order.order_date|date }}<br>
|
||||||
@ -38,6 +38,30 @@
|
|||||||
<strong>{% trans "Created By" %}:</strong> {{ sale_order.created_by }}
|
<strong>{% trans "Created By" %}:</strong> {{ sale_order.created_by }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{% if not prems.inventory.change_saleorder %}
|
||||||
|
<div class="col-md-4 row mb-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<h4>{% trans "Update Order Status" %}</h4>
|
||||||
|
<form method="post" action="">
|
||||||
|
{% csrf_token %}
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="status" class="form-label">{% trans "Status" %}</label>
|
||||||
|
<select class="form-select" id="status" name="status">
|
||||||
|
<option value="PENDING_APPROVAL" {% if sale_order.status == 'PENDING_APPROVAL' %}selected{% endif %}>{% trans "Pending Approval" %}</option>
|
||||||
|
<option value="APPROVED" {% if sale_order.status == 'APPROVED' %}selected{% endif %}>{% trans "Approved" %}</option>
|
||||||
|
<option value="IN_FINANCING" {% if sale_order.status == 'IN_FINANCING' %}selected{% endif %}>{% trans "In Financing" %}</option>
|
||||||
|
<option value="PARTIALLY_PAID" {% if sale_order.status == 'PARTIALLY_PAID' %}selected{% endif %}>{% trans "Partially Paid" %}</option>
|
||||||
|
<option value="FULLY_PAID" {% if sale_order.status == 'FULLY_PAID' %}selected{% endif %}>{% trans "Fully Paid" %}</option>
|
||||||
|
<option value="PENDING_DELIVERY" {% if sale_order.status == 'PENDING_DELIVERY' %}selected{% endif %}>{% trans "Pending Delivery" %}</option>
|
||||||
|
<option value="DELIVERED" {% if sale_order.status == 'DELIVERED' %}selected{% endif %}>{% trans "Delivered" %}</option>
|
||||||
|
<option value="CANCELLED" {% if sale_order.status == 'CANCELLED' %}selected{% endif %}>{% trans "Cancelled" %}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary">{% trans "Save" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Estimate Information -->
|
<!-- Estimate Information -->
|
||||||
|
|||||||
@ -109,11 +109,6 @@
|
|||||||
{{ _("Customer Name")}}</th>
|
{{ _("Customer Name")}}</th>
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="customer_address" style="width:5%;">{{ _("Customer Address")}}</th>
|
<th class="sort align-middle ps-3" scope="col" data-sort="customer_address" style="width:5%;">{{ _("Customer Address")}}</th>
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="customer_phone" style="width:5%;">{{ _("Customer Phone")}}</th>
|
<th class="sort align-middle ps-3" scope="col" data-sort="customer_phone" style="width:5%;">{{ _("Customer Phone")}}</th>
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="make" style="width:5%;">{{ _("Make") }}</th>
|
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="model" style="width:5%;">{{ _("Model") }}</th>
|
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="vin" style="width:5%;">{{ _("VIN") }}</th>
|
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="trim" style="width:5%;">{{ _("Trim") }}</th>
|
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="price" style="width:5%;">{{ _("Price") }}</th>
|
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="quotation" style="width:7%;">{{ _("Quotation") }}</th>
|
<th class="sort align-middle ps-3" scope="col" data-sort="quotation" style="width:7%;">{{ _("Quotation") }}</th>
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="invoice" style="width:7%;">{{ _("Invoice") }}</th>
|
<th class="sort align-middle ps-3" scope="col" data-sort="invoice" style="width:7%;">{{ _("Invoice") }}</th>
|
||||||
<th class="sort align-middle ps-3" scope="col" data-sort="status" style="width:7%;">{{ _("Status") }}</th>
|
<th class="sort align-middle ps-3" scope="col" data-sort="status" style="width:7%;">{{ _("Status") }}</th>
|
||||||
@ -125,7 +120,7 @@
|
|||||||
{% for tx in txs %}
|
{% for tx in txs %}
|
||||||
<tr class="position-static">
|
<tr class="position-static">
|
||||||
<td class="align-middle white-space-nowrap customer_name px-1">
|
<td class="align-middle white-space-nowrap customer_name px-1">
|
||||||
<p class="mb-0 fs-9 text-body">{{tx.customer}}</p>
|
<p class="mb-0 fs-9 text-body">{{tx}}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle white-space-nowrap customer_address">
|
<td class="align-middle white-space-nowrap customer_address">
|
||||||
<p class="mb-0 fs-9 text-body">{{tx.customer.address}}</p>
|
<p class="mb-0 fs-9 text-body">{{tx.customer.address}}</p>
|
||||||
@ -133,24 +128,11 @@
|
|||||||
<td class="align-middle white-space-nowrap customer_phone">
|
<td class="align-middle white-space-nowrap customer_phone">
|
||||||
<p class="mb-0 fs-9 text-body">{{tx.customer.phone_number}}</p>
|
<p class="mb-0 fs-9 text-body">{{tx.customer.phone_number}}</p>
|
||||||
</td>
|
</td>
|
||||||
<td class="align-middle time white-space-nowrap make">{{tx.info.make}}</td>
|
|
||||||
<td class="align-middle white-space-nowrap model">
|
|
||||||
<p class="mb-0 fs-9 text-body">{{tx.info.model}}</p>
|
|
||||||
</td>
|
|
||||||
<td class="align-middle white-space-nowrap vin">
|
|
||||||
<p class="mb-0 fs-9 text-body">{{tx.info.vin}}</p>
|
|
||||||
</td>
|
|
||||||
<td class="align-middle white-space-nowrap trim">
|
|
||||||
<p class="fw-bo text-body fs-9 mb-0">{{tx.info.trim}}</p>
|
|
||||||
</td>
|
|
||||||
<td class="align-middle white-space-nowrap price">
|
|
||||||
<p class="fw-bo text-body fs-9 mb-0">{{tx.finance.total}}</p>
|
|
||||||
</td>
|
|
||||||
<td class="align-middle white-space-nowrap quotation">
|
<td class="align-middle white-space-nowrap quotation">
|
||||||
{% if tx.estimate %}
|
{% if tx.estimate %}
|
||||||
<p class="fw-bo text-body fs-9 mb-0">
|
<p class="fw-bo text-body fs-9 mb-0">
|
||||||
<a href="{% url 'estimate_detail' request.dealer.slug tx.estimate.uuid %}">
|
<a href="{% url 'estimate_detail' request.dealer.slug tx.estimate.uuid %}">
|
||||||
{{tx.estimate.estimate_number}}
|
{{ tx.estimate.estimate_number}}
|
||||||
</a><br>
|
</a><br>
|
||||||
{% if tx.estimate.status == "draft" %}
|
{% if tx.estimate.status == "draft" %}
|
||||||
<span class="badge badge-phoenix badge-phoenix-warning">{{tx.estimate.status}}</span>
|
<span class="badge badge-phoenix badge-phoenix-warning">{{tx.estimate.status}}</span>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user