Compare commits
7 Commits
2a8e2bd276
...
f10b9311bf
| Author | SHA1 | Date | |
|---|---|---|---|
| f10b9311bf | |||
| c39f2eb068 | |||
| 3a08268c12 | |||
| d87ed6d454 | |||
| 10d48ca47d | |||
| 536bdc4cb9 | |||
| 5099bdcb79 |
5
.gitignore
vendored
5
.gitignore
vendored
@ -163,8 +163,11 @@ GitHub.sublime-settings
|
||||
.history
|
||||
|
||||
|
||||
static-copy
|
||||
static
|
||||
static/*
|
||||
staticfiles
|
||||
media
|
||||
tmp
|
||||
logs
|
||||
logs
|
||||
static/testdir
|
||||
@ -17,31 +17,17 @@ import django
|
||||
|
||||
django.setup()
|
||||
|
||||
|
||||
from django.urls import path
|
||||
from channels.routing import ProtocolTypeRouter, URLRouter
|
||||
from whitenoise import WhiteNoise
|
||||
from channels.auth import AuthMiddlewareStack
|
||||
from api import routing
|
||||
from inventory.notifications.sse import NotificationSSEApp
|
||||
from django.urls import re_path
|
||||
from django.core.asgi import get_asgi_application
|
||||
from django.contrib.staticfiles.handlers import ASGIStaticFilesHandler
|
||||
from pathlib import Path
|
||||
# application = ProtocolTypeRouter(
|
||||
# {
|
||||
# "http": get_asgi_application(),
|
||||
# # "websocket": AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns)),
|
||||
# }
|
||||
# )
|
||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "car_inventory.settings")
|
||||
django.setup()
|
||||
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
# BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
app = get_asgi_application()
|
||||
|
||||
# app = WhiteNoise(app, root=str(BASE_DIR / 'staticfiles'))
|
||||
|
||||
application = ProtocolTypeRouter(
|
||||
{
|
||||
"http": AuthMiddlewareStack(
|
||||
@ -50,7 +36,7 @@ application = ProtocolTypeRouter(
|
||||
path("sse/notifications/", NotificationSSEApp()),
|
||||
re_path(
|
||||
r"", app
|
||||
), # All other routes go to Django
|
||||
),
|
||||
]
|
||||
)
|
||||
),
|
||||
@ -58,5 +44,5 @@ application = ProtocolTypeRouter(
|
||||
)
|
||||
|
||||
|
||||
if django.conf.settings.DEBUG:
|
||||
application = ASGIStaticFilesHandler(app)
|
||||
# if django.conf.settings.DEBUG:
|
||||
# application = ASGIStaticFilesHandler(app)
|
||||
@ -5,7 +5,7 @@ from django.urls import path, include
|
||||
from schema_graph.views import Schema
|
||||
from django.conf.urls.static import static
|
||||
from django.conf.urls.i18n import i18n_patterns
|
||||
from inventory.notifications.sse import NotificationSSEApp
|
||||
# from inventory.notifications.sse import NotificationSSEApp
|
||||
|
||||
# import debug_toolbar
|
||||
# from two_factor.urls import urlpatterns as tf_urls
|
||||
@ -33,5 +33,7 @@ urlpatterns += i18n_patterns(
|
||||
# path('', include(tf_urls)),
|
||||
)
|
||||
|
||||
# if not settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
if settings.DEBUG:
|
||||
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
urlpatterns += static(settings.STATIC_URL, document_root = settings.STATIC_ROOT)
|
||||
|
||||
|
||||
@ -1,82 +1,3 @@
|
||||
# import json
|
||||
# from django.contrib.auth.models import AnonymousUser
|
||||
# from django.contrib.auth import get_user_model
|
||||
# from django.db import close_old_connections
|
||||
# from urllib.parse import parse_qs
|
||||
# from channels.db import database_sync_to_async
|
||||
# from inventory.models import Notification
|
||||
# import asyncio
|
||||
|
||||
# @database_sync_to_async
|
||||
# def get_notifications(user, last_id):
|
||||
# return Notification.objects.filter(
|
||||
# user=user, id__gt=last_id, is_read=False
|
||||
# ).order_by("created")
|
||||
|
||||
# class NotificationSSEApp:
|
||||
# async def __call__(self, scope, receive, send):
|
||||
# if scope["type"] != "http":
|
||||
# return
|
||||
|
||||
# query_string = parse_qs(scope["query_string"].decode())
|
||||
# last_id = int(query_string.get("last_id", [0])[0])
|
||||
|
||||
# # Get user from scope if using AuthMiddlewareStack
|
||||
# user = scope.get("user", AnonymousUser())
|
||||
# if not user.is_authenticated:
|
||||
# await send({
|
||||
# "type": "http.response.start",
|
||||
# "status": 403,
|
||||
# "headers": [(b"content-type", b"text/plain")],
|
||||
# })
|
||||
# await send({
|
||||
# "type": "http.response.body",
|
||||
# "body": b"Unauthorized",
|
||||
# })
|
||||
# return
|
||||
|
||||
# await send({
|
||||
# "type": "http.response.start",
|
||||
# "status": 200,
|
||||
# "headers": [
|
||||
# (b"content-type", b"text/event-stream"),
|
||||
# (b"cache-control", b"no-cache"),
|
||||
# (b"x-accel-buffering", b"no"),
|
||||
# ]
|
||||
# })
|
||||
|
||||
# try:
|
||||
# while True:
|
||||
# close_old_connections()
|
||||
|
||||
# notifications = await get_notifications(user, last_id)
|
||||
# for notification in notifications:
|
||||
# data = {
|
||||
# "id": notification.id,
|
||||
# "message": notification.message,
|
||||
# "created": notification.created.isoformat(),
|
||||
# "is_read": notification.is_read,
|
||||
# }
|
||||
|
||||
# event_str = (
|
||||
# f"id: {notification.id}\n"
|
||||
# f"event: notification\n"
|
||||
# f"data: {json.dumps(data)}\n\n"
|
||||
# )
|
||||
|
||||
# await send({
|
||||
# "type": "http.response.body",
|
||||
# "body": event_str.encode("utf-8"),
|
||||
# "more_body": True
|
||||
# })
|
||||
|
||||
# last_id = notification.id
|
||||
|
||||
# await asyncio.sleep(2)
|
||||
|
||||
# except asyncio.CancelledError:
|
||||
# pass
|
||||
|
||||
import json
|
||||
import time
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
|
||||
@ -1003,10 +1003,10 @@ def create_po_item_upload(sender, instance, created, **kwargs):
|
||||
if instance.po_status == "fulfilled" or instance.po_status == 'approved':
|
||||
for item in instance.get_itemtxs_data()[0]:
|
||||
dealer = models.Dealer.objects.get(entity=instance.entity)
|
||||
if item.bill_model.is_paid():
|
||||
models.PoItemsUploaded.objects.get_or_create(
|
||||
dealer=dealer, po=instance, item=item, status=instance.po_status
|
||||
)
|
||||
if item.bill_model and item.bill_model.is_paid():
|
||||
models.PoItemsUploaded.objects.get_or_create(
|
||||
dealer=dealer, po=instance, item=item, status=instance.po_status
|
||||
)
|
||||
|
||||
|
||||
# @receiver(post_save, sender=models.Staff)
|
||||
|
||||
@ -342,6 +342,11 @@ urlpatterns = [
|
||||
views.CarDetailView.as_view(),
|
||||
name="car_detail",
|
||||
),
|
||||
path(
|
||||
"<slug:dealer_slug>/cars/<slug:slug>/estimate/",
|
||||
views.create_estimate_for_car,
|
||||
name="create_estimate_for_car",
|
||||
),
|
||||
path("cars/<slug:slug>/history/", views.car_history, name="car_history"),
|
||||
path(
|
||||
"<slug:dealer_slug>/cars/<slug:slug>/update/",
|
||||
@ -938,7 +943,7 @@ urlpatterns = [
|
||||
views.ItemServiceUpdateView.as_view(),
|
||||
name="item_service_update",
|
||||
),
|
||||
|
||||
|
||||
path(
|
||||
"<slug:dealer_slug>/items/services/<int:pk>/detail/",
|
||||
views.ItemServiceDetailView.as_view(),
|
||||
@ -962,7 +967,7 @@ urlpatterns = [
|
||||
),
|
||||
path(
|
||||
"<slug:dealer_slug>/items/expeneses/<uuid:pk>/detail/",
|
||||
views.ItemExpenseDetailView.as_view(),
|
||||
views.ItemExpenseDetailView.as_view(),
|
||||
name="item_expense_detail",
|
||||
),
|
||||
# Bills
|
||||
|
||||
@ -1329,7 +1329,9 @@ def get_finance_data(estimate, dealer):
|
||||
additional_services = car.get_additional_services()
|
||||
discounted_price = Decimal(car.marked_price) - discount
|
||||
vat_amount = discounted_price * vat.rate
|
||||
total_services_amount=additional_services.get("total")
|
||||
total_services_vat = sum([x[1] for x in additional_services.get("services")])
|
||||
total_services_amount_=additional_services.get("total_")
|
||||
total_vat = vat_amount + total_services_vat
|
||||
return {
|
||||
"car": car,
|
||||
@ -1340,9 +1342,16 @@ def get_finance_data(estimate, dealer):
|
||||
"discount_amount": discount,
|
||||
"additional_services": additional_services,
|
||||
"final_price": discounted_price + vat_amount,
|
||||
|
||||
|
||||
|
||||
"total_services_vat": total_services_vat,
|
||||
"total_services_amount":total_services_amount,
|
||||
"total_services_amount_":total_services_amount_,
|
||||
|
||||
"total_vat": total_vat,
|
||||
"grand_total": discounted_price + total_vat + additional_services.get("total"),
|
||||
|
||||
}
|
||||
|
||||
# totals = self.calculate_totals()
|
||||
|
||||
@ -4656,7 +4656,7 @@ def sales_list_view(request, dealer_slug):
|
||||
search_query = request.GET.get('q', None)
|
||||
if search_query:
|
||||
qs = qs.filter(
|
||||
Q(order_number__icontains=search_query)|
|
||||
Q(customer__phone_number__icontains=search_query)|
|
||||
Q(customer__customer_name__icontains=search_query)
|
||||
).distinct()
|
||||
|
||||
@ -5092,6 +5092,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
|
||||
def get_context_data(self, **kwargs):
|
||||
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
|
||||
estimate = kwargs.get("object")
|
||||
|
||||
if estimate.get_itemtxs_data():
|
||||
# calculator = CarFinanceCalculator(estimate)
|
||||
# finance_data = calculator.get_finance_data()
|
||||
@ -5099,6 +5100,8 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
|
||||
|
||||
invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first()
|
||||
kwargs["data"] = finance_data
|
||||
kwargs["customer_obj"]=estimate.customer.customer_set.first()
|
||||
kwargs['dealer_info']=dealer
|
||||
|
||||
kwargs["invoice"] = invoice_obj
|
||||
try:
|
||||
@ -5119,29 +5122,33 @@ class EstimatePrintView(EstimateDetailView):
|
||||
It reuses the data-fetching logic from EstimateDetailView but
|
||||
uses a dedicated, stripped-down print template.
|
||||
"""
|
||||
template_name = "sales/estimates/estimate_preview.html"
|
||||
|
||||
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
|
||||
self.object = self.get_object()
|
||||
context = self.get_context_data(object=self.object)
|
||||
|
||||
|
||||
|
||||
|
||||
# lang = request.GET.get('lang', 'ar')
|
||||
|
||||
|
||||
template_path = "sales/estimates/estimate_preview.html"
|
||||
|
||||
|
||||
if request.GET.get('lang')=='en':
|
||||
template_path = "sales/estimates/estimate_preview_en.html"
|
||||
else:
|
||||
template_path = "sales/estimates/estimate_preview_ar.html"
|
||||
|
||||
|
||||
html_string = render_to_string(template_path, context)
|
||||
|
||||
|
||||
pdf_file = HTML(string=html_string).write_pdf()
|
||||
|
||||
base_url = request.build_absolute_uri('/')
|
||||
pdf_file = HTML(string=html_string, base_url=base_url).write_pdf()
|
||||
|
||||
|
||||
response = HttpResponse(pdf_file, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="estimate_{self.object.estimate_number}.pdf"'
|
||||
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@ -5522,7 +5529,7 @@ class InvoiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
|
||||
entity = dealer.entity
|
||||
staff = getattr(self.request.user, "staff", None)
|
||||
qs = []
|
||||
qs=None
|
||||
try:
|
||||
if any(
|
||||
[
|
||||
@ -5871,7 +5878,6 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
|
||||
|
||||
model = InvoiceModel
|
||||
context_object_name = "invoice"
|
||||
template_name = "sales/invoices/invoice_preview.html"
|
||||
permission_required = ["django_ledger.view_invoicemodel"]
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
@ -5881,8 +5887,37 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
|
||||
# calculator = CarFinanceCalculator(invoice)
|
||||
finance_data = get_finance_data(invoice,dealer)
|
||||
kwargs["data"] = finance_data
|
||||
kwargs["dealer"] = dealer
|
||||
kwargs["dealer_info"] = dealer
|
||||
kwargs["customer_obj"]=invoice.customer.customer_set.first()
|
||||
return super().get_context_data(**kwargs)
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
||||
self.object = self.get_object()
|
||||
context = self.get_context_data(object=self.object)
|
||||
|
||||
|
||||
# lang = request.GET.get('lang', 'ar')
|
||||
|
||||
|
||||
if request.GET.get('lang')=='en':
|
||||
template_path = "sales/invoices/invoice_preview_en.html"
|
||||
elif request.GET.get('lang')=='ar':
|
||||
template_path = "sales/invoices/invoice_preview_ar.html"
|
||||
else:
|
||||
# just for preview not for download
|
||||
return render(request,'sales/invoices/invoice_preview.html',context)
|
||||
|
||||
|
||||
html_string = render_to_string(template_path, context)
|
||||
|
||||
base_url = request.build_absolute_uri('/')
|
||||
pdf_file = HTML(string=html_string, base_url=base_url).write_pdf()
|
||||
|
||||
|
||||
response = HttpResponse(pdf_file, content_type='application/pdf')
|
||||
response['Content-Disposition'] = f'attachment; filename="invoice_{self.object.invoice_number}.pdf"'
|
||||
|
||||
return response
|
||||
|
||||
|
||||
# payments
|
||||
@ -6221,10 +6256,10 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
| Q(last_name__icontains=query)
|
||||
| Q(id_car_make__name__icontains=query)
|
||||
| Q(id_car_model__name__icontains=query)
|
||||
| Q(email__icontains=query)
|
||||
| Q(phone_number__icontains=query)
|
||||
| Q(next_action__icontains=query)
|
||||
| Q(staff__name__icontains=query)
|
||||
| Q(staff__first_name__icontains=query)
|
||||
| Q(staff__last_name__icontains=query)
|
||||
)
|
||||
|
||||
if self.request.is_dealer: # or self.request.is_manager:
|
||||
@ -10238,7 +10273,8 @@ def payment_callback(request, dealer_slug):
|
||||
# return render(request, "payment_failed.html", {"message": message})
|
||||
|
||||
@login_required
|
||||
async def sse_stream(request): # 👈 Mark as async!
|
||||
async def sse_stream(request):
|
||||
import asyncio
|
||||
def event_generator():
|
||||
last_id = int(request.GET.get("last_id", 0))
|
||||
|
||||
@ -10878,19 +10914,23 @@ def InventoryItemCreateView(request, dealer_slug):
|
||||
serie = request.POST.get("serie")
|
||||
trim = request.POST.get("trim")
|
||||
year = request.POST.get("year")
|
||||
exterior = models.ExteriorColors.objects.get(
|
||||
pk=request.POST.get("exterior")
|
||||
)
|
||||
interior = models.InteriorColors.objects.get(
|
||||
pk=request.POST.get("interior")
|
||||
)
|
||||
exterior = request.POST.get("exterior")
|
||||
interior = request.POST.get("interior")
|
||||
|
||||
make_name = models.CarMake.objects.get(pk=make)
|
||||
model_name = models.CarModel.objects.get(pk=model)
|
||||
serie_name = models.CarSerie.objects.get(pk=serie)
|
||||
trim_name = models.CarTrim.objects.get(pk=trim)
|
||||
exterior_name = models.ExteriorColors.objects.get(
|
||||
pk=request.POST.get("exterior")
|
||||
)
|
||||
interior_name = models.InteriorColors.objects.get(
|
||||
pk=request.POST.get("interior")
|
||||
)
|
||||
|
||||
inventory_name = f"{make_name.name} || {model_name.name} || {serie_name.name} || {trim_name.name} || {year} || {exterior_name.name} || {interior_name.name}"
|
||||
display_name = f"{make_name.name} {model_name.name} {serie_name.name} {trim_name.name} {year} {exterior_name.name}"
|
||||
|
||||
inventory_name = f"{make_name.name} || {model_name.name} || {serie_name.name} || {trim_name.name} || {year} || {exterior.name} || {interior.name}"
|
||||
if (
|
||||
inventory := entity.get_items_inventory()
|
||||
.filter(name=inventory_name)
|
||||
@ -10898,17 +10938,27 @@ def InventoryItemCreateView(request, dealer_slug):
|
||||
):
|
||||
messages.error(request, _("Inventory item already exists"))
|
||||
return response
|
||||
|
||||
uom = entity.get_uom_all().filter(name="Unit").first()
|
||||
if not uom:
|
||||
uom = entity.create_uom(name="Unit", unit_abbr="unit")
|
||||
entity.create_item_inventory(
|
||||
name=inventory_name,
|
||||
item = entity.create_item_inventory(
|
||||
name=display_name,
|
||||
uom_model=uom,
|
||||
item_type=ItemModel.ITEM_TYPE_MATERIAL,
|
||||
inventory_account=account,
|
||||
coa_model=coa,
|
||||
)
|
||||
item.additional_info.update(
|
||||
{
|
||||
"make": make,
|
||||
"model": model,
|
||||
"serie": serie,
|
||||
"trim": trim,
|
||||
"year": year,
|
||||
"exterior": exterior,
|
||||
"interior": interior,
|
||||
})
|
||||
item.save()
|
||||
messages.success(request, _("Inventory item created successfully"))
|
||||
return response
|
||||
|
||||
@ -11005,6 +11055,9 @@ class PurchaseOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detai
|
||||
for i in po_items_qs.values("po_total_amount", "po_item_status")
|
||||
if i["po_item_status"] != "cancelled"
|
||||
)
|
||||
items = [{"total": x.total_amount, "q": x.quantity} for x in po_model.get_itemtxs_data()[0].all()]
|
||||
po_quantity = sum(item["q"] for item in items)
|
||||
context['po_quantity']=po_quantity
|
||||
return context
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
@ -11014,18 +11067,18 @@ class PurchaseOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detai
|
||||
"item_model", "bill_model"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
if self.object.po_status == 'fulfilled':
|
||||
context['po_items_list']=po_items_qs
|
||||
context['vendor']=po_items_qs.first().bill_model.vendor
|
||||
context['dealer']=request.dealer
|
||||
|
||||
|
||||
# Check if PDF format is requested
|
||||
if request.GET.get('format') == 'pdf':
|
||||
# Use a separate, print-friendly template for the PDF
|
||||
if request.GET.get('lang')=='en':
|
||||
html_string = render_to_string(
|
||||
"purchase_orders/po_detail_en_pdf.html",
|
||||
"purchase_orders/po_detail_en_pdf.html",
|
||||
context
|
||||
)
|
||||
else:
|
||||
@ -11035,9 +11088,8 @@ class PurchaseOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detai
|
||||
)
|
||||
|
||||
|
||||
|
||||
# Use WeasyPrint to generate the PDF
|
||||
pdf = HTML(string=html_string).write_pdf()
|
||||
base_url = request.build_absolute_uri('/')
|
||||
pdf = HTML(string=html_string, base_url=base_url).write_pdf()
|
||||
|
||||
response = HttpResponse(pdf, content_type="application/pdf")
|
||||
response["Content-Disposition"] = f'attachment; filename="PO_{self.object.po_number}.pdf"'
|
||||
@ -11223,18 +11275,14 @@ def upload_cars(request, dealer_slug, pk=None):
|
||||
)
|
||||
try:
|
||||
if item:
|
||||
data = [x.strip() for x in item.item_model.name.split("||")]
|
||||
make = models.CarMake.objects.filter(is_sa_import=True).get(
|
||||
name=data[0]
|
||||
)
|
||||
model = make.carmodel_set.get(name=data[1])
|
||||
trim = models.CarTrim.objects.filter(
|
||||
name=data[3], id_car_serie__id_car_model=model.id_car_model
|
||||
).first()
|
||||
serie = trim.id_car_serie
|
||||
year = data[4]
|
||||
exterior = models.ExteriorColors.objects.get(name=data[5])
|
||||
interior = models.InteriorColors.objects.get(name=data[6])
|
||||
# data = [x.strip() for x in item.item_model.name.split("||")]
|
||||
make = models.CarMake.objects.get(pk=item.addition_info.get("make"))
|
||||
model = models.CarModel.objects.get(pk=item.addition_info.get("model"))
|
||||
trim = models.CarTrim.objects.get(pk=item.addition_info.get("trim"))
|
||||
serie = models.CarSerie.objects.get(pk=item.addition_info.get("serie"))
|
||||
year = item.addition_info.get("year")
|
||||
exterior = models.ExteriorColors.objects.get(pk=item.addition_info.get("exterior"))
|
||||
interior = models.InteriorColors.objects.get(pk=item.addition_info.get("interior"))
|
||||
receiving_date = timezone.now()
|
||||
vendor_model = item.bill_model.vendor
|
||||
vendor = models.Vendor.objects.get(vendor_model=vendor_model)
|
||||
|
||||
@ -35,7 +35,8 @@
|
||||
<div class="row my-5">
|
||||
<div class="card rounded ">
|
||||
<div class="card-header ">
|
||||
<p class="mb-0">{{ _("Group Details") }}</p>
|
||||
<p class="mb-2">{{ _("Group Details") }}</p>
|
||||
<a class="btn btn-phoenix-secondary " href="{% url 'group_list' request.dealer.slug %}">{% trans "Group List" %}</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
|
||||
@ -87,9 +87,13 @@
|
||||
didOpen: (toast) => {
|
||||
toast.onmouseenter = Swal.stopTimer;
|
||||
toast.onmouseleave = Swal.resumeTimer;
|
||||
} });
|
||||
}
|
||||
});
|
||||
|
||||
{% with last_notif=notifications_|last %}
|
||||
let lastNotificationId = {{ last_notif.id|default:0 }};
|
||||
{% endwith %}
|
||||
|
||||
let lastNotificationId = {{ notifications_.last.id|default:0 }};
|
||||
let seenNotificationIds = new Set();
|
||||
let counter = document.getElementById('notification-counter');
|
||||
let notificationsContainer = document.getElementById('notifications-container');
|
||||
@ -100,7 +104,6 @@
|
||||
let initialUnreadCount = {{ notifications_.count|default:0 }};
|
||||
updateCounter(initialUnreadCount);
|
||||
|
||||
|
||||
fetchInitialNotifications();
|
||||
|
||||
function fetchInitialNotifications() {
|
||||
@ -108,29 +111,22 @@
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.notifications && data.notifications.length > 0) {
|
||||
|
||||
lastNotificationId = data.notifications[0].id;
|
||||
|
||||
seenNotificationIds = new Set();
|
||||
|
||||
let unreadCount = 0;
|
||||
|
||||
data.notifications.forEach(notification => {
|
||||
seenNotificationIds.add(notification.id);
|
||||
if (!notification.is_read) {
|
||||
unreadCount++;
|
||||
}
|
||||
if (!notification.is_read) unreadCount++;
|
||||
});
|
||||
|
||||
renderNotifications(data.notifications);
|
||||
|
||||
updateCounter(unreadCount);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
connectSSE();
|
||||
}, 5000);
|
||||
}
|
||||
// Always connect SSE after initial load
|
||||
setTimeout(() => {
|
||||
connectSSE();
|
||||
}, 1000);
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching initial notifications:', error);
|
||||
@ -143,12 +139,12 @@
|
||||
eventSource.close();
|
||||
}
|
||||
|
||||
// ✅ FIXED URL HERE
|
||||
eventSource = new EventSource("/sse/notifications/?last_id=" + lastNotificationId);
|
||||
|
||||
eventSource.addEventListener('notification', function(e) {
|
||||
try {
|
||||
const data = JSON.parse(e.data);
|
||||
|
||||
if (seenNotificationIds.has(data.id)) return;
|
||||
seenNotificationIds.add(data.id);
|
||||
|
||||
@ -158,6 +154,11 @@
|
||||
|
||||
updateCounter('increment');
|
||||
|
||||
if (!notificationsContainer) {
|
||||
console.warn("Notification container missing, can't render SSE event");
|
||||
return;
|
||||
}
|
||||
|
||||
const notificationElement = createNotificationElement(data);
|
||||
notificationsContainer.insertAdjacentHTML('afterbegin', notificationElement);
|
||||
|
||||
@ -168,7 +169,7 @@
|
||||
|
||||
Toast.fire({
|
||||
icon: 'info',
|
||||
html:`${data.message}`
|
||||
html: `${data.message}`
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
@ -220,7 +221,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
`;
|
||||
}
|
||||
|
||||
function updateCounter(action) {
|
||||
@ -231,12 +232,14 @@
|
||||
if (notificationCountDiv) {
|
||||
notificationCountDiv.innerHTML = `
|
||||
<span class="badge bg-danger rounded-pill" id="notification-counter" style="position: absolute; top: 8px; right: 3px; font-size: 0.50rem;">0</span>
|
||||
`;
|
||||
`;
|
||||
counter = document.getElementById('notification-counter');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!counter) return;
|
||||
|
||||
let currentCount = parseInt(counter.textContent) || 0;
|
||||
|
||||
if (action === 'increment') {
|
||||
@ -294,11 +297,11 @@
|
||||
notificationCard.closest('.notification-card').classList.add('fade-out');
|
||||
setTimeout(() => {
|
||||
notificationCard.closest('.notification-card').remove();
|
||||
}, 200);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</script>
|
||||
@ -125,7 +125,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
|
||||
<td class="name align-middle white-space-nowrap ps-0 px-1">
|
||||
<td class="name align-middle white-space-nowrap px-1">
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<a class="fs-8 fw-bold"
|
||||
@ -144,7 +144,7 @@
|
||||
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">
|
||||
{{ org.created|date }}
|
||||
</td>
|
||||
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
|
||||
<td class="align-middle white-space-nowrap text-end pe-2 ps-4">
|
||||
{% if perms.inventory.change_organization or perms.inventory.delete_organization %}
|
||||
<div class="btn-reveal-trigger position-static">
|
||||
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10"
|
||||
|
||||
@ -19,9 +19,9 @@
|
||||
{% if po_model.po_status == 'fulfilled' %}
|
||||
<div class="ms-2">
|
||||
<a class="btn btn-phoenix-primary my-2 mx-2"
|
||||
href="{{ request.path }}?format=pdf&lang=en"><i class="fa-solid fa-arrow-down me-1"></i>{% trans 'Download PO ENG' %}</a>
|
||||
href="{{ request.path }}?format=pdf&lang=en"><i class="fa-solid fa-arrow-down me-1"></i>{% trans 'Download PO EN' %}</a>
|
||||
<a class="btn btn-phoenix-primary my-2"
|
||||
href="{{ request.path }}?format=pdf&lang=ar"><i class="fa-solid fa-arrow-down me-1"></i>{% trans 'Download PO ARB' %}</a>
|
||||
href="{{ request.path }}?format=pdf&lang=ar"><i class="fa-solid fa-arrow-down me-1"></i>{% trans 'Download PO AR' %}</a>
|
||||
</diV>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% load tenhal_tag %}
|
||||
{% load custom_filters %}
|
||||
|
||||
{% load i18n static custom_filters num2words_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
@ -109,50 +109,94 @@
|
||||
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
border-top: 1px solid #ddd;
|
||||
|
||||
padding-top: 15px;
|
||||
margin-top: 30px;
|
||||
margin: 0 20mm;
|
||||
|
||||
}
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.footer-logo {
|
||||
text-align: center;
|
||||
}
|
||||
.footer-logo img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
.footer-logo p {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer-powered p {
|
||||
font-size: 11px;
|
||||
}
|
||||
.footer-powered span {
|
||||
font-weight: lighter;
|
||||
}
|
||||
.footer-powered a {
|
||||
color: #112e40;
|
||||
text-decoration: none;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document-header">
|
||||
|
||||
<div>
|
||||
<h1>{{ dealer.name }}</h1>
|
||||
|
||||
<address>
|
||||
العنوان : {{ dealer.address }}<br>
|
||||
البريد الإلكتروني : {{ dealer.user.email }}<br>
|
||||
الهاتف : {{ dealer.phone_number }}<br>
|
||||
رقم السجل التجاري : {{ dealer.crn }} | رقم ضريبة القيمة المضافة : {{ dealer.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="dealer-logo ">
|
||||
{% if dealer.logo %}
|
||||
<img class="rounded-soft"
|
||||
style="max-width:100px;
|
||||
max-height:100px"
|
||||
src="{{dealer.logo.url|default:'' }}"
|
||||
alt="Dealer Logo" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>أمر شراء</h1>
|
||||
<h2 style="font-size: 18px;">{{ po_model.po_number }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<h1>{{ dealer.name }}</h1>
|
||||
<address>
|
||||
العنوان: {{ dealer.address }}<br>
|
||||
البريد الإلكتروني: {{ dealer.user.email }}<br>
|
||||
الهاتف: {{ dealer.phone_number }}<br>
|
||||
رقم السجل التجاري: {{ dealer.crn }} | رقم ضريبة القيمة المضافة: {{ dealer.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>التفاصيل:</h2>
|
||||
<p><span class="label">رقم أمر الشراء:</span> {{ po_model.po_number }}</p>
|
||||
<p><span class="label">تاريخ الإصدار:</span> {{ po_model.date_fulfilled|date:"Y/m/d" }}</p>
|
||||
<p><span class="label">رقم أمر الشراء : </span> {{ po_model.po_number }}</p>
|
||||
<p><span class="label">تاريخ الإصدار : </span> {{ po_model.date_fulfilled|date:"Y/m/d" }}</p>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>يُرسل إلى:</h2>
|
||||
<p><span class="label">المورد:</span> {{ vendor.vendor_name }}</p>
|
||||
<p><span class="label">البريد الإلكتروني:</span> {{ vendor.email }}</p>
|
||||
<p><span class="label">الهاتف:</span> {{ vendor.phone }}</p>
|
||||
<p><span class="label">العنوان:</span> {{ vendor.address_1 }}</p>
|
||||
<p><span class="label">المورد : </span> {{ vendor.vendor_name }}</p>
|
||||
<p><span class="label">البريد الإلكتروني : </span> {{ vendor.email }}</p>
|
||||
<p><span class="label">الهاتف : </span> {{ vendor.phone }}</p>
|
||||
<p><span class="label">العنوان : </span> {{ vendor.address_1 }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
@ -170,13 +214,24 @@
|
||||
|
||||
<div class="document-details" style="margin-top: 10px; border-top: 1px solid #ddd; padding-top: 10px;">
|
||||
<div class="section text-right" style="width: 100%;">
|
||||
<p class="h4"><span class="label">المبلغ الإجمالي:</span> {{ po_total_amount|floatformat:'2g' }}<span class="icon-saudi_riyal"></span></p>
|
||||
<p class="h4"><span class="label">المبلغ الإجمالي : </span> {{ po_total_amount|currency_format }}<span class="icon-saudi_riyal"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="border-bottom:1px solid #ccc; ">
|
||||
|
||||
<div class="document-footer">
|
||||
<div class="footer-logo">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p>
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-powered">
|
||||
<p>
|
||||
<span>Powered by </span>
|
||||
<a href="https://tenhal.sa"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-footer footer-flex">
|
||||
<p>© <strong>هيكل</strong> {% now "Y" %} جميع الحقوق محفوظة.</p>
|
||||
<p><strong>تنحل</strong>مدعوم من</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -103,22 +103,49 @@
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Footer Styles */
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
border-top: 1px solid #ddd;
|
||||
|
||||
padding-top: 15px;
|
||||
margin-top: 30px;
|
||||
margin: 0 20mm;
|
||||
|
||||
}
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.footer-logo {
|
||||
text-align: center;
|
||||
}
|
||||
.footer-logo img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
.footer-logo p {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer-powered p {
|
||||
font-size: 11px;
|
||||
}
|
||||
.footer-powered span {
|
||||
font-weight: lighter;
|
||||
}
|
||||
.footer-powered a {
|
||||
color: #112e40;
|
||||
text-decoration: none;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@ -126,12 +153,25 @@
|
||||
<div>
|
||||
<h1>{{ dealer.name }}</h1>
|
||||
<address>
|
||||
Address: {{ dealer.address}}<br>
|
||||
Email: {{ dealer.user.email }}<br>
|
||||
Phone: {{dealer.phone_number }}<br>
|
||||
CRN: {{dealer.crn}} | VRN: {{dealer.vrn}}
|
||||
Address : {{ dealer.address}}<br>
|
||||
Email : {{ dealer.user.email }}<br>
|
||||
Phone : {{dealer.phone_number }}<br>
|
||||
CRN : {{dealer.crn}} | VRN : {{dealer.vrn}}
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="dealer-logo ">
|
||||
{% if dealer.logo %}
|
||||
<img class="rounded-soft"
|
||||
style="max-width:100px;
|
||||
max-height:100px"
|
||||
src="{{dealer.logo.url|default:'' }}"
|
||||
alt="Dealer Logo" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>PURCHASE ORDER</h1>
|
||||
<h2 style="font-size: 18px;">{{ po_model.po_number }}</h2>
|
||||
@ -141,15 +181,15 @@
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>BILL TO:</h2>
|
||||
<p><span class="label">Vendor: {{vendor.vendor_name}}</span> </p>
|
||||
<p><span class="label">Email: {{vendor.email}}</span> </p>
|
||||
<p><span class="label">Phone: {{vendor.phone}}</span> </p>
|
||||
<p><span class="label">Address: {{vendor.address_1}}</span> </p>
|
||||
<p><span class="label">Vendor : {{vendor.vendor_name}}</span> </p>
|
||||
<p><span class="label">Email : {{vendor.email}}</span> </p>
|
||||
<p><span class="label">Phone : {{vendor.phone}}</span> </p>
|
||||
<p><span class="label">Address : {{vendor.address_1}}</span> </p>
|
||||
</div>
|
||||
<div class="section">
|
||||
<h2>DETAILS:</h2>
|
||||
<p><span class="label">PO Number:</span> {{ po_model.po_number }}</p>
|
||||
<p><span class="label">Issue Date:</span> {{ po_model.date_fulfilled|date:"F j, Y" }}</p>
|
||||
<p><span class="label">PO Number : </span> {{ po_model.po_number }}</p>
|
||||
<p><span class="label">Issue Date : </span> {{ po_model.date_fulfilled|date:"F j, Y" }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-12">
|
||||
@ -161,14 +201,25 @@
|
||||
<div class="document-details" style="margin-top: 30px;">
|
||||
|
||||
<div class="section text-right">
|
||||
<p class="h4"><span class="label">Total Amount:</span> {{ po_total_amount|floatformat:'2g' }}<span class="icon-saudi_riyal"></span></p>
|
||||
<p class="h4"><span class="label">Total Amount : </span> {{ po_total_amount|currency_format }}<span class="icon-saudi_riyal"></span></p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="border-bottom:1px solid #ccc; ">
|
||||
|
||||
<div class="document-footer footer-flex">
|
||||
<p>© {% now "Y" %} All rights reserved <strong>Haikal</strong> </p>
|
||||
<p>Powered By <strong>Tenhal</strong></p>
|
||||
<div class="document-footer">
|
||||
<div class="footer-logo">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p>
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-powered">
|
||||
<p>
|
||||
<span>Powered by </span>
|
||||
<a href="https://tenhal.sa"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -6,7 +6,7 @@
|
||||
<tr class="has-text-centered bg-body-highlight">
|
||||
<th>البند</th>
|
||||
<th>سعر الوحدة</th>
|
||||
<th>كمية أمر الشراء</th>
|
||||
<th>الكمية</th>
|
||||
<th>المبلغ</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -14,7 +14,7 @@
|
||||
{% for item in po_items_list %}
|
||||
<tr>
|
||||
<td>{{ item.item_model }}</td>
|
||||
<td class="has-text-centered">{{ item.po_unit_cost }}</td>
|
||||
<td class="has-text-centered">{{ item.po_unit_cost|currency_format }}</td>
|
||||
<td class="has-text-centered">{{ item.po_quantity }}</td>
|
||||
<td class="has-text-centered">
|
||||
<span class="icon-saudi_riyal"></span>{{ item.po_total_amount | currency_format }}
|
||||
@ -26,12 +26,12 @@
|
||||
<tfoot>
|
||||
<tr>
|
||||
|
||||
<td>إجمالي مبلغ أمر الشراء</td>
|
||||
<td></td>
|
||||
<td class="has-text-left">إجمالي مبلغ أمر الشراء</td>
|
||||
<td class="has-text-left">{{po_quantity}}</td>
|
||||
<td class="has-text-weight-bold has-text-centered">
|
||||
<span class="icon-saudi_riyal"></span>{{ po_model.po_amount | currency_format }}
|
||||
</td>
|
||||
<td></td>
|
||||
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
{% for item in po_items_list %}
|
||||
<tr>
|
||||
<td>{{ item.item_model }}</td>
|
||||
<td class="has-text-centered">{{ item.po_unit_cost }}</td>
|
||||
<td class="has-text-centered">{{ item.po_unit_cost|currency_format}}</td>
|
||||
<td class="has-text-centered">{{ item.po_quantity }}</td>
|
||||
<td class="has-text-centered">
|
||||
<span class="icon-saudi_riyal"></span>{{ item.po_total_amount | currency_format }}
|
||||
@ -26,13 +26,15 @@
|
||||
<tfoot>
|
||||
<tr>
|
||||
|
||||
<td></td>
|
||||
<td class="has-text-right">Total PO Amount</td>
|
||||
<td>Total PO Amount</td>
|
||||
<td class="has-text-right"></td>
|
||||
|
||||
<td>{{po_quantity}}</td>
|
||||
|
||||
<td class="has-text-weight-bold has-text-centered">
|
||||
<span class="icon-saudi_riyal"></span>{{ po_model.po_amount | currency_format }}
|
||||
</td>
|
||||
<td></td>
|
||||
|
||||
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
||||
@ -134,11 +134,17 @@
|
||||
{% if perms.django_ledger.change_estimatemodel %}
|
||||
<a href="{% url 'send_email' request.dealer.slug estimate.pk %}"
|
||||
class="btn btn-phoenix-primary me-2"><span class="fa-regular fa-paper-plane me-sm-2"></span><span class="d-none d-sm-inline-block">{% trans 'Send Quotation' %}</span></a>
|
||||
<a href="{% url 'estimate_print' request.dealer.slug estimate.pk %}"
|
||||
<a href="{% url 'estimate_print' request.dealer.slug estimate.pk %}?lang=en"
|
||||
class="btn btn-phoenix-secondary"
|
||||
target="_blank">
|
||||
<span class="d-none d-sm-inline-block"><i class="fas fa-print me-2"></i>{% trans 'Print' %}</span>
|
||||
<span class="d-none d-sm-inline-block"><i class="fas fa-print me-2"></i>{% trans 'Print EN' %}</span>
|
||||
</a>
|
||||
<a href="{% url 'estimate_print' request.dealer.slug estimate.pk %}?lang=ar"
|
||||
class="btn btn-phoenix-secondary"
|
||||
target="_blank">
|
||||
<span class="d-none d-sm-inline-block"><i class="fas fa-print me-2"></i>{% trans 'Print AR' %}</span>
|
||||
</a>
|
||||
|
||||
{% endif %}
|
||||
{% if estimate.sale_orders.first %}
|
||||
<!--if sale order exist-->
|
||||
|
||||
@ -1,264 +0,0 @@
|
||||
{% load i18n static custom_filters num2words_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% trans "Quotation" %}</title>
|
||||
<style>
|
||||
/* General Body and Font Styles */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Page Layout and Margins for PDF */
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 20mm;
|
||||
@top-left {
|
||||
content: "صفحة " counter(page) " من " counter(pages);
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.document-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.document-header .logo {
|
||||
max-width: 150px;
|
||||
height: auto;
|
||||
}
|
||||
.document-header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #0056b3; /* A professional blue */
|
||||
}
|
||||
.document-header address {
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Document Details Section */
|
||||
.document-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.document-details .section {
|
||||
width: 48%;
|
||||
}
|
||||
.document-details h2 {
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
}
|
||||
.document-details p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.document-details .label {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table tfoot td {
|
||||
border-top: 2px solid #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-right {
|
||||
text-align: left;
|
||||
}
|
||||
.text-left {
|
||||
text-align: right;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 15px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document-header">
|
||||
<div>
|
||||
<h1>{{ request.dealer.name }}</h1>
|
||||
<address>
|
||||
العنوان: {{ request.dealer.address }}<br>
|
||||
البريد الإلكتروني: {{ request.dealer.user.email }}<br>
|
||||
الهاتف: {{ request.dealer.phone_number }}<br>
|
||||
رقم السجل التجاري: {{ request.dealer.crn }} | الرقم الضريبي: {{ request.dealer.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
<div>
|
||||
<h1>عرض سعر</h1>
|
||||
<h2 style="font-size: 18px;">{{ estimate.estimate_number }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>{{ estimate.customer.customer_name }}: إلى</h2>
|
||||
<p><span class="label">العميل: {{ estimate.customer.customer_name }}</span></p>
|
||||
<p><span class="label">البريد الإلكتروني: {{ estimate.customer.email |default:"N/A"}}</span></p>
|
||||
<p><span class="label">الهاتف: {{ estimate.customer.phone_number|default:"N/A" }}</span></p>
|
||||
<p><span class="label">العنوان: {{ estimate.customer.address_1|default:"N/A" }}</span></p>
|
||||
</div>
|
||||
<div class="section text-left">
|
||||
<h2>التفاصيل:</h2>
|
||||
<p><span class="label">رقم عرض السعر:</span> {{ estimate.estimate_number }}</p>
|
||||
<p><span class="label">تاريخ الإصدار:</span> {{ estimate.date_approved|date:"Y/m/d" }}</p>
|
||||
<p><span class="label">طريقة الدفع:</span> {{ estimate.get_terms_display }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="table-responsive">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="fs-9 fw-thin">تفاصيل السيارة</span>
|
||||
</div>
|
||||
<table class="table table-sm table-bordered m-1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">الصانع</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">الموديل</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">السلسلة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">الفئة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">السنة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">رقم الهيكل</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">الكمية</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">سعر الوحدة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">الخصم</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">الضريبة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">الإجمالي</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_make.name }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_model.name }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_serie.name }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_trim.name }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.car.year }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.vin }}</td>
|
||||
<td class="text-center fs-10 align-content-center">1</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.car.marked_price|floatformat:2 }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.discount_amount|floatformat:2 }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.vat_amount|floatformat:2 }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.final_price|floatformat:2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if data.additional_services %}
|
||||
<div class="col-lg-12">
|
||||
<div class="table-responsive">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="fs-9 fw-thin">الخدمات الإضافية</span>
|
||||
</div>
|
||||
<table class="table table-sm table-bordered m-1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center fs-10 align-content-center">النوع</th>
|
||||
<th class="text-center fs-10 align-content-center">القيمة</th>
|
||||
<th class="text-center fs-10 align-content-center">ضريبة الخدمة</th>
|
||||
<th class="text-center fs-10 align-content-center">الإجمالي</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in data.additional_services.services %}
|
||||
<tr>
|
||||
<td class="ps-1 text-center fs-10 align-content-center">{{ service.0.name }}</td>
|
||||
<td class="ps-1 text-center fs-10 align-content-center">{{ service.0.price|floatformat:2 }}</td>
|
||||
<td class="ps-1 text-center fs-10 align-content-center">{{ service.1|floatformat:2 }}</td>
|
||||
<td class="ps-1 text-center fs-10 align-content-center">{{ service.0.price_|floatformat:2 }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="document-details" style="margin-top: 30px;">
|
||||
<div class="section text-left">
|
||||
<p><span class="label">إجمالي ضريبة القيمة المضافة:</span> {{ data.total_vat|floatformat:'2g' }}</p>
|
||||
<p><span class="label">الإجمالي الكلي:</span> {{ data.grand_total|floatformat:'2g' }}</p>
|
||||
<p><span class="label">كتابةً:</span> {{ data.grand_total|num_to_words }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-footer footer-flex">
|
||||
<p>© {% now "Y" %} جميع الحقوق محفوظة <strong>هيكل</strong></p>
|
||||
<p>بواسطة <strong>تنحل</strong></p>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
357
templates/sales/estimates/estimate_preview_ar.html
Normal file
357
templates/sales/estimates/estimate_preview_ar.html
Normal file
@ -0,0 +1,357 @@
|
||||
{% load i18n static custom_filters num2words_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>عرض سعر</title>
|
||||
|
||||
<style>
|
||||
/* General Body and Font Styles */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Page Layout and Margins for PDF */
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 20mm;
|
||||
@top-left {
|
||||
content: "صفحة " counter(page) " من " counter(pages);
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.document-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.document-header .logo {
|
||||
max-width: 150px;
|
||||
height: auto;
|
||||
}
|
||||
.document-header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #0056b3;
|
||||
}
|
||||
.document-header address {
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Document Details Section */
|
||||
.document-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.document-details .section {
|
||||
width: 48%;
|
||||
}
|
||||
.document-details h2 {
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
}
|
||||
.document-details p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.document-details .label {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.table {
|
||||
width: 80%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px auto; /* Centering the table */
|
||||
}
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table tfoot td {
|
||||
border-top: 2px solid #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-right {
|
||||
text-align: left;
|
||||
}
|
||||
.text-left {
|
||||
text-align: right;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;2px solid #333;
|
||||
}
|
||||
|
||||
/* Responsive and layout helpers */
|
||||
.table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.table-header span {
|
||||
font-size: 9px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
|
||||
padding-top: 15px;
|
||||
margin: 0 20mm;
|
||||
|
||||
}
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.footer-logo {
|
||||
text-align: center;
|
||||
}
|
||||
.footer-logo img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
.footer-logo p {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer-powered p {
|
||||
font-size: 11px;
|
||||
}
|
||||
.footer-powered span {
|
||||
font-weight: lighter;
|
||||
}
|
||||
.footer-powered a {
|
||||
color: #112e40;
|
||||
text-decoration: none;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="document-header">
|
||||
<div>
|
||||
<h1>{{ dealer_info.name }}</h1>
|
||||
<address>
|
||||
العنوان : {{ dealer_info.address }}<br>
|
||||
البريد الإلكتروني : {{ dealer_info.user.email }}<br>
|
||||
الهاتف : {{ dealer_info.phone_number }}<br>
|
||||
رقم السجل التجاري : {{dealer_info.crn }}<br>
|
||||
الرقم الضريبي : {{ dealer_info.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="dealer-logo ">
|
||||
{% if dealer_info.logo %}
|
||||
<img class="rounded-soft"
|
||||
style="max-width:100px;
|
||||
max-height:100px"
|
||||
src="{{dealer_info.logo.url|default:'' }}"
|
||||
alt="Dealer Logo" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>عرض سعر</h1>
|
||||
<h2 style="font-size: 18px;">{{ estimate.estimate_number }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>{{ customer_obj.full_name }}: إلى</h2>
|
||||
<p><span class="label">العميل : {{ customer_obj.full_name }}</span></p>
|
||||
<p><span class="label">البريد الإلكتروني : {{ customer_obj.email |default:"N/A"}}</span></p>
|
||||
<p><span class="label">الهاتف : {{ customer_obj.phone_number|default:"N/A" }}</span></p>
|
||||
<p><span class="label">العنوان : {{ customer_obj.address|default:"N/A" }}</span></p>
|
||||
</div>
|
||||
<div class="section text-left">
|
||||
<h2>التفاصيل:</h2>
|
||||
<p><span class="label">رقم عرض السعر : </span> {{estimate.estimate_number }}</p>
|
||||
<p><span class="label">تاريخ الإصدار : </span> {{ estimate.date_approved|date:"Y/m/d" }}</p>
|
||||
<p><span class="label">طريقة الدفع : </span> {{ estimate.get_terms_display }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>تفاصيل السيارة</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>الصانع</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الموديل</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>السلسلة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الفئة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>السنة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>رقم الهيكل</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">{{ data.car.id_car_make.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_model.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_serie.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_trim.name }}</td>
|
||||
<td class="text-center">{{ data.car.year }}</td>
|
||||
<td class="text-center">{{ data.car.vin }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>التفاصيل المالية</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>الكمية</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>سعر الوحدة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الخصم</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الضريبة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الإجمالي</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">{{ data.car.marked_price|currency_format}}</td>
|
||||
<td class="text-center">{{ data.discount_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.vat_amount|currency_format }}</td>
|
||||
<td class="text-center">{{ data.final_price|currency_format }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if data.additional_services %}
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>الخدمات الإضافية</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">النوع</th>
|
||||
<th class="text-center">القيمة</th>
|
||||
<th class="text-center">ضريبة الخدمة</th>
|
||||
<th class="text-center">الإجمالي</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in data.additional_services.services %}
|
||||
<tr>
|
||||
<td class="text-center">{{ service.0.name }}</td>
|
||||
<td class="text-center">{{ service.0.price|currency_format }}</td>
|
||||
<td class="text-center">{{ service.1|currency_format}}</td>
|
||||
<td class="text-center">{{ service.0.price_|currency_format}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center">{{ data.total_services_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_vat|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_amount_|currency_format}}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="document-details" style="margin-top: 30px; ">
|
||||
<div class="section text-left">
|
||||
<p><span class="label">إجمالي ضريبة القيمة المضافة : </span> {{ data.total_vat|currency_format }}</p>
|
||||
<p><span class="label">الإجمالي الكلي : </span> {{ data.grand_total|currency_format}}</p>
|
||||
<p><span class="label">كتابةً : </span> {{ data.grand_total|num_to_words }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="border-bottom:1px solid #ccc; ">
|
||||
|
||||
<div class="document-footer">
|
||||
<div class="footer-logo">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p>
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-powered">
|
||||
<p>
|
||||
<span>Powered by </span>
|
||||
<a href="https://tenhal.sa"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
357
templates/sales/estimates/estimate_preview_en.html
Normal file
357
templates/sales/estimates/estimate_preview_en.html
Normal file
@ -0,0 +1,357 @@
|
||||
{% load static custom_filters num2words_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Quotation</title>
|
||||
|
||||
<style>
|
||||
/* General Body and Font Styles */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Page Layout and Margins for PDF */
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 20mm;
|
||||
@top-left {
|
||||
content: "صفحة " counter(page) " من " counter(pages);
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.document-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.document-header .logo {
|
||||
max-width: 150px;
|
||||
height: auto;
|
||||
}
|
||||
.document-header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #0056b3;
|
||||
}
|
||||
.document-header address {
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Document Details Section */
|
||||
.document-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.document-details .section {
|
||||
width: 48%;
|
||||
}
|
||||
.document-details h2 {
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
}
|
||||
.document-details p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.document-details .label {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.table {
|
||||
width: 80%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px auto; /* Centering the table */
|
||||
}
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table tfoot td {
|
||||
border-top: 2px solid #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-right {
|
||||
text-align: left;
|
||||
}
|
||||
.text-left {
|
||||
text-align: right;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;2px solid #333;
|
||||
}
|
||||
|
||||
/* Responsive and layout helpers */
|
||||
.table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.table-header span {
|
||||
font-size: 9px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
|
||||
padding-top: 15px;
|
||||
margin: 0 20mm;
|
||||
|
||||
}
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.footer-logo {
|
||||
text-align: center;
|
||||
}
|
||||
.footer-logo img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
.footer-logo p {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer-powered p {
|
||||
font-size: 11px;
|
||||
}
|
||||
.footer-powered span {
|
||||
font-weight: lighter;
|
||||
}
|
||||
.footer-powered a {
|
||||
color: #112e40;
|
||||
text-decoration: none;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="document-header">
|
||||
<div>
|
||||
<h1>{{ dealer_info.name }}</h1>
|
||||
<address>
|
||||
Address : {{ dealer_info.address }}<br>
|
||||
Email : {{ dealer_info.user.email }}<br>
|
||||
Phone : {{ dealer_info.phone_number }}<br>
|
||||
Commercial Registration No. : {{dealer_info.crn }}<br>
|
||||
VAT No. : {{ dealer_info.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="dealer-logo ">
|
||||
{% if dealer_info.logo %}
|
||||
<img class="rounded-soft"
|
||||
style="max-width:100px;
|
||||
max-height:100px"
|
||||
src="{{dealer_info.logo.url|default:'' }}"
|
||||
alt="Dealer Logo" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>Quotation</h1>
|
||||
<h2 style="font-size: 18px;">{{ estimate.estimate_number }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>To: {{ customer_obj.full_name }}</h2>
|
||||
<p><span class="label">Customer : {{ customer_obj.full_name }}</span></p>
|
||||
<p><span class="label">Email : {{ customer_obj.email |default:"N/A"}}</span></p>
|
||||
<p><span class="label">Phone : {{ customer_obj.phone_number|default:"N/A" }}</span></p>
|
||||
<p><span class="label">Address : {{ customer_obj.address|default:"N/A" }}</span></p>
|
||||
</div>
|
||||
<div class="section text-right">
|
||||
<h2>Details:</h2>
|
||||
<p><span class="label">Quotation Number : </span> {{ estimate.estimate_number }}</p>
|
||||
<p><span class="label">Issue Date : </span> {{ estimate.date_approved|date:"Y/m/d" }}</p>
|
||||
<p><span class="label">Payment Method : </span> {{ estimate.get_terms_display }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>Car Details</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>Make</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Model</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Series</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Trim</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Year</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>VIN</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">{{ data.car.id_car_make.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_model.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_serie.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_trim.name }}</td>
|
||||
<td class="text-center">{{ data.car.year }}</td>
|
||||
<td class="text-center">{{ data.car.vin }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>Financial Details</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>Quantity</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Unit Price</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Discount</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>VAT</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Total</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">{{ data.car.marked_price|currency_format}}</td>
|
||||
<td class="text-center">{{ data.discount_amount|currency_format }}</td>
|
||||
<td class="text-center">{{ data.vat_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.final_price|currency_format}}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if data.additional_services %}
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>Additional Services</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">Type</th>
|
||||
<th class="text-center">Value</th>
|
||||
<th class="text-center">Service VAT</th>
|
||||
<th class="text-center">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in data.additional_services.services %}
|
||||
<tr>
|
||||
<td class="text-center">{{ service.0.name }}</td>
|
||||
<td class="text-center">{{ service.0.price|currency_format }}</td>
|
||||
<td class="text-center">{{ service.1|currency_format}}</td>
|
||||
<td class="text-center">{{ service.0.price_|currency_format}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center">{{ data.total_services_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_vat|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_amount_|currency_format }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="document-details" style="margin-top: 30px; ">
|
||||
<div class="section text-right">
|
||||
<p><span class="label">Total VAT : </span> {{ data.total_vat|currency_format}}</p>
|
||||
<p><span class="label">Grand Total : </span> {{ data.grand_total|currency_format }}</p>
|
||||
<p><span class="label">In words : </span> {{ data.grand_total|num_to_words }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="border-bottom:1px solid #ccc; ">
|
||||
|
||||
<div class="document-footer">
|
||||
<div class="footer-logo">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p>
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-powered">
|
||||
<p>
|
||||
<span>Powered by </span>
|
||||
<a href="https://tenhal.sa"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -144,6 +144,16 @@
|
||||
{% endif %}
|
||||
<a href="{% url 'invoice_preview' request.dealer.slug invoice.pk %}"
|
||||
class="btn btn-phoenix-primary"><span class="d-none d-sm-inline-block"><i class="fa-regular fa-eye"></i> {% trans 'Preview' %}</span></a>
|
||||
<a href="{% url 'invoice_preview' request.dealer.slug invoice.pk %}?lang=en"
|
||||
class="btn btn-phoenix-secondary"
|
||||
target="_blank">
|
||||
<span class="d-none d-sm-inline-block"><i class="fas fa-print me-2"></i>{% trans 'Print EN' %}</span>
|
||||
</a>
|
||||
<a href="{% url 'invoice_preview' request.dealer.slug invoice.pk %}?lang=ar"
|
||||
class="btn btn-phoenix-secondary"
|
||||
target="_blank">
|
||||
<span class="d-none d-sm-inline-block"><i class="fas fa-print me-2"></i>{% trans 'Print AR' %}</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
{{ invoice.amount_owned }}
|
||||
|
||||
@ -14,9 +14,9 @@
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
{% comment %} <div class="col-auto">
|
||||
<div class="d-flex">{% include 'partials/search_box.html' %}</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
</div>
|
||||
<div class="table-responsive px-1 scrollbar">
|
||||
<table class="table align-items-center table-flush">
|
||||
|
||||
@ -1,367 +1,333 @@
|
||||
{% load i18n static custom_filters num2words_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% trans "Invoice" %}</title>
|
||||
<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">
|
||||
<link href="{% static 'css/custom.css' %}" type="text/css" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap"
|
||||
rel="stylesheet">
|
||||
<style>
|
||||
body {
|
||||
font-family: 'Roboto', sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
<html lang="ar">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>{% trans "Invoice Preview" %}</title>
|
||||
|
||||
.invoice-container {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
padding: 10mm;
|
||||
margin: 10mm auto;
|
||||
background: white;
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
<style>
|
||||
/* General Body and Font Styles */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
background-color: #f7f7f7;
|
||||
margin: 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.invoice-content {
|
||||
flex-grow: 1;
|
||||
}
|
||||
/* Container for the document content */
|
||||
.document-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
background-color: #fff;
|
||||
padding: 40px;
|
||||
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.invoice-header {
|
||||
text-align: center;
|
||||
border-bottom: 2px solid #dee2e6;
|
||||
padding-bottom: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
/* Header Styles */
|
||||
.document-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.document-header .logo {
|
||||
max-width: 150px;
|
||||
height: auto;
|
||||
}
|
||||
.document-header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #0056b3;
|
||||
}
|
||||
.document-header h2 {
|
||||
font-size: 18px;
|
||||
margin: 0;
|
||||
}
|
||||
.document-header address {
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
.qr-code {
|
||||
text-align: center;
|
||||
margin-top: 10px;
|
||||
}
|
||||
/* Document Details Section */
|
||||
.document-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.document-details .section {
|
||||
width: 48%;
|
||||
}
|
||||
.document-details h2 {
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
}
|
||||
.document-details p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.document-details .label {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.qr-code img {
|
||||
width: 3cm;
|
||||
height: 3cm;
|
||||
border-radius: 0.3333333333rem;
|
||||
}
|
||||
/* Table Styles */
|
||||
.table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table tfoot td {
|
||||
border-top: 2px solid #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-right {
|
||||
text-align: left;
|
||||
}
|
||||
.text-left {
|
||||
text-align: right;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.invoice-details, .invoice-table {
|
||||
font-size: 14px;
|
||||
}
|
||||
/* Responsive and layout helpers */
|
||||
.table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.table-header span {
|
||||
font-size: 14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.invoice-table th {
|
||||
background-color: #f8f9fa;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.invoice-total {
|
||||
text-align: right;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
margin-top: auto;
|
||||
padding-top: 10mm;
|
||||
font-size: 13px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-top: 2px solid #dee2e6; /* Add a top border to separate from content */
|
||||
}
|
||||
|
||||
.logo-img img {
|
||||
width: 10mm;
|
||||
height: 10mm;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="row p-2">
|
||||
<div class="col-2">
|
||||
<button class="btn btn-sm btn-phoenix-danger w-100"
|
||||
onclick="window.history.back()">الرجوع / Back</button>
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
border-top: 1px solid #ddd;
|
||||
padding-top: 15px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.footer-logo img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
.footer-logo p {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
margin: 5px 0 0;
|
||||
}
|
||||
.footer-powered p {
|
||||
font-size: 11px;
|
||||
margin: 0;
|
||||
}
|
||||
.footer-powered span {
|
||||
font-weight: lighter;
|
||||
}
|
||||
.footer-powered a {
|
||||
color: #112e40;
|
||||
text-decoration: none;
|
||||
font-size: 9px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document-container">
|
||||
<div class="document-header">
|
||||
<div>
|
||||
<h1>{{ dealer_info.name }}</h1>
|
||||
<address>
|
||||
العنوان : {{ dealer_info.address }}<br>
|
||||
البريد الإلكتروني : {{ dealer_info.user.email }}<br>
|
||||
الهاتف : {{ dealer_info.phone_number }}<br>
|
||||
رقم السجل التجاري : {{dealer_info.crn }} | الرقم الضريبي : {{ dealer_info.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<button class="btn btn-sm btn-phoenix-primary w-100" id="download-pdf">تحميل / Download</button>
|
||||
<div>
|
||||
<h1>عرض سعر</h1>
|
||||
<h2>{{ estimate.estimate_number }}</h2>
|
||||
</div>
|
||||
<div class="col-8"></div>
|
||||
</div>
|
||||
<div class="invoice-container" id="invoice-content">
|
||||
<div class="invoice-content">
|
||||
<div class="invoice-header">
|
||||
<h5 class="fs-5">
|
||||
<span>Invoice</span> / <span>فاتورة</span>
|
||||
</h5>
|
||||
</div>
|
||||
<div class="invoice-details p-1">
|
||||
<div class="d-flex justify-content-around align-items-end">
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="qr-code">
|
||||
<img src="{% static 'qr_code/Marwan_qr.png' %}" alt="QR Code">
|
||||
</div>
|
||||
</div>
|
||||
<div class="dealer-logo ">
|
||||
{% if request.dealer.logo %}
|
||||
<img class="rounded-soft"
|
||||
style="max-width:150px;
|
||||
max-height:150px"
|
||||
src="{{ request.dealer.logo.url|default:'' }}"
|
||||
alt="Dealer Logo" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<table class="table table-sm table-responsive border-gray-50">
|
||||
<tr>
|
||||
<td class="ps-1">
|
||||
<strong>Dealership Name</strong>
|
||||
</td>
|
||||
<td class="text-center">{{ request.dealer.name }}</td>
|
||||
<td class="text-end">
|
||||
<strong>اسم الوكالة</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="ps-1">
|
||||
<strong>Dealership Address</strong>
|
||||
</td>
|
||||
<td class="text-center">{{ request.dealer.address }}</td>
|
||||
<td class="text-end">
|
||||
<strong>عنوان الوكالة</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="ps-1">
|
||||
<strong>Phone</strong>
|
||||
</td>
|
||||
<td class="text-center">{{ request.dealer.phone_number }}</td>
|
||||
<td class="text-end">
|
||||
<strong>جوال</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<strong>VAT Number</strong>
|
||||
</td>
|
||||
<td class="text-center">{{ request.dealer.vrn }}</td>
|
||||
<td class="text-end">
|
||||
<strong>الرقم الضريبي</strong>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<table class="table table-sm table-bordered border-gray-50 ">
|
||||
<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_approved| date:"Y/m/d" }}</td>
|
||||
<td class="text-end p-1">
|
||||
<strong>التاريخ</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="ps-1">
|
||||
<strong>Customer Name</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>Email</strong>
|
||||
</td>
|
||||
<td class="text-center">{{ invoice.customer.email |default:"N/A" }}</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 class="d-flex justify-content-between">
|
||||
<span class="fs-9 fw-thin">Car Details</span>
|
||||
<span class="fs-9 fw-thin">تفاصيل السيارة</span>
|
||||
</div>
|
||||
<div class="invoice-table p-1">
|
||||
<table class="table table-sm table-bordered m-1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Make</span> / <span class="fs-10">الصانع</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Model</span> / <span class="fs-10">الموديل</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Series</span> / <span class="fs-10">السلسلة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Trim</span> / <span class="fs-10">الفئة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Year</span> / <span class="fs-10">السنة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">VIN</span> / <span class="fs-10">رقم الهيكل</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Quantity</span> / <span class="fs-10">الكمية</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Unit Price</span> / <span class="fs-10">سعر الوحدة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Discount</span> / <span class="fs-10">الخصم</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">VAT</span> / <span class="fs-10">الضريبة</span>
|
||||
</th>
|
||||
<th class="text-wrap text-center align-content-center">
|
||||
<span class="fs-10">Total</span> / <span class="fs-10">الإجمالي</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_make.name }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_model.name }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_serie.name }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.id_car_trim.name }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.car.year }}</td>
|
||||
<td class="ps-1 fs-10 align-content-center">{{ data.car.vin }}</td>
|
||||
<td class="text-center fs-10 align-content-center">1</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.car.marked_price |floatformat:2 }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.discount_amount |floatformat:2 }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.vat_amount|floatformat:2 }}</td>
|
||||
<td class="text-center fs-10 align-content-center">{{ data.final_price|floatformat:2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between">
|
||||
<span class="fs-9 fw-thin">Additional Services</span>
|
||||
<span class="fs-9 fw-thin">الخدمات الإضافية</span>
|
||||
</div>
|
||||
{% if data.additional_services %}
|
||||
<div class="invoice-table p-1">
|
||||
<table class="table table-sm table-bordered m-1">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center fs-10 align-content-center">Type / النوع</th>
|
||||
<th class="text-center fs-10 align-content-center">Price / السعر</th>
|
||||
<th class="text-center fs-10 align-content-center">Service VAT / ضريبة الخدمة</th>
|
||||
<th class="text-center fs-10 align-content-center">
|
||||
<span class="fs-10">Total</span> / <span class="fs-10">الإجمالي</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in data.additional_services.services %}
|
||||
<tr>
|
||||
<td class="ps-1 text-start fs-10 align-content-center">{{ service.0.name }}</td>
|
||||
<td class="ps-1 text-center fs-10 align-content-center">{{ service.0.price|floatformat }}</td>
|
||||
<td class="ps-1 text-center fs-10 align-content-center">{{ service.1|floatformat }}</td>
|
||||
<td class="ps-1 text-center fs-10 align-content-center">{{ service.0.price_|floatformat }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="invoice-total 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">Total VAT</strong>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="fs-9">{{ data.total_vat|floatformat }} <span class="icon-saudi_riyal"></span></span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<strong class="fs-9">إجمالي ضريبة القيمة المضافة</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-start ps-1">
|
||||
<strong class="fs-9">Grand Total</strong>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<span class="fs-9">{{ data.grand_total|floatformat }} <span class="icon-saudi_riyal"></span></span>
|
||||
</td>
|
||||
<td class="text-end">
|
||||
<strong class="fs-9">الإجمالي الكلي</strong>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="text-end" colspan="3">
|
||||
<span class="fs-9 fw-bold">كتابةً: </span><span class="fs-9">{{ data.grand_total|num_to_words }} <span class="icon-saudi_riyal"></span></span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>{{ estimate.customer.customer_name }}: إلى</h2>
|
||||
<p><span class="label">العميل : {{ customer_obj.full_name }}</span></p>
|
||||
<p><span class="label">البريد الإلكتروني : {{ customer_obj.email |default:"N/A"}}</span></p>
|
||||
<p><span class="label">الهاتف : {{ customer_obj.phone_number|default:"N/A" }}</span></p>
|
||||
<p><span class="label">العنوان : {{ customer_obj.address|default:"N/A" }}</span></p>
|
||||
</div>
|
||||
<div class="footer-note">
|
||||
<div class="logo-img text-center">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p class="fs-9 fw-bold">
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
<div class="section text-left">
|
||||
<h2>التفاصيل:</h2>
|
||||
<p><span class="label">رقم عرض السعر : </span> {{ estimate.estimate_number }}</p>
|
||||
<p><span class="label">تاريخ الإصدار : </span> {{ estimate.date_approved|date:"Y/m/d" }}</p>
|
||||
<p><span class="label">طريقة الدفع : </span> {{ estimate.get_terms_display }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>تفاصيل السيارة</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>الصانع</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الموديل</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>السلسلة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الفئة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>السنة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>رقم الهيكل</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">{{ data.car.id_car_make.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_model.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_serie.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_trim.name }}</td>
|
||||
<td class="text-center">{{ data.car.year }}</td>
|
||||
<td class="text-center">{{ data.car.vin }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>التفاصيل المالية</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>الكمية</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>سعر الوحدة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الخصم</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الضريبة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الإجمالي</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">{{ data.car.marked_price|floatformat:2 }}</td>
|
||||
<td class="text-center">{{ data.discount_amount|floatformat:2 }}</td>
|
||||
<td class="text-center">{{ data.vat_amount|floatformat:2 }}</td>
|
||||
<td class="text-center">{{ data.final_price|floatformat:2 }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if data.additional_services %}
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>الخدمات الإضافية</span>
|
||||
</div>
|
||||
<p class="fs-11">
|
||||
<span class="fw-thin">Powered by </span>
|
||||
<a class="text-decoration-none fs-9"
|
||||
href="https://tenhal.sa"
|
||||
style="color: #112e40"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">النوع</th>
|
||||
<th class="text-center">القيمة</th>
|
||||
<th class="text-center">ضريبة الخدمة</th>
|
||||
<th class="text-center">الإجمالي</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in data.additional_services.services %}
|
||||
<tr>
|
||||
<td class="text-center">{{ service.0.name }}</td>
|
||||
<td class="text-center">{{ service.0.price|floatformat:2 }}</td>
|
||||
<td class="text-center">{{ service.1|floatformat:2 }}</td>
|
||||
<td class="text-center">{{ service.0.price_|floatformat:2 }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center">{{ data.total_services_amount|floatformat:'2g'}}</td>
|
||||
<td class="text-center">{{ data.total_services_vat|floatformat:'2g'}}</td>
|
||||
<td class="text-center">{{ data.total_services_amount_|floatformat:'2g' }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="document-details" style="margin-top: 30px; ">
|
||||
<div class="section text-left">
|
||||
<p><span class="label">إجمالي ضريبة القيمة المضافة : </span> {{ data.total_vat|floatformat:'2g' }}</p>
|
||||
<p><span class="label">الإجمالي الكلي : </span> {{ data.grand_total|floatformat:'2g' }}</p>
|
||||
<p><span class="label">كتابةً : </span> {{ data.grand_total|num_to_words }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="border-bottom:1px solid #ccc; ">
|
||||
|
||||
<div class="document-footer">
|
||||
<div class="footer-logo">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p>
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-powered">
|
||||
<p>
|
||||
<span>Powered by </span>
|
||||
<a href="https://tenhal.sa"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<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 () {
|
||||
html2pdf().from(document.getElementById('invoice-content')).set({
|
||||
margin: 0,
|
||||
filename: "{{ invoice.invoice_number }}_{{ invoice.customer.customer_name }}_{{invoice.date_approved|date:'Y-m-d' }}.pdf",
|
||||
image: { type: 'jpeg', quality: 0.98 },
|
||||
html2canvas: { scale: 3 },
|
||||
jsPDF: { unit: 'mm', format: 'a3', orientation: 'portrait' }
|
||||
}).save();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
359
templates/sales/invoices/invoice_preview_ar.html
Normal file
359
templates/sales/invoices/invoice_preview_ar.html
Normal file
@ -0,0 +1,359 @@
|
||||
{% load static custom_filters num2words_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
|
||||
<title>فاتورة</title>
|
||||
|
||||
<style>
|
||||
/* General Body and Font Styles */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Page Layout and Margins for PDF */
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 20mm;
|
||||
@top-left {
|
||||
content: "صفحة " counter(page) " من " counter(pages);
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.document-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.document-header .logo {
|
||||
max-width: 150px;
|
||||
height: auto;
|
||||
}
|
||||
.document-header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #0056b3;
|
||||
}
|
||||
.document-header address {
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Document Details Section */
|
||||
.document-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.document-details .section {
|
||||
width: 48%;
|
||||
}
|
||||
.document-details h2 {
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
}
|
||||
.document-details p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.document-details .label {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.table {
|
||||
width: 80%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px auto; /* Centering the table */
|
||||
}
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table tfoot td {
|
||||
border-top: 2px solid #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-right {
|
||||
text-align: left;
|
||||
}
|
||||
.text-left {
|
||||
text-align: right;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;2px solid #333;
|
||||
}
|
||||
|
||||
/* Responsive and layout helpers */
|
||||
.table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.table-header span {
|
||||
font-size: 9px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
|
||||
padding-top: 15px;
|
||||
margin: 0 20mm;
|
||||
|
||||
}
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.footer-logo {
|
||||
text-align: center;
|
||||
}
|
||||
.footer-logo img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
.footer-logo p {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer-powered p {
|
||||
font-size: 11px;
|
||||
}
|
||||
.footer-powered span {
|
||||
font-weight: lighter;
|
||||
}
|
||||
.footer-powered a {
|
||||
color: #112e40;
|
||||
text-decoration: none;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="document-header">
|
||||
<div>
|
||||
<h1>{{ dealer_info.name }}</h1>
|
||||
<address>
|
||||
العنوان : {{ dealer_info.address }}<br>
|
||||
البريد الإلكتروني : {{ dealer_info.user.email }}<br>
|
||||
الهاتف : {{ dealer_info.phone_number }}<br>
|
||||
رقم السجل التجاري : {{dealer_info.crn }}<br>
|
||||
الرقم الضريبي : {{ dealer_info.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="dealer-logo ">
|
||||
{% if dealer_info.logo %}
|
||||
<img class="rounded-soft"
|
||||
style="max-width:100px;
|
||||
max-height:100px"
|
||||
src="{{dealer_info.logo.url|default:'' }}"
|
||||
alt="Dealer Logo" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>فاتورة</h1>
|
||||
<h2 style="font-size: 18px;">{{ invoice.invoice_number }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>{{ customer_obj.full_name }}: إلى</h2>
|
||||
<p><span class="label">العميل : {{ customer_obj.full_name }}</span></p>
|
||||
<p><span class="label">البريد الإلكتروني : {{ customer_obj.email |default:"N/A"}}</span></p>
|
||||
<p><span class="label">الهاتف : {{ customer_obj.phone_number|default:"N/A" }}</span></p>
|
||||
<p><span class="label">العنوان : {{ customer_obj.address|default:"N/A" }}</span></p>
|
||||
</div>
|
||||
<div class="section text-left">
|
||||
<h2>التفاصيل:</h2>
|
||||
<p><span class="label">رقم عرض السعر : </span> {{ invoice.invoice_number }}</p>
|
||||
<p><span class="label">تاريخ الإصدار : </span> {{ invoice.date_approved|date:"Y/m/d" }}</p>
|
||||
<p><span class="label">طريقة الدفع : </span> {{ invoice.get_terms_display }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>تفاصيل السيارة</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>الصانع</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الموديل</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>السلسلة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الفئة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>السنة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>رقم الهيكل</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">{{ data.car.id_car_make.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_model.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_serie.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_trim.name }}</td>
|
||||
<td class="text-center">{{ data.car.year }}</td>
|
||||
<td class="text-center">{{ data.car.vin }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>التفاصيل المالية</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>الكمية</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>سعر الوحدة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الخصم</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الضريبة</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>الإجمالي</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">{{ data.car.marked_price|currency_format }}</td>
|
||||
<td class="text-center">{{ data.discount_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.vat_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.final_price|currency_format }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if data.additional_services %}
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>الخدمات الإضافية</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">النوع</th>
|
||||
<th class="text-center">القيمة</th>
|
||||
<th class="text-center">ضريبة الخدمة</th>
|
||||
<th class="text-center">الإجمالي</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in data.additional_services.services %}
|
||||
<tr>
|
||||
<td class="text-center">{{ service.0.name }}</td>
|
||||
<td class="text-center">{{ service.0.price|currency_format}}</td>
|
||||
<td class="text-center">{{ service.1|currency_format }}</td>
|
||||
<td class="text-center">{{ service.0.price_|currency_format}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center">{{ data.total_services_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_vat|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_amount_|currency_format }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="document-details" style="margin-top: 30px; ">
|
||||
<div class="section text-left">
|
||||
<p><span class="label">إجمالي ضريبة القيمة المضافة : </span> {{ data.total_vat|currency_format }}</p>
|
||||
<p><span class="label">الإجمالي الكلي : </span> {{ data.grand_total|currency_format}}</p>
|
||||
<p><span class="label">كتابةً : </span> {{ data.grand_total|num_to_words }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="border-bottom:1px solid #ccc; ">
|
||||
|
||||
<div class="document-footer">
|
||||
<div class="footer-logo">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p>
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-powered">
|
||||
<p>
|
||||
<span>Powered by </span>
|
||||
<a href="https://tenhal.sa"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
357
templates/sales/invoices/invoice_preview_en.html
Normal file
357
templates/sales/invoices/invoice_preview_en.html
Normal file
@ -0,0 +1,357 @@
|
||||
{% load static custom_filters num2words_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Invoice</title>
|
||||
|
||||
<style>
|
||||
/* General Body and Font Styles */
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Page Layout and Margins for PDF */
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 20mm;
|
||||
@top-left {
|
||||
content: "صفحة " counter(page) " من " counter(pages);
|
||||
font-size: 10px;
|
||||
color: #555;
|
||||
}
|
||||
}
|
||||
|
||||
/* Header Styles */
|
||||
.document-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
border-bottom: 2px solid #333;
|
||||
padding-bottom: 15px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.document-header .logo {
|
||||
max-width: 150px;
|
||||
height: auto;
|
||||
}
|
||||
.document-header h1 {
|
||||
font-size: 24px;
|
||||
margin: 0;
|
||||
color: #0056b3;
|
||||
}
|
||||
.document-header address {
|
||||
text-align: right;
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Document Details Section */
|
||||
.document-details {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.document-details .section {
|
||||
width: 48%;
|
||||
}
|
||||
.document-details h2 {
|
||||
font-size: 14px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
padding-bottom: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: #555;
|
||||
}
|
||||
.document-details p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
.document-details .label {
|
||||
font-weight: bold;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
/* Table Styles */
|
||||
.table {
|
||||
width: 80%;
|
||||
border-collapse: collapse;
|
||||
margin: 20px auto; /* Centering the table */
|
||||
}
|
||||
.table th, .table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: right;
|
||||
}
|
||||
.table th {
|
||||
background-color: #f2f2f2;
|
||||
font-weight: bold;
|
||||
}
|
||||
.table tfoot td {
|
||||
border-top: 2px solid #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.text-right {
|
||||
text-align: left;
|
||||
}
|
||||
.text-left {
|
||||
text-align: right;
|
||||
}
|
||||
.text-center {
|
||||
text-align: center;2px solid #333;
|
||||
}
|
||||
|
||||
/* Responsive and layout helpers */
|
||||
.table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.table-header span {
|
||||
font-size: 9px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
/* Footer Styles */
|
||||
.document-footer {
|
||||
position: relative;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
color: #888;
|
||||
|
||||
padding-top: 15px;
|
||||
margin: 0 20mm;
|
||||
|
||||
}
|
||||
.footer-flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
.footer-logo {
|
||||
text-align: center;
|
||||
}
|
||||
.footer-logo img {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
.footer-logo p {
|
||||
font-size: 9px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.footer-powered p {
|
||||
font-size: 11px;
|
||||
}
|
||||
.footer-powered span {
|
||||
font-weight: lighter;
|
||||
}
|
||||
.footer-powered a {
|
||||
color: #112e40;
|
||||
text-decoration: none;
|
||||
font-size: 9px;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="document-header">
|
||||
<div>
|
||||
<h1>{{ dealer_info.name }}</h1>
|
||||
<address>
|
||||
Address : {{ dealer_info.address }}<br>
|
||||
Email : {{ dealer_info.user.email }}<br>
|
||||
Phone : {{ dealer_info.phone_number }}<br>
|
||||
Commercial Registration No. : {{dealer_info.crn }}<br>
|
||||
VAT No. : {{ dealer_info.vrn }}
|
||||
</address>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="dealer-logo ">
|
||||
{% if dealer_info.logo %}
|
||||
<img class="rounded-soft"
|
||||
style="max-width:100px;
|
||||
max-height:100px"
|
||||
src="{{dealer_info.logo.url|default:'' }}"
|
||||
alt="Dealer Logo" />
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h1>Invoice</h1>
|
||||
<h2 style="font-size: 18px;">{{ invoice.invoice_number }}</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="document-details">
|
||||
<div class="section">
|
||||
<h2>To: {{ customer_obj.full_name }}</h2>
|
||||
<p><span class="label">Customer : {{ customer_obj.full_name }}</span></p>
|
||||
<p><span class="label">Email : {{ customer_obj.email |default:"N/A"}}</span></p>
|
||||
<p><span class="label">Phone : {{ customer_obj.phone_number|default:"N/A" }}</span></p>
|
||||
<p><span class="label">Address : {{ customer_obj.address|default:"N/A" }}</span></p>
|
||||
</div>
|
||||
<div class="section text-right">
|
||||
<h2>Details:</h2>
|
||||
<p><span class="label">Quotation Number : </span> {{ invoice.invoice_number }}</p>
|
||||
<p><span class="label">Issue Date : </span> {{ invoice.date_approved|date:"Y/m/d" }}</p>
|
||||
<p><span class="label">Payment Method : </span> {{ invoiceget_terms_display }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>Car Details</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>Make</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Model</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Series</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Trim</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Year</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>VIN</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">{{ data.car.id_car_make.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_model.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_serie.name }}</td>
|
||||
<td class="text-center">{{ data.car.id_car_trim.name }}</td>
|
||||
<td class="text-center">{{ data.car.year }}</td>
|
||||
<td class="text-center">{{ data.car.vin }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>Financial Details</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">
|
||||
<span>Quantity</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Unit Price</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Discount</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>VAT</span>
|
||||
</th>
|
||||
<th class="text-center">
|
||||
<span>Total</span>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center">{{ data.car.marked_price|currency_format}}</td>
|
||||
<td class="text-center">{{ data.discount_amount|currency_format }}</td>
|
||||
<td class="text-center">{{ data.vat_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.final_price|currency_format }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% if data.additional_services %}
|
||||
<div class="table-container">
|
||||
<div class="table-header">
|
||||
<span>Additional Services</span>
|
||||
</div>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="text-center">Type</th>
|
||||
<th class="text-center">Value</th>
|
||||
<th class="text-center">Service VAT</th>
|
||||
<th class="text-center">Total</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for service in data.additional_services.services %}
|
||||
<tr>
|
||||
<td class="text-center">{{ service.0.name }}</td>
|
||||
<td class="text-center">{{ service.0.price|currency_format }}</td>
|
||||
<td class="text-center">{{ service.1|currency_format}}</td>
|
||||
<td class="text-center">{{ service.0.price_|currency_format}}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td class="text-center"></td>
|
||||
<td class="text-center">{{ data.total_services_amount|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_vat|currency_format}}</td>
|
||||
<td class="text-center">{{ data.total_services_amount_|currency_format }}</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="document-details" style="margin-top: 30px; ">
|
||||
<div class="section text-right">
|
||||
<p><span class="label">Total VAT : </span> {{ data.total_vat|currency_format}}</p>
|
||||
<p><span class="label">Grand Total : </span> {{ data.grand_total|currency_format}}</p>
|
||||
<p><span class="label">In words : </span> {{ data.grand_total|num_to_words }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr style="border-bottom:1px solid #ccc; ">
|
||||
|
||||
<div class="document-footer">
|
||||
<div class="footer-logo">
|
||||
<img src="{% static 'images/logos/logo-d-pdf.png' %}" alt="Logo" />
|
||||
<p>
|
||||
<span>Haikal</span> | <span>هيكل</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="footer-powered">
|
||||
<p>
|
||||
<span>Powered by </span>
|
||||
<a href="https://tenhal.sa"><span>TENHAL</span> | <span>تنحل</span></a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -15,9 +15,9 @@
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
{% comment %} <div class="col-auto">
|
||||
<div class="d-flex">{% include 'partials/search_box.html' %}</div>
|
||||
</div>
|
||||
</div> {% endcomment %}
|
||||
</div>
|
||||
<div class="table-responsive scrollbar mx-n1 px-1">
|
||||
<table class="table align-items-center table-flush">
|
||||
|
||||
2
templates/vendors/vendors_list.html
vendored
2
templates/vendors/vendors_list.html
vendored
@ -101,7 +101,7 @@
|
||||
<a class="fs-8 fw-bold"
|
||||
href="{% url 'vendor_detail' request.dealer.slug vendor.slug %}">{{ vendor.arabic_name }}</a>
|
||||
<div class="d-flex align-items-center">
|
||||
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.name }}</p>
|
||||
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.name|title }}</p>
|
||||
<!--<span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.vendor_model.uuid }}</span>-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user