This commit is contained in:
gitea 2024-12-22 15:14:28 +00:00
parent 791e74b3d3
commit cb89dd450b
14 changed files with 910 additions and 463 deletions

View File

@ -26,7 +26,7 @@ SECRET_KEY = 'django-insecure-gc9bh4*3=b6hihdnaom0edjsbxh$5t)aap@e8p&340r7)*)qb8
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = True
ALLOWED_HOSTS = ['10.10.1.109', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4'] ALLOWED_HOSTS = ['10.10.1.120', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
# Application definition # Application definition
@ -111,9 +111,9 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django_prometheus.db.backends.postgresql", "ENGINE": "django_prometheus.db.backends.postgresql",
"NAME": "haikal_app", "NAME": "haikal",
"USER": "f95166", "USER": "haikal",
"PASSWORD": "Kfsh&rc9788", "PASSWORD": "haikal",
"HOST": "localhost", "HOST": "localhost",
"PORT": 5432, "PORT": 5432,
} }

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-22 08:39
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0040_additionalservices_display_name'),
]
operations = [
migrations.AddField(
model_name='salequotation',
name='quotation_number',
field=models.CharField(default=1, max_length=10, unique=True),
preserve_default=False,
),
]

View File

@ -0,0 +1,26 @@
# Generated by Django 4.2.17 on 2024-12-22 10:36
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('django_ledger', '0017_alter_accountmodel_unique_together_and_more'),
('inventory', '0041_salequotation_quotation_number'),
]
operations = [
migrations.AddField(
model_name='salequotation',
name='entity',
field=models.ForeignKey(default="cb12725d-3b89-4742-8668-05d825b0b1f0", on_delete=django.db.models.deletion.CASCADE, to='django_ledger.entitymodel'),
preserve_default=False,
),
migrations.AddField(
model_name='salequotation',
name='is_approved',
field=models.BooleanField(default=False),
),
]

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2024-12-22 11:48
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0042_salequotation_entity_salequotation_is_approved'),
]
operations = [
migrations.RemoveField(
model_name='salequotation',
name='status',
),
]

View File

@ -36,7 +36,7 @@ class LocalizedNameMixin:
class AddDealerInstanceMixin: class AddDealerInstanceMixin:
def form_valid(self, form): def form_valid(self, form):
if form.is_valid(): if form.is_valid():
form.instance.dealer = self.request.user.dealer.get_parent_or_self form.instance.dealer = self.request.user.dealer.get_root_dealer
form.save() form.save()
return super().form_valid(form) return super().form_valid(form)
else: else:

View File

