Compare commits

...

6 Commits

Author SHA1 Message Date
85efc27bf1 update 2025-07-06 12:54:14 +03:00
8a073d8df8 update 2025-07-06 12:52:32 +03:00
ae4d981d37 update the signup view and other perms changes 2025-07-06 12:47:33 +03:00
7a9b9901d5 update 2025-07-06 12:47:20 +03:00
d6f4ebfd4e update the saleorder 2025-07-03 15:57:09 +03:00
37661aa50e Merge pull request 'Permissions' (#106) from frontend into main
Reviewed-on: #106
2025-07-03 15:56:08 +03:00
22 changed files with 1323 additions and 436 deletions

View 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))

View File

@ -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(
@ -2967,3 +2957,21 @@ class ExtraInfo(models.Model):
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]

View File

@ -24,6 +24,10 @@ from django.utils.timezone import now
from django.db import transaction from django.db import transaction
from django_q.tasks import async_task from django_q.tasks import async_task
#logging
import logging
logger=logging.getLogger(__name__)
User = get_user_model() User = get_user_model()
# @receiver(post_save, sender=models.SaleQuotation) # @receiver(post_save, sender=models.SaleQuotation)
@ -88,7 +92,15 @@ def create_car_location(sender, instance, created, **kwargs):
""" """
try: try:
if created: if created:
# Log that the signal was triggered for a new car
logger.debug(f"Post-save signal triggered for new Car (VIN: {instance.vin}). Attempting to create CarLocation.")
if instance.dealer is None: if instance.dealer is None:
# Log the critical data integrity error before raising
logger.error(
f"Attempted to create CarLocation for car (VIN: {instance.vin}) "
f"but 'dealer' field is missing. This indicates a data integrity issue or unhandled logic."
)
raise ValueError( raise ValueError(
f"Cannot create CarLocation for car {instance.vin}: dealer is missing." f"Cannot create CarLocation for car {instance.vin}: dealer is missing."
) )
@ -99,7 +111,18 @@ def create_car_location(sender, instance, created, **kwargs):
showroom=instance.dealer, showroom=instance.dealer,
description=f"Initial location set for car {instance.vin}.", description=f"Initial location set for car {instance.vin}.",
) )
# Log successful CarLocation creation
logger.info(
f"Successfully created CarLocation for new car (VIN: {instance.vin}) "
f"with owner '{instance.dealer.name}' (ID: {instance.dealer.pk})."
)
except Exception as e: except Exception as e:
# --- Single-line log for general error during CarLocation creation ---
logger.error(
f"Failed to create CarLocation for car (VIN: {instance.vin}). "
f"An unexpected error occurred: {e}",
exc_info=True
)
print(f"Failed to create CarLocation for car {instance.vin}: {e}") print(f"Failed to create CarLocation for car {instance.vin}: {e}")
@ -517,6 +540,8 @@ def track_lead_status_change(sender, instance, **kwargs):
:return: None :return: None
""" """
if instance.pk: # Ensure the instance is being updated, not created if instance.pk: # Ensure the instance is being updated, not created
# Log that a lead update is being checked for status changes
logger.debug(f"Checking for status change on Lead ID: {instance.pk}.")
try: try:
old_lead = models.Lead.objects.get(pk=instance.pk) old_lead = models.Lead.objects.get(pk=instance.pk)
if old_lead.status != instance.status: # Check if status has changed if old_lead.status != instance.status: # Check if status has changed
@ -526,7 +551,17 @@ def track_lead_status_change(sender, instance, **kwargs):
new_status=instance.status, new_status=instance.status,
changed_by=instance.staff, # Assuming the assigned staff made the change changed_by=instance.staff, # Assuming the assigned staff made the change
) )
# --- Single-line log for successful status change and history creation ---
logger.info(
f"Lead ID: {instance.pk} status changed from '{old_lead.status}' to '{instance.status}'. "
f"LeadStatusHistory recorded by Staff: {instance.staff.username if instance.staff else 'N/A'}."
)
except models.Lead.DoesNotExist: except models.Lead.DoesNotExist:
# --- Single-line log for expected Lead.DoesNotExist (e.g., during initial object creation) ---
logger.debug(
f"Lead ID: {instance.pk} not found in database when checking for status change. "
f"This might occur during initial object creation. Skipping status history tracking."
)
pass # Ignore if the lead doesn't exist (e.g., during initial creation) pass # Ignore if the lead doesn't exist (e.g., during initial creation)

