update the signup view and other perms changes
This commit is contained in:
parent
d6f4ebfd4e
commit
ae4d981d37
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
|
||||
|
||||
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()
|
||||
######################################
|
||||
######################################
|
||||
@ -2698,7 +2689,6 @@ class CustomGroup(models.Model):
|
||||
"view_carcolors",
|
||||
"view_cartransfer",
|
||||
"view_saleorder",
|
||||
|
||||
],
|
||||
)
|
||||
self.set_permissions(
|
||||
@ -2966,4 +2956,22 @@ class ExtraInfo(models.Model):
|
||||
verbose_name_plural = "Extra Info"
|
||||
|
||||
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_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.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):
|
||||
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)
|
||||
|
||||
|
||||
# @background
|
||||
def long_running_task(duration):
|
||||
"""Example background task"""
|
||||
print("Task completed")
|
||||
return 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)
|
||||
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 secrets
|
||||
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
|
||||
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_ledger.io import roles
|
||||
from django.contrib import messages
|
||||
from django.shortcuts import redirect
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
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_q.tasks import async_task
|
||||
from django.core.mail import send_mail
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_ledger.models.items import ItemModel
|
||||
from plans.models import AbstractOrder
|
||||
from django_ledger.models import (
|
||||
InvoiceModel,
|
||||
BillModel,
|
||||
VendorModel,
|
||||
)
|
||||
from django_ledger.models.items import ItemModel
|
||||
from django.utils.translation import get_language
|
||||
from appointment.models import StaffMember
|
||||
from django.contrib.auth.models import User
|
||||
from django_q.tasks import async_task
|
||||
import secrets
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_ledger.models.transactions import TransactionModel
|
||||
from django_ledger.models.journal_entry import JournalEntryModel
|
||||
|
||||
|
||||
def make_random_password(
|
||||
@ -1340,31 +1336,6 @@ def create_make_accounts(dealer):
|
||||
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):
|
||||
url = "https://api.moyasar.com/v1/payments"
|
||||
callback_url = request.build_absolute_uri(reverse("payment_callback", args=[request.dealer.slug]))
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
# Standard
|
||||
import os
|
||||
import re
|
||||
import io
|
||||
import csv
|
||||
import cv2
|
||||
@ -48,8 +49,7 @@ from django.shortcuts import HttpResponse
|
||||
|
||||
from django.db.models import Sum, F, Count
|
||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import Group
|
||||
from django.contrib.auth.models import User,Group
|
||||
from django.db.models import Value
|
||||
from django.urls import reverse, reverse_lazy
|
||||
from django.utils import timezone, translation
|
||||
@ -183,7 +183,6 @@ from .services import (
|
||||
)
|
||||
from .utils import (
|
||||
CarFinanceCalculator,
|
||||
create_user_dealer,
|
||||
get_car_finance_data,
|
||||
get_item_transactions,
|
||||
handle_payment,
|
||||
@ -194,11 +193,11 @@ from .utils import (
|
||||
set_invoice_payment,
|
||||
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
|
||||
from easyaudit.models import RequestEvent, CRUDEvent, LoginEvent
|
||||
|
||||
from django_q.tasks import async_task
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
@ -290,47 +289,43 @@ def dealer_signup(request):
|
||||
or failure.
|
||||
:rtype: Union[django.http.HttpResponse, django.http.JsonResponse]
|
||||
"""
|
||||
form1 = forms.WizardForm1()
|
||||
form2 = forms.WizardForm2()
|
||||
form3 = forms.WizardForm3()
|
||||
|
||||
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)
|
||||
wf1 = data.get("wizardValidationForm1")
|
||||
wf2 = data.get("wizardValidationForm2")
|
||||
wf3 = data.get("wizardValidationForm3")
|
||||
email = wf1.get("email")
|
||||
password = wf1.get("password")
|
||||
password_confirm = wf1.get("confirm_password")
|
||||
name = wf2.get("name")
|
||||
arabic_name = wf2.get("arabic_name")
|
||||
phone = wf2.get("phone_number")
|
||||
crn = wf3.get("crn")
|
||||
vrn = wf3.get("vrn")
|
||||
address = wf3.get("address")
|
||||
|
||||
email = data.get("email")
|
||||
password = data.get("password")
|
||||
password_confirm = data.get("confirm_password")
|
||||
name = data.get("name")
|
||||
arabic_name = data.get("arabic_name")
|
||||
phone = data.get("phone_number")
|
||||
crn = data.get("crn")
|
||||
vrn = data.get("vrn")
|
||||
address = data.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:
|
||||
return JsonResponse({"error": _("Passwords do not match")}, status=400)
|
||||
try:
|
||||
#TODO make this a django-q task
|
||||
create_user_dealer(
|
||||
async_task(create_user_dealer(
|
||||
email, password, name, arabic_name, phone, crn, vrn, address
|
||||
)
|
||||
))
|
||||
return JsonResponse({"message": _("User created successfully")}, status=200)
|
||||
except Exception as e:
|
||||
return JsonResponse({"error": str(e)}, status=400)
|
||||
return render(
|
||||
request,
|
||||
"account/signup-wizard.html",
|
||||
{"form1": form1, "form2": form2, "form3": form3},
|
||||
)
|
||||
|
||||
|
||||
@ -1165,7 +1160,7 @@ def inventory_stats_view(request, dealer_slug):
|
||||
"inventory/inventory_stats.html" template.
|
||||
:rtype: HttpResponse
|
||||
"""
|
||||
|
||||
|
||||
# Base queryset for cars belonging to the dealer
|
||||
cars = models.Car.objects.filter(dealer=request.dealer)
|
||||
|
||||
@ -4122,11 +4117,20 @@ def sales_list_view(request, dealer_slug):
|
||||
"""
|
||||
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
||||
entity = dealer.entity
|
||||
|
||||
sale_orders = models.SaleOrder.objects.filter(
|
||||
dealer=dealer,
|
||||
)
|
||||
paginator = Paginator(sale_orders, 30)
|
||||
staff = getattr(request.user.staffmember, "staff", None)
|
||||
qs = []
|
||||
try:
|
||||
if dealer:
|
||||
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_obj = paginator.get_page(page_number)
|
||||
|
||||
@ -9272,7 +9276,7 @@ def permenant_delete_account(request,dealer_slug, content_type, slug):
|
||||
def PurchaseOrderCreateView(request, dealer_slug,entity_slug):
|
||||
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
|
||||
entity = dealer.entity
|
||||
|
||||
|
||||
if request.method == "POST":
|
||||
try:
|
||||
po = entity.create_purchase_order(po_title=request.POST.get("po_title"))
|
||||
|
||||
@ -26,6 +26,8 @@ python3 manage.py tenhal_plan
|
||||
|
||||
python3 manage.py set_vat
|
||||
|
||||
python3 manage.py set_custom_permissions
|
||||
|
||||
python3 manage.py initial_services_offered
|
||||
|
||||
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>
|
||||
</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 ">
|
||||
<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">
|
||||
@ -37,21 +49,101 @@
|
||||
</ul>
|
||||
</div>
|
||||
<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">
|
||||
<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 class="needs-validation" id="wizardValidationForm1" novalidate="novalidate" data-wizard-form="1" data-ref-f1>
|
||||
<div class="mb-3">
|
||||
<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>
|
||||
</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 class="needs-validation" id="wizardValidationForm2" novalidate="novalidate" data-wizard-form="2" data-ref-f2>
|
||||
<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>
|
||||
</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 class="needs-validation" id="wizardValidationForm3" novalidate="novalidate" data-wizard-form="3" data-ref-f3>
|
||||
<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>
|
||||
</div>
|
||||
<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="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>
|
||||
<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 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">
|
||||
<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>
|
||||
<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>
|
||||
@ -84,167 +176,43 @@
|
||||
</section>
|
||||
<section class="pt-lg-0 pt-xl-8">
|
||||
{% include 'footer.html' %}
|
||||
</section>
|
||||
<script src="{% static 'js/phoenix.js' %}"></script>
|
||||
{% endblock content %}
|
||||
</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>
|
||||
{% block customJS %}
|
||||
<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>
|
||||
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) {
|
||||
function validatePassword(password, confirmPassword) {
|
||||
return password === confirmPassword && password.length > 7 && password !== '';
|
||||
}
|
||||
function validateEmail(email) {
|
||||
const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
|
||||
return emailRegex.test(email) && email !== '';
|
||||
}
|
||||
function validateform2(name,arabic_name,phone_number) {
|
||||
if (name === '' || arabic_name === '' || phone_number === '' || phone_number.length < 10 || !phone_number.startsWith('056')) {
|
||||
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);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
return true
|
||||
}
|
||||
function validate_sa_phone_number(phone_number) {
|
||||
const phone_numberRegex = /^056[0-9]{7}$/;
|
||||
return phone_numberRegex.test(phone_number) && phone_numberRegex !== '';
|
||||
}
|
||||
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;
|
||||
const forms = document.querySelectorAll('.needs-validation');
|
||||
const formData = {};
|
||||
forms.forEach(form => {
|
||||
const fields = form.querySelectorAll('input,textarea,select');
|
||||
fields.forEach(field => {
|
||||
formData[field.name] = field.value;
|
||||
});
|
||||
});
|
||||
return formData;
|
||||
}
|
||||
|
||||
function showLoading() {
|
||||
@ -268,7 +236,7 @@
|
||||
titleText: msg
|
||||
});
|
||||
}
|
||||
function getCookie(name) {
|
||||
function getCookie(name) {
|
||||
let cookieValue = null;
|
||||
if (document.cookie && document.cookie !== "") {
|
||||
const cookies = document.cookie.split(";");
|
||||
@ -282,6 +250,33 @@
|
||||
}
|
||||
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>
|
||||
|
||||
{% endblock customJS %}
|
||||
@ -26,7 +26,6 @@
|
||||
<meta name="msapplication-TileImage" content="{% static 'images/logos/logo-d.png' %}">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
|
||||
<script src="{% static 'vendors/simplebar/simplebar.min.js' %}"></script>
|
||||
<script src="{% static 'js/config.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/echarts.js' %}"></script>
|
||||
|
||||
|
||||
{% block customCSS %}
|
||||
|
||||
{% endblock %}
|
||||
@ -96,7 +94,7 @@
|
||||
<script src="{% static 'vendors/is/is.min.js' %}"></script>
|
||||
<!--2-->
|
||||
<script src="{% static 'vendors/fontawesome/all.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/feather-icons/feather.min.js' %}"></script>
|
||||
|
||||
@ -5,125 +5,60 @@
|
||||
<div class="collapse navbar-collapse" id="navbarVerticalCollapse">
|
||||
<div class="navbar-vertical-content">
|
||||
<ul class="navbar-nav flex-column" id="navbarVerticalNav">
|
||||
|
||||
|
||||
<li class="nav-item">
|
||||
<p class="navbar-vertical-label">Apps</p>
|
||||
<hr class="navbar-vertical-line" />
|
||||
{% if perms.inventory.can_view_inventory %}
|
||||
<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">
|
||||
<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="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">
|
||||
<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>
|
||||
<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-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>
|
||||
<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>
|
||||
</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%}
|
||||
|
||||
{% if perms.inventory.view_car%}
|
||||
<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">
|
||||
<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>
|
||||
</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>
|
||||
{% 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>
|
||||
{% 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 %}
|
||||
{% 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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if perms.inventory.can_view_crm %}
|
||||
@ -200,6 +135,69 @@
|
||||
</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>
|
||||
<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 %}
|
||||
<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">
|
||||
@ -225,11 +223,11 @@
|
||||
<a class="nav-link" href="{% url 'bank_account_list' request.dealer.slug %}">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
|
||||
|
||||
{% if perms.django_ledger.view_journalentrymodel %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'ledger_list' request.dealer.slug %}">
|
||||
@ -290,7 +288,7 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% if perms.django_ledger.can_view_reports %}
|
||||
<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">
|
||||
|
||||
@ -164,6 +164,7 @@
|
||||
<tr>
|
||||
<th>{% trans "Custom Card" %}</th>
|
||||
<td>
|
||||
{% if perms.inventory.add_customcard %}
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-phoenix-success"
|
||||
data-bs-toggle="modal"
|
||||
@ -172,6 +173,7 @@
|
||||
hx-target=".main-modal-body"
|
||||
hx-swap="innerHTML"
|
||||
>{% trans 'Add' %}</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
@ -190,15 +192,16 @@
|
||||
<tr>
|
||||
<th>{% trans "Registration" %}</th>
|
||||
<td>
|
||||
{% if perms.inventory.add_carregistration %}
|
||||
<button type="button"
|
||||
class="btn btn-sm btn-phoenix-success"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#mainModal"
|
||||
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
|
||||
hx-target=".main-modal-body"
|
||||
hx-swap="innerHTML"
|
||||
class="btn btn-sm btn-phoenix-success"
|
||||
data-bs-toggle="modal"
|
||||
data-bs-target="#mainModal"
|
||||
hx-get="{% url 'add_registration' request.dealer.slug car.slug %}"
|
||||
hx-target=".main-modal-body"
|
||||
hx-swap="innerHTML"
|
||||
>{% trans 'Add' %}</button>
|
||||
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
@ -212,14 +215,18 @@
|
||||
{% else %}
|
||||
{{ car.location.showroom.get_local_name }}
|
||||
{% endif %}
|
||||
<a href="{% url 'update_car_location' car.slug car.location.pk %}"
|
||||
class="btn btn-phoenix-danger btn-sm">
|
||||
{% trans "transfer"|capfirst %}
|
||||
</a>
|
||||
{% if perms.inventory.add_cartransfer %}
|
||||
<a href="{% url 'update_car_location' car.slug car.location.pk %}"
|
||||
class="btn btn-phoenix-danger btn-sm">
|
||||
{% trans "transfer"|capfirst %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
{% trans "No location available." %}
|
||||
<a href="{% url 'add_car_location' car.slug %}"
|
||||
class="btn btn-phoenix-success btn-sm ms-2">{% trans "Add" %}</a>
|
||||
{% if perms.inventory.add_carlocation %}
|
||||
<a href="{% url 'add_car_location' car.slug %}"
|
||||
class="btn btn-phoenix-success btn-sm ms-2">{% trans "Add" %}</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
@ -243,11 +250,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="col-lg-6 col-xl-6">
|
||||
|
||||
{% if perms.inventory.view_carfinance%}
|
||||
<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>
|
||||
@ -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>
|
||||
{% else %}
|
||||
<span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
@ -342,7 +346,7 @@
|
||||
style="background-color: rgb({{ car.colors.interior.rgb }})"></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
||||
{% if perms.inventory.change_carcolors%}
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
@ -433,7 +437,7 @@
|
||||
</td>
|
||||
</tr>
|
||||
{% endif %}
|
||||
|
||||
|
||||
</tbody>
|
||||
{% endif %}
|
||||
</table>
|
||||
@ -501,7 +505,7 @@
|
||||
alt="">
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Custom Card Modal -->
|
||||
<div class="modal fade"
|
||||
id="customCardModal"
|
||||
|
||||
@ -109,11 +109,6 @@
|
||||
{{ _("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_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="invoice" style="width:7%;">{{ _("Invoice") }}</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 %}
|
||||
<tr class="position-static">
|
||||
<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 class="align-middle white-space-nowrap customer_address">
|
||||
<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">
|
||||
<p class="mb-0 fs-9 text-body">{{tx.customer.phone_number}}</p>
|
||||
</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">
|
||||
{% if tx.estimate %}
|
||||
<p class="fw-bo text-body fs-9 mb-0">
|
||||
<a href="{% url 'estimate_detail' request.dealer.slug tx.estimate.uuid %}">
|
||||
{{tx.estimate.estimate_number}}
|
||||
{{ tx.estimate.estimate_number}}
|
||||
</a><br>
|
||||
{% if tx.estimate.status == "draft" %}
|
||||
<span class="badge badge-phoenix badge-phoenix-warning">{{tx.estimate.status}}</span>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user