@ -1,3 +1,4 @@
import itertools
from uuid import uuid4 from uuid import uuid4
from django.conf import settings from django.conf import settings
from django.db import models, transaction from django.db import models, transaction
@ -24,9 +25,9 @@ from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
from django.utils.timezone import now from django.utils.timezone import now
from .utilities.financials import get_financial_value, get_total, get_total_financials from .utilities.financials import get_financial_value, get_total, get_total_financials
from django.db.models import FloatField
from .mixins import LocalizedNameMixin from .mixins import LocalizedNameMixin
from django_ledger.models import EntityModel
class CarMake(models.Model, LocalizedNameMixin): class CarMake(models.Model, LocalizedNameMixin):
id_car_make = models.AutoField(primary_key=True) id_car_make = models.AutoField(primary_key=True)
@ -538,7 +539,7 @@ class Dealer(models.Model, LocalizedNameMixin):
def is_parent(self): def is_parent(self):
return self.dealer_type == "Owner" return self.dealer_type == "Owner"
@property @property
def get_parent_or_self(self): def get_root_dealer(self):
return self.parent_dealer if self.parent_dealer else self return self.parent_dealer if self.parent_dealer else self
# Vendor Model # Vendor Model
@ -604,6 +605,8 @@ class Customer(models.Model):
return f"{self.first_name} {self.middle_name} {self.last_name}" return f"{self.first_name} {self.middle_name} {self.last_name}"
class Organization(models.Model, LocalizedNameMixin): class Organization(models.Model, LocalizedNameMixin):
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='organizations') dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='organizations')
name = models.CharField(max_length=255, verbose_name=_("Name")) name = models.CharField(max_length=255, verbose_name=_("Name"))
@ -639,6 +642,8 @@ class Representative(models.Model, LocalizedNameMixin):
class SaleQuotation(models.Model): class SaleQuotation(models.Model):
quotation_number = models.CharField(max_length=10, unique=True)
STATUS_CHOICES = [ STATUS_CHOICES = [
("DRAFT", _("Draft")), ("DRAFT", _("Draft")),
("CONFIRMED", _("Confirmed")), ("CONFIRMED", _("Confirmed")),
@ -647,6 +652,7 @@ class SaleQuotation(models.Model):
dealer = models.ForeignKey( dealer = models.ForeignKey(
Dealer, on_delete=models.CASCADE, related_name="sales", null=True Dealer, on_delete=models.CASCADE, related_name="sales", null=True
) )
entity = models.ForeignKey(EntityModel, on_delete=models.CASCADE)
customer = models.ForeignKey( customer = models.ForeignKey(
Customer, Customer,
on_delete=models.CASCADE, on_delete=models.CASCADE,
@ -660,9 +666,10 @@ class SaleQuotation(models.Model):
verbose_name=_("Amount"), verbose_name=_("Amount"),
) )
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks")) remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
status = models.CharField( is_approved = models.BooleanField(default=False)
max_length=10, choices=STATUS_CHOICES, default="DRAFT", verbose_name=_("Status") # status = models.CharField(
) # max_length=10, choices=STATUS_CHOICES, default="DRAFT", verbose_name=_("Status")
# )
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At")) created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At")) updated_at = models.DateTimeField(auto_now=True, verbose_name=_("Updated At"))
@ -671,30 +678,53 @@ class SaleQuotation(models.Model):
total_quantity = self.quotation_cars.aggregate(total=Sum('quantity'))['total'] total_quantity = self.quotation_cars.aggregate(total=Sum('quantity'))['total']
return total_quantity or 0 return total_quantity or 0
# @property @property
# def total(self): def total(self):
# total = self.quotation_cars.aggregate(total_price=Sum(F('car__finances__selling_price') * F('quantity'))) total = self.quotation_cars.aggregate(total_price=Sum(F('car__finances__selling_price') * F('quantity')))
# if total: if not total:
# return float(total["total_price"]) * 0.15 + float(total["total_price"]) return 0
# return 0 return total["total_price"]
def confirm(self): @property
"""Confirm the quotation and lock financial details.""" def total_vat(self):
if self.status != "DRAFT": if self.total:
raise ValueError(_("Only draft quotations can be confirmed.")) return float(self.total) * 0.15 + float(self.total)
self.status = "CONFIRMED" return 0
self.save()
def cancel(self): # def confirm(self):
"""Cancel the quotation.""" # """Confirm the quotation and lock financial details."""
if self.status == "CONFIRMED": # if self.status != "DRAFT":
raise ValueError(_("Cannot cancel a confirmed quotation.")) # raise ValueError(_("Only draft quotations can be confirmed."))
self.status = "CANCELED" # self.status = "CONFIRMED"
self.save() # self.save()
# def cancel(self):
# """Cancel the quotation."""
# if self.status == "CONFIRMED":
# raise ValueError(_("Cannot cancel a confirmed quotation."))
# self.status = "CANCELED"
# self.save()
def __str__(self): def __str__(self):
return f"Quotation #{self.id} for {self.customer}" return f"Quotation #{self.quotation_number} for {self.customer}"
@property
def display_quotation_number(self):
return f"QN-{self.quotation_number}"
def save(self, *args, **kwargs):
if not self.quotation_number:
self.quotation_number = str(next(self._get_quotation_number())).zfill(6)
super().save(*args, **kwargs)
@classmethod
def _get_quotation_number(cls):
last_quotation = cls.objects.all().order_by('id').last()
if last_quotation:
last_quotation_number = int(last_quotation.quotation_number)
else:
last_quotation_number = 0
return itertools.count(last_quotation_number + 1)
class SaleQuotationCar(models.Model): class SaleQuotationCar(models.Model):
quotation = models.ForeignKey( quotation = models.ForeignKey(
@ -733,14 +763,23 @@ class SaleQuotationCar(models.Model):
# "total_amount": car_finance.total, # "total_amount": car_finance.total,
} }
# @property @property
# def total(self): def total(self):
# """ """
# Calculate total price dynamically based on quantity and selling price. Calculate total price dynamically based on quantity and selling price.
# """ """
# if not self.car.finances: if not self.car.finances:
# return Decimal("0.00") return Decimal("0.00")
# return self.car.finances.selling_price * self.quantity return self.car.finances.selling_price * self.quantity
@property
def total_vat(self):
"""
Calculate total price dynamically based on quantity and selling price.
"""
if not self.car.finances:
return Decimal("0.00")
price = float(self.car.finances.selling_price * self.quantity)
return (price * 0.15) + price
def __str__(self): def __str__(self):
return f"{self.car} - Quotation #{self.quotation.id}" return f"{self.car} - Quotation #{self.quotation.id}"