View File

@ -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,
)

View File

@ -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]))

File diff suppressed because it is too large Load Diff

View File

@ -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"

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View 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 %}

View File

@ -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 %}

View File

@ -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 %}

View File

@ -63,10 +63,12 @@
class="btn btn-sm btn-phoenix-primary me-md-2"> class="btn btn-sm btn-phoenix-primary me-md-2">
{% trans 'View' %} {% trans 'View' %}
</a> </a>
{% if perms.django_ledger.change_billmodel %}
<a href="{% url 'django_ledger:bill-update' entity_slug=entity_slug bill_pk=bill.uuid %}" <a href="{% url 'django_ledger:bill-update' entity_slug=entity_slug bill_pk=bill.uuid %}"
class="btn btn-sm btn-phoenix-warning me-md-2"> class="btn btn-sm btn-phoenix-warning me-md-2">
{% trans 'Update' %} {% trans 'Update' %}
</a> </a>
{% if bill.can_pay %} {% if bill.can_pay %}
<button onclick="djLedger.toggleModal('{{ bill.get_html_id }}')" <button onclick="djLedger.toggleModal('{{ bill.get_html_id }}')"
@ -80,6 +82,7 @@
{% trans 'Cancel' %} {% trans 'Cancel' %}
</button> </button>
{% endif %} {% endif %}
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -201,9 +204,10 @@
<div class="card-footer p-0"> <div class="card-footer p-0">
<div class="d-flex flex-wrap gap-2 mt-2"> <div class="d-flex flex-wrap gap-2 mt-2">
<!-- Update Button --> <!-- Update Button -->
{% if perms.django_ledger.change_billmodel%}
<a href="{% url 'bill-update' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill.uuid %}" class="btn btn-phoenix-primary"> <a href="{% url 'bill-update' dealer_slug=dealer_slug entity_slug=entity_slug bill_pk=bill.uuid %}" class="btn btn-phoenix-primary">
<i class="fas fa-edit me-2"></i>{% trans 'Update' %} <i class="fas fa-edit me-2"></i>{% trans 'Update' %}
</a>
<!-- Mark as Draft --> <!-- Mark as Draft -->
{% if bill.can_draft %} {% if bill.can_draft %}
<button class="btn btn-phoenix-success" <button class="btn btn-phoenix-success"
@ -247,6 +251,7 @@
</button> </button>
{% modal_action_v2 bill bill.get_mark_as_canceled_url bill.get_mark_as_canceled_message bill.get_mark_as_canceled_html_id %} {% modal_action_v2 bill bill.get_mark_as_canceled_url bill.get_mark_as_canceled_message bill.get_mark_as_canceled_html_id %}
{% endif %} {% endif %}
{% endif %}
</div> </div>
</div> </div>
</div> </div>
@ -254,6 +259,7 @@
{% endif %} {% endif %}
{% else %} {% else %}
<!-- Create Bill Card --> <!-- Create Bill Card -->
{% if perms.django_ledger.add_billmodel%}
<div class=" bg-light"> <div class=" bg-light">
<div class="card-body text-center p-5"> <div class="card-body text-center p-5">
<a href="{% url 'django_ledger:bill-create' entity_slug=entity_slug %}" <a href="{% url 'django_ledger:bill-create' entity_slug=entity_slug %}"
@ -263,6 +269,7 @@
</a> </a>
</div> </div>
</div> </div>
{% endif %}
{% endif %} {% endif %}
</div> </div>

View File

@ -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">
@ -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">

View File

@ -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>
@ -244,10 +251,7 @@
</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>

View File

