update
This commit is contained in:
parent
c37f87299b
commit
ec30867916
Binary file not shown.
11
generate.py
11
generate.py
@ -1,3 +1,9 @@
|
||||
import os
|
||||
import django
|
||||
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "car_inventory.settings")
|
||||
django.setup()
|
||||
|
||||
from inventory.models import *
|
||||
from rich import print
|
||||
import random
|
||||
@ -94,7 +100,7 @@ def run():
|
||||
for vin in vin_list:
|
||||
try:
|
||||
for _ in range(15):
|
||||
dealer = Dealer.objects.get(user__email="ismail.mosa.ibrahim@gmail.com")
|
||||
dealer = Dealer.objects.get(user__email="marwan@tenhal.sa")
|
||||
vin = f"{vin[:-4]}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}"
|
||||
result = decodevin(vin)
|
||||
make = CarMake.objects.get(name=result["maker"])
|
||||
@ -126,3 +132,6 @@ def run():
|
||||
print(make, model, serie, trim)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,5 +1,7 @@
|
||||
from appointment.models import Appointment, Service, StaffMember
|
||||
from django.urls import reverse
|
||||
from django_countries.widgets import CountrySelectWidget
|
||||
from django_ledger.models import CustomerModel
|
||||
from phonenumber_field.formfields import PhoneNumberField
|
||||
from django.core.validators import MinLengthValidator
|
||||
from django.core.validators import RegexValidator
|
||||
@ -64,7 +66,7 @@ class StaffForm(forms.ModelForm):
|
||||
email = forms.EmailField(
|
||||
required=True,
|
||||
label="Email",
|
||||
widget=forms.EmailInput(attrs={"class": "form-control"}),
|
||||
widget=forms.EmailInput(attrs={"class": "form-control form-control-sm"}),
|
||||
)
|
||||
|
||||
class Meta:
|
||||
@ -773,3 +775,5 @@ class EstimateModelCreateForm(EstimateModelCreateFormBase):
|
||||
def get_customer_queryset(self):
|
||||
return self.USER_MODEL.dealer.entity.get_customers()
|
||||
|
||||
|
||||
|
||||
|
||||
22
inventory/migrations/0018_customer_user.py
Normal file
22
inventory/migrations/0018_customer_user.py
Normal file
@ -0,0 +1,22 @@
|
||||
# Generated by Django 5.1.5 on 2025-02-11 00:23
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0017_car_hash'),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='customer',
|
||||
name='user',
|
||||
field=models.OneToOneField(default=4, on_delete=django.db.models.deletion.CASCADE, related_name='customer_profile', to=settings.AUTH_USER_MODEL),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@ -1007,6 +1007,7 @@ class Customer(models.Model):
|
||||
dealer = models.ForeignKey(
|
||||
Dealer, on_delete=models.CASCADE, related_name="customers"
|
||||
)
|
||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='customer_profile')
|
||||
title = models.CharField(
|
||||
choices=Title.choices, default=Title.NA, max_length=10, verbose_name=_("Title")
|
||||
)
|
||||
|
||||
@ -664,6 +664,23 @@ def create_customer(sender, instance, created, **kwargs):
|
||||
print(f"Customer created: {name}")
|
||||
|
||||
|
||||
@receiver(post_save, sender=models.Customer)
|
||||
def create_customer_user(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
user = User.objects.create(
|
||||
username=instance.email,
|
||||
email=instance.email,
|
||||
password=None,
|
||||
first_name=instance.first_name,
|
||||
last_name=instance.last_name
|
||||
)
|
||||
user.is_active = True
|
||||
user.is_staff = True
|
||||
user.save()
|
||||
instance.user = user
|
||||
instance.save()
|
||||
|
||||
|
||||
# Create Item
|
||||
@receiver(post_save, sender=models.Car)
|
||||
def create_item_model(sender, instance, created, **kwargs):
|
||||
|
||||
@ -4,7 +4,7 @@ from num2words import num2words
|
||||
register = template.Library()
|
||||
|
||||
@register.filter
|
||||
def num_to_words(value, lang='en'):
|
||||
def num_to_words(value, lang='ar'):
|
||||
try:
|
||||
return num2words(value, lang=lang)
|
||||
except:
|
||||
|
||||
@ -159,6 +159,7 @@ urlpatterns = [
|
||||
views.mark_notification_as_read,
|
||||
name="mark_notification_as_read",
|
||||
),
|
||||
path('crm/calender/', views.EmployeeCalendarView.as_view(), name='calendar_list'),
|
||||
# Vendor URLs
|
||||
path("vendors", views.VendorListView.as_view(), name="vendor_list"),
|
||||
path("vendors/<uuid:pk>/", views.vendorDetailView, name="vendor_detail"),
|
||||
|
||||
@ -112,6 +112,7 @@ from pyzbar.pyzbar import decode
|
||||
from django.core.files.storage import default_storage
|
||||
from plans.models import Plan,PlanPricing
|
||||
from django_ledger.utils import accruable_net_summary
|
||||
from appointment.views_admin import fetch_user_appointments
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -2889,12 +2890,14 @@ class InvoicePreviewView(LoginRequiredMixin, DetailView):
|
||||
template_name = "sales/invoices/invoice_preview.html"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
dealer = get_user_type(self.request)
|
||||
invoice = kwargs.get("object")
|
||||
if invoice.get_itemtxs_data():
|
||||
# data = get_financial_values(invoice)
|
||||
calculator = CarFinanceCalculator(invoice)
|
||||
finance_data = calculator.get_finance_data()
|
||||
kwargs["data"] = finance_data
|
||||
kwargs['dealer'] = dealer
|
||||
# kwargs["vat_amount"] = data["vat_amount"]
|
||||
# kwargs["total"] = data["grand_total"]
|
||||
# kwargs["discount_amount"] = data["discount_amount"]
|
||||
@ -4154,4 +4157,14 @@ class PnLAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View):
|
||||
}, status=401)
|
||||
|
||||
|
||||
class EmployeeCalendarView(ListView):
|
||||
template_name = 'crm/employee_calendar.html'
|
||||
model = Appointment
|
||||
context_object_name = 'appointments'
|
||||
|
||||
# def get_context_data(self):
|
||||
# self.context['dealer'] = get_user_type(self.request)
|
||||
# dealer =
|
||||
# return Appointment.objects.all()
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@ -52,7 +52,7 @@ django-debug-toolbar==5.0.1
|
||||
django-extensions==3.2.3
|
||||
django-filter==24.3
|
||||
django-formtools==2.5.1
|
||||
django-ledger==0.7.3
|
||||
django-ledger==0.7.4.1
|
||||
django-money==3.5.3
|
||||
django-next-url-mixin==0.4.0
|
||||
django-nine==0.2.7
|
||||
@ -85,7 +85,6 @@ filelock==3.17.0
|
||||
fire==0.7.0
|
||||
Flask==3.1.0
|
||||
fonttools==4.55.7
|
||||
fpdf==1.7.2
|
||||
fpdf2==2.8.2
|
||||
frozenlist==1.5.0
|
||||
fsspec==2024.12.0
|
||||
|
||||
BIN
static/.DS_Store
vendored
BIN
static/.DS_Store
vendored
Binary file not shown.
43
static/css/pdf_preview.css
Normal file
43
static/css/pdf_preview.css
Normal file
@ -0,0 +1,43 @@
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.pdf-container {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
padding: 20mm;
|
||||
margin: auto;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.pdf-header {
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.pdf-content {
|
||||
flex: 1;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.pdf-footer {
|
||||
text-align: center;
|
||||
margin-top: auto;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
border-top: 1px solid #ddd;
|
||||
}
|
||||
BIN
static/images/.DS_Store
vendored
BIN
static/images/.DS_Store
vendored
Binary file not shown.
BIN
static/images/logos/logo-d-pdf.png
Normal file
BIN
static/images/logos/logo-d-pdf.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 12 KiB |
@ -217,7 +217,7 @@ function displayEventList(events, date) {
|
||||
eventListHtml += `<div class="djangoAppt_no-events">` + noEventTxt + `</div>`;
|
||||
}
|
||||
|
||||
eventListHtml += `<button class="btn btn-primary djangoAppt_btn-new-event" onclick="createNewAppointment('${date_obj}')">` + newEventTxt + `</button></div>`;
|
||||
eventListHtml += `<button class="btn btn-sm btn-primary" onclick="createNewAppointment('${date_obj}')">` + newEventTxt + `</button></div>`;
|
||||
|
||||
const eventListContainer = document.getElementById('event-list-container');
|
||||
eventListContainer.innerHTML = eventListHtml;
|
||||
@ -580,10 +580,12 @@ async function showCreateAppointmentModal(defaultStartTime, formattedDate) {
|
||||
if (isUserSuperUser) {
|
||||
staffDropdown = await populateStaffMembers(null, false);
|
||||
staffDropdown.id = "staffSelect";
|
||||
staffDropdown.classList.add('form-select')
|
||||
staffDropdown.disabled = false; // Enable dropdown
|
||||
attachEventListenersToDropdown(); // Attach event listener
|
||||
}
|
||||
servicesDropdown.id = "serviceSelect";
|
||||
servicesDropdown.classList.add('form-select')
|
||||
servicesDropdown.disabled = false; // Enable dropdown
|
||||
|
||||
document.getElementById('eventModalBody').innerHTML = prepareCreateAppointmentModalContent(servicesDropdown, staffDropdown, defaultStartTime, formattedDate);
|
||||
@ -635,6 +637,8 @@ async function getAppointmentData(eventId, isCreatingMode, defaultStartTime) {
|
||||
async function getServiceDropdown(serviceId, isEditMode) {
|
||||
const servicesDropdown = await populateServices(serviceId, !isEditMode);
|
||||
servicesDropdown.id = "serviceSelect";
|
||||
servicesDropdown.classList.add('form-select');
|
||||
servicesDropdown.classList.add('form-select-sm');
|
||||
servicesDropdown.disabled = !isEditMode;
|
||||
return servicesDropdown;
|
||||
}
|
||||
@ -643,6 +647,8 @@ async function getServiceDropdown(serviceId, isEditMode) {
|
||||
async function getStaffDropdown(staffId, isEditMode) {
|
||||
const staffDropdown = await populateStaffMembers(staffId, !isEditMode);
|
||||
staffDropdown.id = "staffSelect";
|
||||
staffDropdown.classList.add('form-select');
|
||||
staffDropdown.classList.add('form-select-sm');
|
||||
staffDropdown.disabled = !isEditMode;
|
||||
return staffDropdown;
|
||||
}
|
||||
|
||||
@ -15,7 +15,7 @@ const calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
left: 'title',
|
||||
right: 'prev,today,next',
|
||||
},
|
||||
height: '400px',
|
||||
|
||||
themeSystem: 'bootstrap',
|
||||
nowIndicator: true,
|
||||
bootstrapFontAwesome: {
|
||||
|
||||
3
static/js/html2pdf.bundle.min.js
vendored
Normal file
3
static/js/html2pdf.bundle.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -221,3 +221,4 @@ const getDataTableInit = () => {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BIN
static/qr_code/Marwan_qr.png
Normal file
BIN
static/qr_code/Marwan_qr.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
@ -1,97 +1,98 @@
|
||||
{% extends BASE_TEMPLATE %}
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% block customCSS %}
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/app_admin/display_appointment.css' %}"/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css"
|
||||
integrity="sha512-DTOQO9RWCH3ppGqcWaEA1BIZOC6xxalwEsw9c2QQeAIftl+Vegovlnee1c9QX4TctnWMn13TZye+giMm8e2LwA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
||||
{% endblock %}
|
||||
{% block title %}
|
||||
{{ page_title }}
|
||||
{% endblock %}
|
||||
{% block description %}
|
||||
{{ page_description }}
|
||||
{% endblock %}
|
||||
{% block body %}
|
||||
<section class="content content-wrapper">
|
||||
<div class="appointment-display-content">
|
||||
<div class="app-content">
|
||||
<div class="appointment-card">
|
||||
<h2>{{ page_title }}</h2>
|
||||
<div class="appointment-details">
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-calendar-alt"></i>
|
||||
<strong>{% trans 'Date' %}:</strong> {{ appointment.get_date }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-clock"></i>
|
||||
<strong>{% trans 'Start time' %}:</strong> {{ appointment.get_start_time|time:"g:i A" }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-clock"></i>
|
||||
<strong>{% trans 'End time' %}:</strong> {{ appointment.get_end_time|time:"g:i A" }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-hands-helping"></i>
|
||||
<strong>{% trans 'Service' %}:</strong> {{ appointment.get_service_name }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-user"></i>
|
||||
<strong>{% trans 'Client' %}:</strong> {{ appointment.get_client_name }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-envelope"></i>
|
||||
<strong>{% trans 'Email' %}:</strong> {{ appointment.client.email }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-phone"></i>
|
||||
<strong>{% trans 'Phone' %}:</strong> {{ appointment.phone }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-comment"></i>
|
||||
<strong>{% trans 'Wants reminder' %}:</strong> {{ appointment.want_reminder }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-map-marker-alt"></i>
|
||||
<strong>{% trans 'Client address' %}:</strong> {{ appointment.address }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-info-circle"></i>
|
||||
<strong>{% trans 'Additional Information' %}:</strong> {{ appointment.additional_info }}
|
||||
</div>
|
||||
<div class="appointment-detail {% if appointment.is_paid %} is-paid-true {% else %} is-paid-false {% endif %}">
|
||||
<i class="fas fa-money-bill-wave"></i>
|
||||
<strong>{% trans 'Is paid' %}:</strong> {{ appointment.is_paid_text }}
|
||||
</div>
|
||||
<div class="appointment-detail hover-element">
|
||||
<i class="fas fa-dollar-sign"></i>
|
||||
<strong>{% trans 'Service price' %}:</strong> {{ appointment.get_appointment_amount_to_pay_text }}
|
||||
</div>
|
||||
|
||||
{% block title %}{{ page_title }}{% endblock %}
|
||||
{% block description %}{{ page_description }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container py-5">
|
||||
<div class="card bg-body">
|
||||
<div class="card-header ">
|
||||
<h4 class="mb-0">{{ page_title }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-calendar-alt me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Date' %}:</strong> {{ appointment.get_date }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="messages" style="margin: 20px 0">
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
<div class="alert alert-dismissible {% if message.tags %}alert-{% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %}danger{% else %}{{ message.tags }}{% endif %}{% endif %}"
|
||||
role="alert">{{ message }}</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-clock me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Start time' %}:</strong> {{ appointment.get_start_time|time:"g:i A" }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-clock me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'End time' %}:</strong> {{ appointment.get_end_time|time:"g:i A" }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-hands-helping me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Service' %}:</strong> {{ appointment.get_service_name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-user me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Client' %}:</strong> {{ appointment.get_client_name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-envelope me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Email' %}:</strong> {{ appointment.client.email }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-phone me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Phone' %}:</strong> {{ appointment.phone }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-comment me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Wants reminder' %}:</strong> {{ appointment.want_reminder }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-map-marker-alt me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Client address' %}:</strong> {{ appointment.address }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-info-circle me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Additional Information' %}:</strong> {{ appointment.additional_info }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-money-bill-wave me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Is paid' %}:</strong> {{ appointment.is_paid_text }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center">
|
||||
<i class="fas fa-dollar-sign me-2 text-primary"></i>
|
||||
<strong class="me-2">{% trans 'Service price' %}:</strong> {{ appointment.get_appointment_amount_to_pay_text }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block customJS %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.js"
|
||||
integrity="sha512-3CuraBvy05nIgcoXjVN33mACRyI89ydVHg7y/HMN9wcTVbHeur0SeBzweSd/rxySapO7Tmfu68+JlKkLTnDFNg=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js"
|
||||
integrity="sha512-t/mY3un180WRfsSkWy4Yi0tAxEDGcY2rAEx873hb5BrkvLA0QLk54+SjfYgFBBoCdJDV1H86M8uyZdJhAOHeyA=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/6.1.10/index.global.min.js"
|
||||
integrity="sha512-JCQkxdym6GmQ+AFVioDUq8dWaWN6tbKRhRyHvYZPupQ6DxpXzkW106FXS1ORgo/m3gxtt5lHRMqSdm2OfPajtg=="
|
||||
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment-timezone/0.5.45/moment-timezone-with-data.min.js" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/6.1.10/index.global.min.js" crossorigin="anonymous"></script>
|
||||
{% endblock %}
|
||||
|
||||
@ -35,9 +35,9 @@
|
||||
</div>
|
||||
</section>
|
||||
<div id="customContextMenu" style="display: none; position: absolute; z-index: 1000;">
|
||||
<ul>
|
||||
<li id="newAppointmentOption"><a href="#">{{ _("New Appointment")}}</a></li>
|
||||
</ul>
|
||||
|
||||
<a id="newAppointmentOption" class="btn btn-sm btn-phoenix-success rounded-pill me-1 mb-1" href="#">{{ _("New Appointment")}}</a>
|
||||
|
||||
</div>
|
||||
|
||||
{% include 'modal/confirm_modal.html' %}
|
||||
@ -103,7 +103,7 @@
|
||||
if (isUserSuperUser) {
|
||||
superuserInputField = `
|
||||
<div class="flex-container-appt">
|
||||
<label>{% trans 'Staff Member' %}:</label>
|
||||
<label class="form-label">{% trans 'Staff Member' %}:</label>
|
||||
${staffDropdown.outerHTML}
|
||||
</div>
|
||||
`;
|
||||
@ -112,27 +112,27 @@
|
||||
return `
|
||||
${superuserInputField}
|
||||
<div class="flex-container-appt">
|
||||
<label>{% trans 'Service Name' %}:</label>
|
||||
<label class="form-label">{% trans 'Service Name' %}:</label>
|
||||
${servicesDropdown.outerHTML}
|
||||
</div>
|
||||
<label for="clientName">{% trans 'Client Name' %}:</label>
|
||||
<input type="text" name="clientName" value="${appointment.client_name || ''}" ${disabledAttribute} id="clientName" placeholder="John Doe">
|
||||
<label for="clientEmail">{% trans 'Client Email' %}:</label>
|
||||
<input type="email" name="clientEmail" value="${appointment.client_email || ''}" ${disabledAttribute} id="clientEmail" placeholder="john.doe@example.com">
|
||||
<label class="form-label" for="clientName">{% trans 'Client Name' %}:</label>
|
||||
<input type="text" name="clientName" class="form-control form-control-sm" value="${appointment.client_name || ''}" ${disabledAttribute} id="clientName" placeholder="">
|
||||
<label class="form-label" for="clientEmail">{% trans 'Client Email' %}:</label>
|
||||
<input type="email" name="clientEmail" class="form-control form-control-sm" value="${appointment.client_email || ''}" ${disabledAttribute} id="clientEmail" placeholder="">
|
||||
<span id="emailError" style="display:none;"></span>
|
||||
<label for="clientPhone">{% trans 'Phone Number' %}:</label>
|
||||
<input type="tel" name="clientPhone" value="${appointment.client_phone || ''}" ${disabledAttribute} id="clientPhone" placeholder="+12392350799">
|
||||
<label for="clientAddress">{% trans 'Client address' %}:</label>
|
||||
<input type="text" name="clientAddress" value="${appointment.client_address || ''}" ${disabledAttribute} id="clientAddress" placeholder="Naples, Florida">
|
||||
<label class="form-label" for="clientPhone">{% trans 'Phone Number' %}:</label>
|
||||
<input type="tel" name="clientPhone" class="form-control form-control-sm" value="${appointment.client_phone || ''}" ${disabledAttribute} id="clientPhone" placeholder="+9665********">
|
||||
<label class="form-label" for="clientAddress">{% trans 'Client address' %}:</label>
|
||||
<input type="text" name="clientAddress" class="form-control form-control-sm" value="${appointment.client_address || ''}" ${disabledAttribute} id="clientAddress" placeholder="">
|
||||
<div class="flex-container-appt">
|
||||
<label for="want_reminder">{% trans 'Wants reminder' %}:</label>
|
||||
<input type="checkbox" name="want_reminder" id="want_reminder" value="true" ${appointment.want_reminder ? 'checked' : ''} ${disabledAttribute}>
|
||||
<label class="form-check-label" for="want_reminder">{% trans 'Wants reminder' %}:</label>
|
||||
<input class="form-check-input" type="checkbox" name="want_reminder" id="want_reminder" value="true" ${appointment.want_reminder ? 'checked' : ''} ${disabledAttribute}>
|
||||
</div>
|
||||
<label for="additional_info">{% trans 'Additional Information' %}:</label>
|
||||
<input type="text" name="additional_info" value="${appointment.additional_info || ''}" ${disabledAttribute} placeholder="{% trans 'Client wants this and that' %}" id="additional_info">
|
||||
<label class="form-label" for="additional_info">{% trans 'Additional Information' %}:</label>
|
||||
<input class="form-control form-control-sm" type="text" name="additional_info" value="${appointment.additional_info || ''}" ${disabledAttribute} placeholder="{% trans 'Client wants this and that' %}" id="additional_info">
|
||||
<div class="flex-container-appt">
|
||||
<label for="startTime">{% trans 'Start time' %}:</label>
|
||||
<input type="time" name="startTime" value="${startTimeValue}" ${disabledAttribute}>
|
||||
<label class="form-label" for="startTime">{% trans 'Start time' %}:</label>
|
||||
<input class="form-control form-control-sm" type="time" name="startTime" value="${startTimeValue}" ${disabledAttribute}>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -143,8 +143,8 @@
|
||||
return `
|
||||
${commonInputs}
|
||||
<div class="flex-container-appt">
|
||||
<label for="endTime">{% trans 'End time' %}:</label>
|
||||
<input type="time" name="endTime" value="${moment(appointment.end_time).format('HH:mm:ss')}" ${!isEditMode ? 'disabled' : ''}>
|
||||
<label class="form-label" for="endTime">{% trans 'End time' %}:</label>
|
||||
<input class="form-control form-control-sm" type="time" name="endTime" value="${moment(appointment.end_time).format('HH:mm:ss')}" ${!isEditMode ? 'disabled' : ''}>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
@ -156,7 +156,7 @@
|
||||
return `
|
||||
${commonInputs}
|
||||
<label for="date" style="display: none"></label>
|
||||
<input type="hidden" name="date" value="${formattedDate}">
|
||||
<input type="hidden" name="date" value="${formattedDate}" class="datepicker">
|
||||
`;
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
<!-- Appointment Information Section -->
|
||||
<div class="row mt-4">
|
||||
<div class="col-6">
|
||||
<h3>{% trans 'Appointment Information' %}</h3>
|
||||
<h3>{% trans 'Appointments Information' %}</h3>
|
||||
<small>
|
||||
{{ service_msg }}
|
||||
</small>
|
||||
|
||||
22
templates/crm/employee_calendar.html
Normal file
22
templates/crm/employee_calendar.html
Normal file
@ -0,0 +1,22 @@
|
||||
<!-- crm/employee_calendar.html -->
|
||||
<h1>Upcoming Test Drives</h1>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Customer</th>
|
||||
<th>Vehicle</th>
|
||||
<th>Date/Time</th>
|
||||
<th>Staff</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for appointment in appointments %}
|
||||
<tr>
|
||||
<td>{{ appointment.client.name }}</td>
|
||||
<td>{{ appointment.service.name }}</td>
|
||||
<td>{{ appointment.start_time|date:"M j, Y H:i" }}</td>
|
||||
<td>{{ appointment.staff.user.get_full_name }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
@ -199,24 +199,7 @@
|
||||
</thead>
|
||||
<tbody class="list" id="lead-details-table-body">
|
||||
{% for note in notes %}
|
||||
<div class="modal fade" id="deleteModal" data-bs-keyboard="false" tabindex="-1" aria-labelledby="deleteModalModal" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
<h4 class="mb-0 me-2 text-danger">{{ _("Delete")}}<i class="fas fa-exclamation-circle text-danger ms-2"></i></h4>
|
||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close"><span class="fas fa-times"></span></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<p>{{ _("Are you sure you want to delete this note?") }}</p>
|
||||
</div>
|
||||
<div class="modal-footer flex justify-content-center border-top-0">
|
||||
<a class="btn btn-sm btn-danger w-100" href="{% url 'delete_note' note.pk %}">
|
||||
{{ _("Delete") }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="align-middle text-start fw-bold text-body-tertiary ps-1">{{note.note}}</td>
|
||||
{% if note.created_by.staff %}
|
||||
@ -230,9 +213,13 @@
|
||||
<a id="updateBtn" href="#" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'update_note' note.pk %}" data-bs-toggle="modal" data-bs-target="#noteModal" data-note-title="{{ _("Update") }}<i class='fas fa-pen-square text-primary ms-2'></i>">
|
||||
<i class="fas fa-pen"></i>
|
||||
</a>
|
||||
<button class="btn btn-sm btn-phoenix-danger text-danger" type="button" data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
|
||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||
data-url="{% url 'delete_note' note.pk %}"
|
||||
data-message="Are you sure you want to delete this note?"
|
||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
@ -587,6 +574,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include 'modal/delete_modal.html' %}
|
||||
<!-- add update Modal -->
|
||||
<div class="modal fade" id="noteModal" tabindex="-1" aria-labelledby="noteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
@ -622,7 +610,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
modalTitle.innerHTML = title;
|
||||
})
|
||||
.catch((error) => {
|
||||
modalBody.innerHTML = '<p class="text-danger">Error loading form. Please try again later.</p>';
|
||||
modalBody.innerHTML = '<p class="text-danger">{% trans 'Error loading form. Please try again later' %}.</p>';
|
||||
console.error("Error loading form:", error);
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
<div class="col-auto">
|
||||
<div class="d-md-flex justify-content-between">
|
||||
<div>
|
||||
<a href="{% url 'lead_create' %}" class="btn btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{{ _("Add Lead") }}</a>
|
||||
<a href="{% url 'lead_create' %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{{ _("Add Lead") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -91,24 +91,19 @@
|
||||
<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="deleteModalLabel">
|
||||
{% trans "Delete Lead" %}
|
||||
<span data-feather="alert-circle"></span>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
<h4 class="mb-0 me-2 text-danger">{{ _("Delete")}}<i class="fas fa-exclamation-circle text-danger ms-2"></i></h4>
|
||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close"><span class="fas fa-times"></span></button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<p>{% trans "Are you sure you want to delete this lead?" %}</p>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p class="mb-0 text-danger fw-bold">
|
||||
{% trans "Are you sure you want to delete this lead?" %}
|
||||
</p>
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
|
||||
{% trans "No" %}
|
||||
</button>
|
||||
<a type="button" class="btn btn-danger btn-sm" href="{% url 'lead_delete' lead.pk %}">
|
||||
<div class="modal-footer flex justify-content-center border-top-0">
|
||||
<a type="button" class="btn btn-sm btn-danger w-100" href="{% url 'lead_delete' lead.pk %}">
|
||||
{% trans "Yes" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -69,37 +69,7 @@
|
||||
|
||||
{% for customer in customers %}
|
||||
<!-- Delete Modal -->
|
||||
<div class="modal fade" id="deleteModal"
|
||||
data-bs-backdrop="static"
|
||||
data-bs-keyboard="false"
|
||||
tabindex="-1"
|
||||
aria-labelledby="deleteModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="deleteModalLabel">
|
||||
|
||||
{% trans "Delete Customer" %}
|
||||
<span data-feather="alert-circle"></span>
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center">
|
||||
<p class="mb-0 text-danger fw-bold">
|
||||
{% trans "Are you sure you want to delete this customer?" %}
|
||||
</p>
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
|
||||
{% trans "No" %}
|
||||
</button>
|
||||
{% comment %} <a type="button" class="btn btn-danger btn-sm" href="{% url 'customer_delete' customer.id %}">
|
||||
{% trans "Yes" %}
|
||||
</a> {% endcomment %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td>
|
||||
|
||||
@ -119,13 +89,15 @@
|
||||
{{ customer.address_1 }}</td>
|
||||
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ customer.created|date }}</td>
|
||||
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
|
||||
<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>
|
||||
<div class="dropdown-menu dropdown-menu-end py-2">
|
||||
<a href="{% url 'customer_update' customer.pk %}" class="dropdown-item text-success-dark">{% trans "Edit" %}</a>
|
||||
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
<a href="#" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'customer_update' customer.pk %}">
|
||||
<i class="fas fa-pen"></i>
|
||||
</a>
|
||||
<button class="btn btn-phoenix-danger btn-sm delete-btn"
|
||||
data-url="{% url 'customer_delete' customer.pk %}"
|
||||
data-message="Are you sure you want to delete this customer?"
|
||||
data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
@ -134,12 +106,13 @@
|
||||
</table>
|
||||
</div>
|
||||
<div class="row align-items-center justify-content-end py-4 pe-0 fs-9">
|
||||
|
||||
<!-- Optional: Pagination -->
|
||||
{% if is_paginated %}
|
||||
{% include 'partials/pagination.html' %}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include 'modal/delete_modal.html' %}
|
||||
|
||||
{% endblock %}
|
||||
@ -413,7 +413,11 @@
|
||||
<span class="fa fa-user text-body-tertiary" style="width: 32px;"></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if user.dealer %}
|
||||
<h6 class="mt-2 text-body-emphasis">{{ user.dealer.get_local_name }}</h6>
|
||||
{% else %}
|
||||
<h6 class="mt-2 text-body-emphasis">{{ user.staff.get_local_name }}</h6>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto scrollbar" style="height: 10rem;">
|
||||
|
||||
48
templates/modal/delete_modal.html
Normal file
48
templates/modal/delete_modal.html
Normal file
@ -0,0 +1,48 @@
|
||||
{% load i18n %}
|
||||
<div class="modal fade" id="deleteModal"
|
||||
data-bs-backdrop="static"
|
||||
data-bs-keyboard="false"
|
||||
tabindex="-1"
|
||||
aria-labelledby="deleteModalLabel"
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
<h4 class="mb-0 me-2 text-danger">
|
||||
{{ _("Delete") }}
|
||||
<i class="fas fa-exclamation-circle text-danger ms-2"></i>
|
||||
</h4>
|
||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close">
|
||||
<span class="fas fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body p-4">
|
||||
<p id="deleteModalText">
|
||||
|
||||
</p>
|
||||
</div>
|
||||
<div class="modal-footer flex justify-content-center border-top-0">
|
||||
<a id="deleteModalConfirm" type="button" class="btn btn-sm btn-danger w-100" href="">
|
||||
{{ _("Delete") }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const deleteModal = document.getElementById("deleteModal");
|
||||
const confirmDeleteBtn = document.getElementById("deleteModalConfirm");
|
||||
const deleteModalMessage = document.getElementById("deleteModalText");
|
||||
|
||||
document.querySelectorAll(".delete-btn").forEach(button => {
|
||||
button.addEventListener("click", function () {
|
||||
let deleteUrl = this.getAttribute("data-url");
|
||||
let deleteMessage = this.getAttribute("data-message");
|
||||
|
||||
confirmDeleteBtn.setAttribute("href", deleteUrl);
|
||||
deleteModalMessage.innerHTML = deleteMessage;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@ -4,33 +4,35 @@
|
||||
aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="eventModalLabel">{% trans 'Event Details' %}</h5>
|
||||
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"
|
||||
onclick="closeModal()"></button>
|
||||
<div class="modal-header justify-content-between align-items-start gap-5 px-4 pt-4 pb-3 border-0">
|
||||
<h4 class="modal-title" id="eventModalLabel">{% trans 'Appointment Details' %}</h4>
|
||||
<button class="btn p-0 text-body-quaternary fs-6" data-bs-dismiss="modal" aria-label="Close" onclick="closeModal()">
|
||||
<span class="fas fa-times"></span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body" id="eventModalBody">
|
||||
<!-- Event details will be populated here -->
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-phoenix-success" id="eventGoBtn"
|
||||
<div class="btn-group btn-group-sm mt-2 gap-1" role="group">
|
||||
<button type="button" class="btn btn-sm btn-phoenix-success" id="eventGoBtn"
|
||||
onclick="goToEvent()">{% trans 'Go' %}</button>
|
||||
<button type="button" class="btn btn-phoenix-secondary" data-dismiss="modal"
|
||||
<button type="button" class="btn btn-sm btn-phoenix-secondary" data-dismiss="modal"
|
||||
onclick="closeModal()">{% trans 'Close' %}
|
||||
</button>
|
||||
<button type="button" class="btn btn-phoenix-secondary" id="eventCancelBtn" style="display: none;"
|
||||
<button type="button" class="btn btn-sm btn-phoenix-secondary" id="eventCancelBtn" style="display: none;"
|
||||
onclick="cancelEdit()">{% trans 'Cancel' %}
|
||||
</button>
|
||||
<button type="button" class="btn btn-phoenix-primary" id="eventEditBtn"
|
||||
<button type="button" class="btn btn-sm btn-phoenix-primary" id="eventEditBtn"
|
||||
onclick="toggleEditMode()">{% trans 'Edit' %}</button>
|
||||
<button type="button" class="btn btn-primary" id="eventSubmitBtn" style="display: none;"
|
||||
<button type="button" class="btn btn-sm btn-primary" id="eventSubmitBtn" style="display: none;"
|
||||
onclick="submitChanges()">{% trans 'Submit' %}
|
||||
</button>
|
||||
<button type="button" class="btn btn-phoenix-danger" id="eventDeleteBtn"
|
||||
onclick="deleteAppointment()" style="display: none;">
|
||||
{% trans 'Delete' %}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@ -229,7 +229,7 @@
|
||||
<td class="align-middle">{{item.model}}</td>
|
||||
<td class="align-middle">{{item.year}}</td>
|
||||
<td class="align-middle">{{item.vin}}</td>
|
||||
<td class="align-middle">{{item.quantity}}</td>
|
||||
<td class="align-middle">{{item.quantity|floatformat:-1}}</td>
|
||||
<td class="align-middle ps-5">{{item.total}}</td>
|
||||
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
|
||||
</tr>
|
||||
|
||||
@ -1,280 +1,193 @@
|
||||
{% load i18n static custom_filters%}
|
||||
{% load i18n static custom_filters num2words_tags%}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="ar">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>invoice</title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<title>Invoice</title>
|
||||
<!-- CSS -->
|
||||
<link href="{% static 'css/theme.min.css' %}" type="text/css" rel="stylesheet" id="style-default">
|
||||
<link href="{% static 'css/user.min.css' %}" type="text/css" rel="stylesheet" id="user-style-default">
|
||||
<!-- Google Fonts - Roboto -->
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<!-- Custom CSS -->
|
||||
<style>
|
||||
body {
|
||||
background-color: #f8f9fa;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
margin: 0; /* Reset body margin */
|
||||
padding: 0; /* Reset body padding */
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
.invoice-row {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
||||
padding: 3rem;
|
||||
margin: 3rem auto;
|
||||
max-width: 1000px;
|
||||
.invoice-container {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
padding: 10mm;
|
||||
margin: auto;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.invoice-header {
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
padding-bottom: 1.5rem;
|
||||
margin-bottom: 2.5rem;
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.invoice-header h1 {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 0.5rem;
|
||||
.qr-code {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.invoice-header p {
|
||||
font-size: 1.1rem;
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
.qr-code img {
|
||||
width: 3cm;
|
||||
height: 3cm;
|
||||
}
|
||||
.invoice-details {
|
||||
margin-bottom: 2.5rem;
|
||||
}
|
||||
.invoice-details-en {
|
||||
text-align: left;
|
||||
}
|
||||
.invoice-details-ar {
|
||||
text-align: right;
|
||||
|
||||
}
|
||||
.invoice-details p {
|
||||
margin: 0.75rem 0;
|
||||
color: #555;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
.invoice-table {
|
||||
margin-bottom: 2.5rem;
|
||||
.invoice-details, .invoice-table {
|
||||
font-size: 12px;
|
||||
}
|
||||
.invoice-table th {
|
||||
background-color: #f8f9fa;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
.invoice-table td {
|
||||
color: #555;
|
||||
font-weight: 400;
|
||||
}
|
||||
.additional-charges {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.additional-charges p {
|
||||
margin: 0.5rem 0;
|
||||
color: #555;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
font-weight: 600;
|
||||
}
|
||||
.invoice-total {
|
||||
text-align: right;
|
||||
font-size: 1.5rem;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin-top: 1rem;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.footer-note {
|
||||
text-align: center;
|
||||
color: #777;
|
||||
margin-top: 3rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
.logo {
|
||||
max-width: 150px;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
.highlight {
|
||||
color: #007bff;
|
||||
font-weight: 500;
|
||||
}
|
||||
.btn-primary {
|
||||
background-color: #007bff;
|
||||
border-color: #007bff;
|
||||
font-weight: 500;
|
||||
padding: 0.75rem 1.5rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: #0056b3;
|
||||
border-color: #0056b3;
|
||||
}
|
||||
.button-row {
|
||||
text-align: right;
|
||||
margin-top: 2rem;
|
||||
margin-right:10rem;
|
||||
}
|
||||
position: absolute;
|
||||
bottom: 35mm;
|
||||
font-size: 10px;
|
||||
margin-top: auto;
|
||||
margin-right: auto;
|
||||
width: 28%;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="button-row">
|
||||
<button id="download-pdf" class="btn btn-primary">Download Invoice</button>
|
||||
</div>
|
||||
<div class="invoice-row" id="invoice-content">
|
||||
<!-- Header -->
|
||||
<div class="invoice-container" id="invoice-content">
|
||||
<div class="invoice-header">
|
||||
|
||||
<h3 style="margin-top: 10px;">Invoice / فاتورة</h3>
|
||||
<p></p>
|
||||
<h5>Tax Invoice / فاتورة ضريبية</h5>
|
||||
</div>
|
||||
|
||||
<!-- Details -->
|
||||
<div class="details-row">
|
||||
<div class="col-12">
|
||||
<table class="table table-responsive">
|
||||
<tr>
|
||||
<td class="col-3 text-start p-2">
|
||||
<strong>Invoice Number</strong>
|
||||
</td>
|
||||
<td class="col-6 text-center p-2">
|
||||
<span class="highlight">{{invoice.invoice_number}}</span>
|
||||
</td>
|
||||
<td class="col-3 text-end p-2">
|
||||
<strong>رقم الفاتورة</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-3 text-start p-2">
|
||||
<strong>Date</strong>
|
||||
</td>
|
||||
<td class="col-6 text-center p-2">
|
||||
<span class="highlight">{{invoice.date_in_review}}</span>
|
||||
</td>
|
||||
<td class="col-3 text-end p-2">
|
||||
<strong>التاريخ</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-3 text-start p-2">
|
||||
<strong>Customer</strong>
|
||||
</td>
|
||||
<td class="col-6 text-center p-2">
|
||||
<span class="highlight">{{invoice.customer.customer_name}}</span>
|
||||
</td>
|
||||
<td class="col-3 text-end p-2">
|
||||
<strong>العميل</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-3 text-start p-2">
|
||||
<strong>Email</strong>
|
||||
</td>
|
||||
<td class="col-6 text-center p-2">
|
||||
<span class="highlight">{{invoice.customer.email}}</span>
|
||||
</td>
|
||||
<td class="col-3 text-end p-2">
|
||||
<strong>البريد الالكتروني</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="col-3 text-start p-2">
|
||||
<strong>Terms</strong>
|
||||
</td>
|
||||
<td class="col-6 text-center p-2">
|
||||
<span class="highlight">{{invoice.get_terms_display}}</span>
|
||||
</td>
|
||||
<td class="col-3 text-end p-2">
|
||||
<strong>طريقة الدفع</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<div class="qr-code">
|
||||
<img src="{% static 'qr_code/Marwan_qr.png' %}" alt="QR Code">
|
||||
</div>
|
||||
<div class="invoice-details">
|
||||
<table class="table table-sm table-responsive ">
|
||||
<tr><td></td><td class="text-end"></td><td class="text-end"><img class="rounded-soft" src="{{ dealer.logo.url }}" alt="" style="height: 2cm; width: 2cm; border-radius: 0.3333333333rem;"/></td></tr>
|
||||
<tr><td><strong>{{ dealer.name }}</strong></td><td></td><td class="text-end"><strong>{{ dealer.arabic_name }}</strong></td></tr>
|
||||
<tr><td><strong>Address</strong></td><td>{{ dealer.address }}</td><td class="text-end"><strong>العنوان</strong></td></tr>
|
||||
<tr><td><strong>Phone</strong></td><td>{{ dealer.phone_number }}</td><td class="text-end"><strong>جوال</strong></td></tr>
|
||||
<tr><td><strong>VAT Number</strong></td><td>{{ dealer.vrn }}</td><td class="text-end"><strong>الرقم الضريبي</strong></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
<div class="invoice-details">
|
||||
<table class="table table-sm table-bordered">
|
||||
<tr><td class="ps-1"><strong>Invoice Number</strong></td><td class="text-center">{{ invoice.invoice_number }}</td><td class="text-end p-1"><strong>رقم الفاتورة</strong></td></tr>
|
||||
<tr><td class="ps-1"><strong>Date</strong></td><td class="text-center">{{ invoice.date_in_review }}</td><td class="text-end p-1"><strong>التاريخ</strong></td></tr>
|
||||
<tr><td class="ps-1"><strong>Customer</strong></td><td class="text-center">{{ invoice.customer.customer_name }}</td><td class="text-end p-1"><strong>العميل</strong></td></tr>
|
||||
<tr><td class="ps-1"><strong>VAT Number</strong></td><td class="text-center"></td><td class="text-end p-1"><strong>الرقم الضريبي</strong></td></tr>
|
||||
<tr><td class="ps-1"><strong>Email</strong></td><td class="text-center">{{ invoice.customer.email }}</td><td class="text-end p-1"><strong>البريد الالكتروني</strong></td></tr>
|
||||
<tr><td class="ps-1"><strong>Terms</strong></td><td class="text-center">{{ invoice.get_terms_display }}</td><td class="text-end p-1"><strong>طريقة الدفع</strong></td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
<!-- Items Table -->
|
||||
<div class="invoice-table mt-3">
|
||||
<table class="table table-bordered">
|
||||
<div class="invoice-table">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><small class="fs-10">Make</small><br><small class="fs-10">الصنف</small></th>
|
||||
<th><small class="fs-10">Model</small><br><small class="fs-10">الموديل</small></th>
|
||||
<th><small class="fs-10">Year</small><br><small class="fs-10">السنة</small></th>
|
||||
<th><small class="fs-10">VIN</small><br><small class="fs-10">رقم الهيكل</small></th>
|
||||
<th><small class="fs-10">Quantity</small><br><small class="fs-10">العدد</small></th>
|
||||
<th><small class="fs-10">Unit Price</small><br><small class="fs-10">سعر الوحدة</small></th>
|
||||
<th><small class="fs-10">Total</small><br><small class="fs-10">الإجمالي</small></th>
|
||||
</tr>
|
||||
<th class="text-wrap text-center">Make / الصانع</th>
|
||||
<th class="text-wrap text-center">Model / الموديل</th>
|
||||
<th class="text-wrap text-center">Year / السنة</th>
|
||||
<th class="text-wrap text-center">VIN / الهيكل</th>
|
||||
<th class="text-wrap text-center">Quantity / الكمية</th>
|
||||
<th class="text-wrap text-center">Unit Price / سعر الوحدة</th>
|
||||
<th class="text-wrap text-center">VAT / الضريبة</th>
|
||||
<th class="text-wrap text-center">Total / الإجمالي</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in data.cars %}
|
||||
<tr>
|
||||
<td class="">{{item.make}}</td>
|
||||
<td class="">{{item.model}}</td>
|
||||
<td class="">{{item.year}}</td>
|
||||
<td class="">{{item.vin}}</td>
|
||||
<td class="align-middle">{{item.quantity}}</td>
|
||||
<td class="align-middle ps-5">{{item.selling_price}}</td>
|
||||
<td class="align-middle text-body-tertiary fw-semibold">{{item.total}}</td>
|
||||
<td class="ps-1">{{ item.make }}</td>
|
||||
<td class="ps-1">{{ item.model }}</td>
|
||||
<td class="text-center">{{ item.year }}</td>
|
||||
<td class="ps-1">{{ item.vin }}</td>
|
||||
<td class="text-center">{{ item.quantity|floatformat:-1 }}</td>
|
||||
<td class="text-center">{{ item.selling_price }}</td>
|
||||
<td class="text-center">{{ item.vat_amount }}</td>
|
||||
<td class="text-center">{{ item.total }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="invoice-note">Additional Services \ الخدمات الإضافية</div>
|
||||
<div class="invoice-table">
|
||||
<table class="table table-sm table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-wrap text-center">Type / النوع</th>
|
||||
<th class="text-wrap text-center">Quantity / الكمية</th>
|
||||
<th class="text-wrap text-center">Unit Price / سعر الوحدة</th>
|
||||
<th class="text-wrap text-center">VAT / الضريبة</th>
|
||||
<th class="text-wrap text-center">Total / الإجمالي</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in data.cars %}
|
||||
<tr>
|
||||
<td class="ps-1">{{ item }}</td>
|
||||
<td class="text-center">{{ item.quantity|floatformat:-1 }}</td>
|
||||
<td class="text-center">{{ item.selling_price }}</td>
|
||||
<td class="text-center">{{ item.vat_amount }}</td>
|
||||
<td class="text-center">{{ item.total }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-end">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm table-responsive ">
|
||||
<tr>
|
||||
<td class="text-start ps-1"><strong class="fs-9">VAT</strong></td>
|
||||
<td class="text-center"><span class="fs-9">{{ data.total_vat_amount }} {{ CURRENCY }}</span></td>
|
||||
<td class="text-end"><strong class="fs-9">ضريبة القيمة المضافة</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-start ps-1"><strong class="fs-9">Total</strong></td>
|
||||
<td class="text-center"><span class="fs-9">{{ data.grand_total }} {{ CURRENCY }}</span></td>
|
||||
<td class="text-end"><strong class="fs-9">الإجمالي</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
<!-- Additional Charges (VAT and Services) -->
|
||||
<div class="additional-charges">
|
||||
<td class="text-end" colspan="3"><span class="fs-9 fw-bold">كتابةً: </span><span class="fs-9">{{ data.grand_total|num_to_words }} {{ CURRENCY }}</span></td>
|
||||
|
||||
<p><strong>VAT/ضريبة القيمة المضافة ({{vat}}%):</strong> <span class="highlight">{{data.vat|percentage}} {{ _("SAR") }}</span></p>
|
||||
<p><strong>Additional Services/ الخدمات الإضافية</strong>
|
||||
<br>
|
||||
{% for service in data.additional_services %}
|
||||
<span class="highlight">{{service.name}} - {{service.price}} {{ _("SAR") }}</span><br>
|
||||
{% endfor %}
|
||||
</p>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Total -->
|
||||
<div class="invoice-total">
|
||||
<p><strong>Total/الإجمالي</strong> <span class="highlight">{{data.grand_total}} {{ _("SAR") }}</span></p>
|
||||
<div class="footer-note d-flex justify-content-between align-items-end">
|
||||
<div class="logo-img text-center">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" style="width: 10mm; height: 10mm;"/>
|
||||
<p class="fs-11 fw-bold">Haikal | هيكل</p>
|
||||
</div>
|
||||
<p class="fs-11">Powered by <a class="text-decoration-none" href="https://tenhal.sa" style="color: #112e40;">TENHAL | تنحل</a></p>
|
||||
</div>
|
||||
|
||||
<!-- Footer Note -->
|
||||
<div class="footer-note">
|
||||
<p><small class="text-end">تواصل معنا <a href="mailto:haikal@tenhal.sa">haikal@tenhal.sa</a>.</small>
|
||||
<small class="text-start">Contact US <a href="mailto:haikal@tenhal.sa">haikal@tenhal.sa</a>.</small></p>
|
||||
<p>Made by TENHAL.SA</p>
|
||||
</div>
|
||||
<!-- Button row -->
|
||||
</div>
|
||||
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
|
||||
<script src="{% static 'vendors/bootstrap/bootstrap.min.js' %}"></script>
|
||||
<script src="{% static 'js/html2pdf.bundle.min.js' %}"></script>
|
||||
<script>
|
||||
document.getElementById('download-pdf').addEventListener('click', function () {
|
||||
const element = document.getElementById('invoice-content');
|
||||
|
||||
const options = {
|
||||
html2pdf().from(document.getElementById('invoice-content')).set({
|
||||
margin: 0,
|
||||
filename: '{{invoice.invoice_number}}.pdf',
|
||||
filename: "{{ invoice.invoice_number_invoice.customer.name_invoice.date_in_review }}.pdf",
|
||||
image: { type: 'jpeg', quality: 0.98 },
|
||||
html2canvas: {
|
||||
scale: 2,
|
||||
scrollX: 0,
|
||||
scrollY: 0,
|
||||
useCORS: true,
|
||||
},
|
||||
html2canvas: { scale: 2 },
|
||||
jsPDF: { unit: 'mm', format: 'a4', orientation: 'portrait' }
|
||||
};
|
||||
|
||||
html2pdf().from(element).set(options).save();
|
||||
}).save();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user