View File

@ -15,11 +15,19 @@ from django.utils.translation import gettext_lazy as _
from . import models from . import models
@receiver(pre_delete, sender=models.Dealer)
def remove_user_account(sender, instance, **kwargs): # @receiver(post_save, sender=models.SaleQuotation)
user = instance.user # def link_quotation_to_entity(sender, instance, created, **kwargs):
if user: # if created:
user.delete() # # Get the corresponding Django Ledger entity for the dealer
# entity = EntityModel.objects.get(name=instance.dealer.get_root_dealer.name)
# instance.entity = entity
# instance.save()
# @receiver(pre_delete, sender=models.Dealer)
# def remove_user_account(sender, instance, **kwargs):
# user = instance.user
# if user:
# user.delete()
@receiver(post_save, sender=models.Car) @receiver(post_save, sender=models.Car)
def create_car_location(sender, instance, created, **kwargs): def create_car_location(sender, instance, created, **kwargs):
""" """
@ -61,18 +69,23 @@ def update_car_status_on_reservation_delete(sender, instance, **kwargs):
@receiver(post_save, sender=models.Dealer) @receiver(post_save, sender=models.Dealer)
def create_ledger_entity(sender, instance, created, **kwargs): def create_ledger_entity(sender, instance, created, **kwargs):
if created: if created:
entity = EntityModel.objects.create( entity, created = EntityModel.objects.get_or_create(
name=instance.name, name=instance.get_root_dealer.name,
admin=instance.user, admin=instance.get_root_dealer.user,
# address_1=instance.address, # address_1=instance.address,
accrual_method=False, accrual_method=False,
fy_start_month=1, fy_start_month=1,
# depth=0, # depth=0,
) )
print(entity)
default_coa = entity.create_chart_of_accounts(assign_as_default=True, if created:
default_coa = entity.create_chart_of_accounts(assign_as_default=True,
commit=True, commit=True,
coa_name=_("Chart of Accounts")) coa_name=_("Chart of Accounts"))
if default_coa:
entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
print(f"Ledger entity created for Dealer: {instance.name}")
# entity.create_account( # entity.create_account(
# coa_model=coa, # coa_model=coa,
# code=1010, # code=1010,
@ -120,15 +133,12 @@ def create_ledger_entity(sender, instance, created, **kwargs):
# active=True) # active=True)
if default_coa:
entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
# uom_name = _("Unit") # uom_name = _("Unit")
# unit_abbr = _("U") # unit_abbr = _("U")
# #
# entity.create_uom(uom_name, unit_abbr) # entity.create_uom(uom_name, unit_abbr)
print(f"Ledger entity created for Dealer: {instance.name}")
# Create Vendor # Create Vendor
@ -157,21 +167,21 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
@receiver(post_save, sender=models.Customer) @receiver(post_save, sender=models.Customer)
def create_customer(sender, instance, created, **kwargs): def create_customer(sender, instance, created, **kwargs):
if created: if created:
entity = EntityModel.objects.filter(name=instance.dealer.name).first() entity = EntityModel.objects.filter(name=instance.dealer.get_root_dealer.name).first()
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}" name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
entity.create_customer( entity.create_customer(
customer_name=name, customer_model_kwargs={
customer_number=instance.national_id, "customer_name": name,
address_1=instance.address, "address_1": instance.address,
phone=instance.phone_number, "phone": instance.phone_number,
email=instance.email, "email": instance.email,
sales_tax_rate=0.15, "sales_tax_rate": 0.15,
active=True, "active": True,
hidden=False, "hidden": False,
additional_info={} "additional_info": {}
}
) )
print(f"Customer created: {name}") print(f"Customer created: {name}")