@ -9,7 +9,9 @@
<div class="d-flex justify-content-between mb-2"> <div class="d-flex justify-content-between mb-2">
<h3 class="">{% trans "Expenses" %}</h3> <h3 class="">{% trans "Expenses" %}</h3>
{% if perms.django_ledger.add_itemmodel %}
<a href="{% url 'item_expense_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Expense" %}</a> <a href="{% url 'item_expense_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Expense" %}</a>
{% endif %}
</div> </div>
{% include "partials/search_box.html" %} {% include "partials/search_box.html" %}
{% if page_obj.object_list %} {% if page_obj.object_list %}
@ -39,10 +41,12 @@
{{ expense.uom }} {{ expense.uom }}
</td> </td>
<td class="align-middle product white-space-nowrap"> <td class="align-middle product white-space-nowrap">
{% if perms.django_ledger.change_itemmodel %}
<a href="{% url 'item_expense_update' request.dealer.slug expense.pk %}" <a href="{% url 'item_expense_update' request.dealer.slug expense.pk %}"
class="btn btn-sm btn-phoenix-success"> class="btn btn-sm btn-phoenix-success">
{% trans "Update" %} {% trans "Update" %}
</a> </a>
{% endif %}
</td> </td>
<td class="align-middle product white-space-nowrap"> <td class="align-middle product white-space-nowrap">

View File

@ -8,7 +8,9 @@
<div class="d-flex justify-content-between mb-2"> <div class="d-flex justify-content-between mb-2">
<h3 class="">{% trans "Services" %}</h3> <h3 class="">{% trans "Services" %}</h3>
{% if perms.inventory.add_additionalservices %}
<a href="{% url 'item_service_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Service" %}</a> <a href="{% url 'item_service_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Service" %}</a>
{% endif %}
</div> </div>
{% include "partials/search_box.html" %} {% include "partials/search_box.html" %}
{% if page_obj.object_list %} {% if page_obj.object_list %}
@ -46,10 +48,12 @@
{{ service.item.co }} {{ service.item.co }}
</td> </td>
<td class="align-middle white-space-nowrap text-start"> <td class="align-middle white-space-nowrap text-start">
{% if perms.inventory.add_additionalservices %}
<a href="{% url 'item_service_update' request.dealer.slug service.pk %}" <a href="{% url 'item_service_update' request.dealer.slug service.pk %}"
class="btn btn-sm btn-phoenix-success"> class="btn btn-sm btn-phoenix-success">
{% trans "Update" %} {% trans "Update" %}
</a> </a>
{% endif %}
</td> </td>
</tr> </tr>

View File

@ -8,7 +8,9 @@
<div class="d-flex justify-content-between mb-2"> <div class="d-flex justify-content-between mb-2">
<h3 class="">{% trans "Bank Accounts" %}</h3> <h3 class="">{% trans "Bank Accounts" %}</h3>
{% if perms.django_ledger.add_bankaccountmodel%}
<a href="{% url 'bank_account_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Bank Account" %}</a> <a href="{% url 'bank_account_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Bank Account" %}</a>
{% endif %}
</div> </div>
{% include "partials/search_box.html" %} {% include "partials/search_box.html" %}
{% if page_obj.object_list %} {% if page_obj.object_list %}
@ -38,10 +40,12 @@
{{ bank.account_type|capfirst }} {{ bank.account_type|capfirst }}
</td> </td>
<td class="align-middle product white-space-nowrap"> <td class="align-middle product white-space-nowrap">
{% if perms.django_ledger.change_bankaccountmodel%}
<a href="{% url 'bank_account_update' request.dealer.slug bank.pk %}" <a href="{% url 'bank_account_update' request.dealer.slug bank.pk %}"
class="btn btn-sm btn-phoenix-success"> class="btn btn-sm btn-phoenix-success">
{% trans "Update" %} {% trans "Update" %}
</a> </a>
{% endif %}
</td> </td>
</tr> </tr>

View File

@ -14,7 +14,9 @@
<div class="row mt-4"> <div class="row mt-4">
<div class="d-flex justify-content-between mb-2"> <div class="d-flex justify-content-between mb-2">
<h3 class="">{% trans "Bills" %}</h3> <h3 class="">{% trans "Bills" %}</h3>
{% if perms.django_ledger.add_billmodel %}
<a href="{% url 'bill-create' request.dealer.slug entity.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Bill' %}</a> <a href="{% url 'bill-create' request.dealer.slug entity.slug %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans 'New Bill' %}</a>
{% endif %}
</div> </div>
<div class="col-12"> <div class="col-12">
@ -78,12 +80,14 @@
{{bill.vendor.vendor_name}} {{bill.vendor.vendor_name}}
</td> </td>
<td class="align-middle product white-space-nowrap"> <td class="align-middle product white-space-nowrap">
{% if perms.django_ledger.view_billmodel %}
<div class="btn-reveal-trigger position-static"> <div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button> <button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"> <div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=entity.slug bill_pk=bill.pk %}" class="dropdown-item text-success-dark">{% trans 'View' %}</a> <a href="{% url 'bill-detail' dealer_slug=request.dealer.slug entity_slug=entity.slug bill_pk=bill.pk %}" class="dropdown-item text-success-dark">{% trans 'View' %}</a>
</div> </div>
</div> </div>
{% endif %}
</td> </td>
</tr> </tr>
{% empty %} {% empty %}