View File

@ -73,6 +73,9 @@ urlpatterns = [
path('sales/quotations/', views.QuotationListView.as_view(), name='quotation_list'), path('sales/quotations/', views.QuotationListView.as_view(), name='quotation_list'),
path('sales/quotations/<int:pk>/confirm/', views.confirm_quotation, name='confirm_quotation'), path('sales/quotations/<int:pk>/confirm/', views.confirm_quotation, name='confirm_quotation'),
path('sales/orders/detail/<int:order_id>/', views.SalesOrderDetailView.as_view(), name='order_detail'), path('sales/orders/detail/<int:order_id>/', views.SalesOrderDetailView.as_view(), name='order_detail'),
path('quotation/<pk>/pdf/', views.quotation_pdf_view, name='quotation_pdf'),
path('generate_invoice/<int:pk>/', views.generate_invoice, name='generate_invoice'),
# Users URLs # Users URLs
path('user/create/', views.UserCreateView.as_view(), name='user_create'), path('user/create/', views.UserCreateView.as_view(), name='user_create'),

File diff suppressed because it is too large Load Diff

0
quotation.pdf Normal file
View File

View File

@ -24,6 +24,10 @@
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.10.5/dist/sweetalert2.all.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.10.5/dist/sweetalert2.all.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.5.3/jspdf.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.4/jspdf.plugin.autotable.min.js" integrity="sha512-PRJxIx+FR3gPzyBBl9cPt62DD7owFXVcfYv0CRNFAcLZeEYfht/PpPNTKHicPs+hQlULFhH2tTWdoxnd1UGu1g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<style> <style>
body { body {
@ -40,6 +44,7 @@ small, .small {
} }
</style> </style>
{% block extra_head %}{% endblock extra_head %}
</head> </head>
<body> <body>
{% include 'header.html' %} {% include 'header.html' %}
@ -102,7 +107,14 @@ small, .small {
}); });
}); });
function save_as_pdf(){
const quotationHtml = document.getElementById('quotation-html').outerHTML;
const printWindow = window.open('', '', 'height=500,width=800');
printWindow.document.write(quotationHtml);
printWindow.document.close();
printWindow.print();
printWindow.close();
}
</script> </script>
</body> </body>
</html> </html>

View File

@ -31,9 +31,9 @@
</div> </div>
<div class="container mt-4"> <div class="container mt-4">
<div class="card"> <div class="card" id="quotation-html">
<div class="card-header"> <div class="card-header">
<h4>{% trans "Quotation Details" %} - {{ quotation.id }}</h4> <h4>{% trans "Quotation Details" %} - {{ quotation.display_quotation_number }}</h4>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
@ -47,7 +47,7 @@
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<h5>{% trans "Quotation Information" %}</h5> <h5>{% trans "Quotation Information" %}</h5>
<p><strong>{% trans "Quotation No" %}:</strong> {{ quotation.id }}</p> <p><strong>{% trans "Quotation No" %}:</strong> {{ quotation.display_quotation_number }}</p>
<p><strong>{% trans "Date" %}:</strong> {{ quotation.created_at|date }}</p> <p><strong>{% trans "Date" %}:</strong> {{ quotation.created_at|date }}</p>
<p><strong>{% trans "Remarks" %}:</strong> {{ quotation.remarks }}</p> <p><strong>{% trans "Remarks" %}:</strong> {{ quotation.remarks }}</p>
</div> </div>
@ -75,7 +75,7 @@
<td>{{ item.quantity }}</td> <td>{{ item.quantity }}</td>
<td>{{ item.car.finances.selling_price }}</td> <td>{{ item.car.finances.selling_price }}</td>
<td>{{ 0.15 }}</td> <td>{{ 0.15 }}</td>
<td>{{ item.total }}</td> <td>{{ item.total_vat}}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -83,9 +83,9 @@
<tr> <tr>
<th colspan="3">{% trans "Totals" %}</th> <th colspan="3">{% trans "Totals" %}</th>
<th>{{ quotation.total_quantity }}</th> <th>{{ quotation.total_quantity }}</th>
<th>{{ total_sales_before_vat }}</th> <th>{{ quotation.total }}</th>
<th>{{ vat_amount }}</th> <th>{{ vat_amount }}</th>
<th>{{ total_sales_after_vat }}</th> <th>{{ quotation.total_vat }}</th>
</tr> </tr>
</tfoot> </tfoot>
</table> </table>
@ -115,7 +115,6 @@
<td>{{ total_vat }}</td> <td>{{ total_vat }}</td>
<td>{{ total_cost_vat }}</td> <td>{{ total_cost_vat }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
@ -123,7 +122,15 @@
<a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Back to Quotations" %}</a> <a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Back to Quotations" %}</a>
{% if perms.inventory.change_carfinance and quotation.status == 'DRAFT' %} {% if perms.inventory.change_carfinance and quotation.status == 'DRAFT' %}
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#customCardModal">{% trans "Approve Quotation" %}</button> <button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#customCardModal">{% trans "Approve Quotation" %}</button>
{% endif %} {% endif %}
<a href="{% url 'quotation_pdf' quotation.pk %}" class="btn btn-primary">Download as PDF</a>
{% if not quotation.is_approved %}
<a href="{% url 'confirm_quotation' quotation.pk %}" class="btn btn-success">Approve</a>
{% endif %}
{% if quotation.is_approved %}
<a href="{% url 'generate_invoice' quotation.pk %}" class="btn btn-success">Generate Invoice</a>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -11,6 +11,7 @@
<thead> <thead>
<tr> <tr>
<th>#</th> <th>#</th>
<th>{% trans "Quotation Number" %}</th>
<th>{% trans "Customer" %}</th> <th>{% trans "Customer" %}</th>
<th>{% trans "Total Cars" %}</th> <th>{% trans "Total Cars" %}</th>
<th>{% trans "Total Amount" %}</th> <th>{% trans "Total Amount" %}</th>
@ -22,18 +23,15 @@
{% for quotation in quotations %} {% for quotation in quotations %}
<tr> <tr>
<td>{{ forloop.counter }}</td> <td>{{ forloop.counter }}</td>
<td>{{ quotation.quotation_number }}</td>
<td>{{ quotation.customer.get_full_name }}</td> <td>{{ quotation.customer.get_full_name }}</td>
<td>{{ quotation.quotation_cars.count }}</td> <td>{{ quotation.quotation_cars.count }}</td>
<td>{{ quotation.quotation_cars.get_financial_details.total_amount }}</td> <td>{{ quotation.total_vat }}</td>
<td> <td>
{% if quotation.status == 'DRAFT' %} {% if quotation.is_approved %}
<span class="badge rounded-pill bg-light">{{ quotation.status }}</span> <span class="badge rounded-pill bg-success">Approved</span>
{% elif quotation.status == 'PENDING' %} {% else %}
<span class="badge rounded-pill bg-warning">{{ quotation.status }}</span> <span class="badge rounded-pill bg-warning">Pending For Approval</span>
{% elif quotation.status == 'CONFIRMED' %}
<span class="badge rounded-pill bg-success">{{ quotation.status }}</span>
{% elif quotation.status == 'CANCELED' %}
<span class="badge rounded-pill bg-danger">{{ quotation.status }}</span>
{% endif %} {% endif %}
</td> </td>
<td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td> <td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td>