View File

@ -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 -->

View File

@ -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>

View File

@ -11,8 +11,11 @@
<h3 class=""> <h3 class="">
{{ _("Vendors") |capfirst }} {{ _("Vendors") |capfirst }}
</h2> </h2>
<a href="{% url 'vendor_create' request.dealer.slug %}" {% if perms.django_ledger.add_vendormodel %}
class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{{ _("Add Vendor") }}</a> <a href="{% url 'vendor_create' request.dealer.slug %}" class="btn btn-md btn-phoenix-primary">
<i class="fa fa-plus me-2"></i>{{ _("Add Vendor") }}
</a>
{% endif %}
</div> </div>
{% include "partials/search_box.html" %} {% include "partials/search_box.html" %}
{% if page_obj.object_list %} {% if page_obj.object_list %}
@ -121,14 +124,21 @@
<span class="fas fa-ellipsis-h fs-10"></span> <span class="fas fa-ellipsis-h fs-10"></span>
</button> </button>
<div class="dropdown-menu dropdown-menu-end py-2"> <div class="dropdown-menu dropdown-menu-end py-2">
{% if perms.django_ledger.change_vendormodel %}
<a href="{% url 'vendor_update' request.dealer.slug vendor.slug %}" <a href="{% url 'vendor_update' request.dealer.slug vendor.slug %}"
class="dropdown-item text-success-dark">{% trans "Edit" %}</a> class="dropdown-item text-success-dark">{% trans "Edit" %}
</a>
{% endif %}
{% if perms.django_ledger.delete_vendormodel %}
<div class="dropdown-divider"></div> <div class="dropdown-divider"></div>
<button class="delete-btn dropdown-item text-danger" <button class="delete-btn dropdown-item text-danger"
data-url="{% url 'vendor_delete' request.dealer.slug vendor.slug %}" data-url="{% url 'vendor_delete' request.dealer.slug vendor.slug %}"
data-message="{{ _("Are you sure you want to delete this vendor") }}?" data-message="{{ _("Are you sure you want to delete this vendor") }}?"
data-bs-toggle="modal" data-bs-toggle="modal"
data-bs-target="#deleteModal">{{ _("Delete") }}</button> data-bs-target="#deleteModal">{{ _("Delete") }}
</button>
{% endif %}
</div> </div>
</div> </div>
</td> </td>

View File

@ -28,10 +28,14 @@
</ul> </ul>
</div> </div>
<div class="card-footer d-flex"> <div class="card-footer d-flex">
{% if perms.django_ledger.change_vendormodel %}
<a class="btn btn-sm btn-phoenix-primary me-1" href="{% url 'vendor_update' request.dealer.slug vendor.slug %}"> <a class="btn btn-sm btn-phoenix-primary me-1" href="{% url 'vendor_update' request.dealer.slug vendor.slug %}">
{% trans "Edit" %} {% trans "Edit" %}
<i class="fa fa-pencil"></i> <i class="fa fa-pencil"></i>
</a> </a>
{% endif %}
{% if perms.django_ledger.delete_vendormodel %}
<button class="btn btn-phoenix-danger btn-sm delete-btn" <button class="btn btn-phoenix-danger btn-sm delete-btn"
data-url="{% url 'vendor_delete' request.dealer.slug vendor.slug %}" data-url="{% url 'vendor_delete' request.dealer.slug vendor.slug %}"
data-message="{{ _("Are you sure you want to delete this vendor")}}?" data-message="{{ _("Are you sure you want to delete this vendor")}}?"
@ -39,6 +43,7 @@
{{ _("Delete") }} {{ _("Delete") }}
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
{% endif %}
</div> </div>
</div> </div>
</div> </div>