View File

@ -0,0 +1,112 @@
{% load static %} {% load i18n %}
<!DOCTYPE html>
{% get_current_language as LANGUAGE_CODE %}
<html lang="{{ LANGUAGE_CODE }}" dir="{% if LANGUAGE_CODE == 'ar' %}rtl{% else %}ltr{% endif %}" data-bs-theme="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="mobile-web-app-capable" content="yes" />
<title>{% block title %}{% trans 'HAIKAL' %}{% endblock %}</title>
<link href="{% static 'css/themes/cosmo/_variables.scss' %}" rel="stylesheet" />
<link href="{% static 'css/custom.css' %}" rel="stylesheet" />
{% if LANGUAGE_CODE == 'ar' %}
<link href="{% static 'css/themes/cosmo/bootstrap.rtl.css' %}" rel="stylesheet" />
{% else %}
<link href="{% static 'css/themes/cosmo/bootstrap.css' %}" rel="stylesheet" />
{% endif %}
<link href="{% static 'css/themes/cosmo/_bootswatch.scss' %}" rel="stylesheet" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
</head>
<body>
<div class="container mt-4">
<div class="card" id="quotation-html">
<div class="card-header">
<h4>{% trans "Quotation Details" %} - {{ quotation.quotation_number }}</h4>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h5>{% trans "Customer Details" %}</h5>
<p>
<strong>{% trans "Name" %}:</strong>
{{ quotation.customer.get_full_name }}</p>
<p><strong>{% trans "Address" %}:</strong> {{ quotation.customer.address }}</p>
<p><strong>{% trans "VAT No" %}:</strong> {{ quotation.customer.vat_number }}</p>
</div>
<div class="col-md-6">
<h5>{% trans "Quotation Information" %}</h5>
<p><strong>{% trans "Quotation No" %}:</strong> {{ quotation.quotation_number }}</p>
<p><strong>{% trans "Date" %}:</strong> {{ quotation.created_at|date }}</p>
<p><strong>{% trans "Remarks" %}:</strong> {{ quotation.remarks }}</p>
</div>
</div>
<h5 class="mt-4">{% trans "Car Details" %}</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>{% trans "VIN" %}</th>
<th>{% trans "Model" %}</th>
<th>{% trans "Year" %}</th>
<th>{% trans "Quantity" %}</th>
<th>{% trans "Price" %}</th>
<th>{% trans "VAT" %}</th>
<th>{% trans "Total" %}</th>
</tr>
</thead>
<tbody>
{% for item in quotation.quotation_cars.all %}
<tr>
<td>{{ item.car.vin }}</td>
<td>{{ item.car.id_car_model.get_local_name }}</td>
<td>{{ item.car.year }}</td>
<td>{{ item.quantity }}</td>
<td>{{ item.car.finances.selling_price }}</td>
<td>{{ 0.15 }}</td>
<td>{{ item.total_vat}}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<th colspan="3">{% trans "Totals" %}</th>
<th>{{ quotation.total_quantity }}</th>
<th>{{ quotation.total }}</th>
<th>{{ vat_amount }}</th>
<th>{{ quotation.total_vat }}</th>
</tr>
</tfoot>
</table>
<h5 class="mt-4">{% trans "Additional Costs" %}</h5>
<table class="table table-bordered">
<thead>
<tr>
<th>{% trans "Additions" %}</th>
<th>{% trans "Cost" %}</th>
<th>{% trans "VAT %" %}</th>
<th>{% trans "Total Cost with VAT" %}</th>
</tr>
</thead>
<tbody>
{% for service in services %}
<tr>
<td>{{service.name}}</td>
<td>{{ service.price }}</td>
<td>{{ service.vated }}</td>
<td>{{ service.total_price_vat }}</td>
</tr>
{% endfor %}
<tr>
<td></td>
<td>{{ total_cost }}</td>
<td>{{ total_vat }}</td>
<td>{{ total_cost_vat }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>