This commit is contained in:
Marwan Alwali 2024-12-19 20:08:14 +03:00
commit 791e74b3d3
27 changed files with 556 additions and 263 deletions

View File

@ -1535,6 +1535,4 @@ def decode_vin(vin):
# Example usage # Example usage
vin_number = 'VYFED9HP0SJ519559' vin_number = 'VYFED9HP0SJ519559'
decoded_vin = decode_vin(vin_number) decoded_vin = decode_vin(vin_number)
print(decoded_vin) print(decoded_vin)

View File

@ -23,6 +23,8 @@ admin.site.register(models.CarLocation)
admin.site.register(models.CarReservation) admin.site.register(models.CarReservation)
admin.site.register(models.Organization) admin.site.register(models.Organization)
admin.site.register(models.Representative) admin.site.register(models.Representative)
admin.site.register(models.CarTrim)
admin.site.register(models.AdditionalServices)
@admin.register(models.CarMake) @admin.register(models.CarMake)
class CarMakeAdmin(admin.ModelAdmin): class CarMakeAdmin(admin.ModelAdmin):

View File

@ -17,7 +17,8 @@ from .models import (
CarLocation, CarLocation,
Organization, Organization,
Representative, Representative,
SaleQuotationCar SaleQuotationCar,
AdditionalServices
) )
from django.forms import ModelMultipleChoiceField from django.forms import ModelMultipleChoiceField
@ -112,11 +113,24 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin):
class CarFinanceForm(AddClassMixin, forms.ModelForm): class CarFinanceForm(AddClassMixin, forms.ModelForm):
additional_finances = forms.ModelMultipleChoiceField(
queryset=AdditionalServices.objects.all(),
widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'}),
required=False
)
class Meta: class Meta:
model = CarFinance model = CarFinance
exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate'] exclude = ['car', 'additional_finances','profit_margin', 'vat_amount', 'total', 'vat_rate','additional_services']
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance.pk:
self.fields['additional_finances'].initial = self.instance.additional_services.all()
def save(self, commit=True):
instance = super().save()
instance.additional_services.set(self.cleaned_data['additional_finances'])
instance.save()
return instance
class CarLocationForm(forms.ModelForm): class CarLocationForm(forms.ModelForm):
class Meta: class Meta:

View File

@ -0,0 +1,17 @@
# Generated by Django 4.2.17 on 2024-12-18 10:14
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0030_alter_carfinance_options_and_more'),
]
operations = [
migrations.RemoveField(
model_name='carfinance',
name='vat_amount',
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-18 10:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0031_remove_carfinance_vat_amount'),
]
operations = [
migrations.AddField(
model_name='carfinance',
name='vat_amount',
field=models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=14, verbose_name='Vat Amount'),
preserve_default=False,
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-18 10:19
from decimal import Decimal
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0032_carfinance_vat_amount'),
]
operations = [
migrations.AlterField(
model_name='carfinance',
name='vat_amount',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=14, verbose_name='Vat Amount'),
),
]

View File

@ -0,0 +1,59 @@
# Generated by Django 4.2.17 on 2024-12-18 15:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0033_alter_carfinance_vat_amount'),
]
operations = [
migrations.CreateModel(
name='AdditionalServices',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=255, verbose_name='Name')),
('description', models.TextField(verbose_name='Description')),
('price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Price')),
],
options={
'verbose_name': 'Additional Services',
'verbose_name_plural': 'Additional Services',
},
),
migrations.RemoveField(
model_name='carfinance',
name='administration_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='custom_card_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='discount_amount',
),
migrations.RemoveField(
model_name='carfinance',
name='profit_margin',
),
migrations.RemoveField(
model_name='carfinance',
name='registration_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='transportation_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='vat_amount',
),
migrations.AddField(
model_name='car',
name='additional_services',
field=models.ManyToManyField(related_name='cars', to='inventory.additionalservices'),
),
]

View File

@ -0,0 +1,50 @@
# Generated by Django 4.2.17 on 2024-12-18 16:00
from decimal import Decimal
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0034_additionalservices_and_more'),
]
operations = [
migrations.AddField(
model_name='carfinance',
name='administration_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Administration Fee'),
),
migrations.AddField(
model_name='carfinance',
name='custom_card_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Custom Card Fee'),
),
migrations.AddField(
model_name='carfinance',
name='discount_amount',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount'),
),
migrations.AddField(
model_name='carfinance',
name='profit_margin',
field=models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=14, verbose_name='Profit Margin'),
preserve_default=False,
),
migrations.AddField(
model_name='carfinance',
name='registration_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Registration Fee'),
),
migrations.AddField(
model_name='carfinance',
name='transportation_fee',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Transportation Fee'),
),
migrations.AddField(
model_name='carfinance',
name='vat_amount',
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=14, verbose_name='Vat Amount'),
),
]

View File

@ -0,0 +1,22 @@
# Generated by Django 4.2.17 on 2024-12-18 16:02
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0035_carfinance_administration_fee_and_more'),
]
operations = [
migrations.RemoveField(
model_name='car',
name='additional_services',
),
migrations.AddField(
model_name='carfinance',
name='additional_services',
field=models.ManyToManyField(related_name='cars', to='inventory.additionalservices'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-19 09:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0036_remove_car_additional_services_and_more'),
]
operations = [
migrations.AlterField(
model_name='carfinance',
name='additional_services',
field=models.ManyToManyField(related_name='additional_finances', to='inventory.additionalservices'),
),
]

View File

@ -0,0 +1,42 @@
# Generated by Django 4.2.17 on 2024-12-19 09:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0037_alter_carfinance_additional_services'),
]
operations = [
migrations.RemoveField(
model_name='carfinance',
name='administration_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='custom_card_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='profit_margin',
),
migrations.RemoveField(
model_name='carfinance',
name='registration_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='transportation_fee',
),
migrations.RemoveField(
model_name='carfinance',
name='vat_amount',
),
migrations.AlterField(
model_name='carfinance',
name='additional_services',
field=models.ManyToManyField(blank=True, null=True, related_name='additional_finances', to='inventory.additionalservices'),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.17 on 2024-12-19 09:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0038_remove_carfinance_administration_fee_and_more'),
]
operations = [
migrations.AlterField(
model_name='carfinance',
name='additional_services',
field=models.ManyToManyField(blank=True, related_name='additional_finances', to='inventory.additionalservices'),
),
]

View File

@ -0,0 +1,19 @@
# Generated by Django 4.2.17 on 2024-12-19 12:01
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0039_alter_carfinance_additional_services'),
]
operations = [
migrations.AddField(
model_name='additionalservices',
name='display_name',
field=models.CharField(default=1, max_length=255, verbose_name='Display Name'),
preserve_default=False,
),
]

View File

@ -1,6 +1,7 @@
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
from django.db.models import Sum, F, Count
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db.models.signals import pre_save, post_save from django.db.models.signals import pre_save, post_save
from django.dispatch import receiver from django.dispatch import receiver
@ -22,6 +23,7 @@ from django.core.exceptions import ValidationError
from phonenumber_field.modelfields import PhoneNumberField 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 .mixins import LocalizedNameMixin from .mixins import LocalizedNameMixin
@ -140,6 +142,19 @@ class DEALER_TYPES(models.TextChoices):
Sales = "sales", _("Sales") Sales = "sales", _("Sales")
class AdditionalServices(models.Model):
name = models.CharField(max_length=255, verbose_name=_("Name"))
display_name = models.CharField(max_length=255, verbose_name=_("Display Name"))
description = models.TextField(verbose_name=_("Description"))
price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Price"))
class Meta:
verbose_name = _("Additional Services")
verbose_name_plural = _("Additional Services")
def __str__(self):
return self.name + " - " + str(self.price)
class Car(models.Model): class Car(models.Model):
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN")) vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
dealer = models.ForeignKey( dealer = models.ForeignKey(
@ -217,90 +232,6 @@ class Car(models.Model):
active_reservations = self.reservations.filter(reserved_until__gt=now()) active_reservations = self.reservations.filter(reserved_until__gt=now())
return active_reservations.exists() return active_reservations.exists()
@property
def finance(self):
"""Fetch the first related CarFinance object, or return None."""
return self.finances # Assuming a related_name of 'finances'
def _get_financial_value(self, attribute):
"""Reusable method to safely get financial values."""
finance = self.finance
return getattr(finance, attribute, Decimal('0.00')) if finance else Decimal('0.00')
def _calculate_vat(self, value):
"""Helper to calculate VAT dynamically for a given value."""
vat_rate = getattr(settings, 'VAT_RATE', Decimal('0.15')) # Default VAT rate
return (value * vat_rate).quantize(Decimal('0.01'))
@property
def cost_price(self):
return self._get_financial_value('cost_price')
@property
def selling_price(self):
return self._get_financial_value('selling_price')
@property
def registration_fee(self):
return self._get_financial_value('registration_fee')
@property
def administration_fee(self):
return self._get_financial_value('administration_fee')
@property
def transportation_fee(self):
return self._get_financial_value('transportation_fee')
@property
def custom_card_fee(self):
return self._get_financial_value('custom_card_fee')
@property
def discount_amount(self):
return self._get_financial_value('discount_amount')
@property
def vat_amount(self):
"""Dynamically calculate VAT for the selling price after discount."""
price_after_discount = self.selling_price - self.discount_amount
return self._calculate_vat(price_after_discount)
@property
def administration_fee_vat(self):
return self._calculate_vat(self.administration_fee)
@property
def transportation_fee_vat(self):
return self._calculate_vat(self.transportation_fee)
@property
def custom_card_fee_vat(self):
return self._calculate_vat(self.custom_card_fee)
@property
def total_vat_amount(self):
"""Sum up the VAT for all applicable fields."""
return (
self.vat_amount +
self.administration_fee_vat +
self.transportation_fee_vat +
self.custom_card_fee_vat
)
@property
def total(self):
"""Calculate the total amount including VAT."""
price_after_discount = self.selling_price - self.discount_amount
subtotal = (
price_after_discount +
self.registration_fee +
self.administration_fee +
self.transportation_fee +
self.custom_card_fee
)
return subtotal + self.total_vat_amount
# class CarData(models.Model): # class CarData(models.Model):
# vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN")) # vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
# make = models.CharField(max_length=255, verbose_name=_("Make")) # make = models.CharField(max_length=255, verbose_name=_("Make"))
@ -328,7 +259,6 @@ class Car(models.Model):
# receiving_date = models.DateTimeField(verbose_name=_("Receiving Date")) # receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
class CarReservation(models.Model): class CarReservation(models.Model):
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car")) car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car"))
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By")) reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By"))
@ -344,49 +274,52 @@ class CarReservation(models.Model):
verbose_name = _("Car Reservation") verbose_name = _("Car Reservation")
verbose_name_plural = _("Car Reservations") verbose_name_plural = _("Car Reservations")
# Car Finance Model # Car Finance Model
class CarFinance(models.Model): class CarFinance(models.Model):
additional_services = models.ManyToManyField(AdditionalServices, related_name="additional_finances",blank=True)
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances') car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances')
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price")) cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price")) selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
profit_margin = models.DecimalField(max_digits=14,
decimal_places=2,
verbose_name=_("Profit Margin"),
editable=False)
vat_amount = models.DecimalField(max_digits=14,
decimal_places=2,
verbose_name=_("Vat Amount"),
editable=False)
discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"), discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
default=Decimal('0.00')) default=Decimal('0.00'))
registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"), # profit_margin = models.DecimalField(max_digits=14,
default=Decimal('0.00')) # decimal_places=2,
administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"), # verbose_name=_("Profit Margin"),
default=Decimal('0.00')) # editable=False)
transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"), # vat_amount = models.DecimalField(max_digits=14,
default=Decimal('0.00')) # decimal_places=2,
custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"), # verbose_name=_("Vat Amount"),
default=Decimal('0.00')) # editable=False,default=Decimal('0.00'))
# registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
# default=Decimal('0.00'))
# administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"),
# default=Decimal('0.00'))
# transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"),
# default=Decimal('0.00'))
# custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
# default=Decimal('0.00'))
@property
def total(self):
"""Calculate the total amount including VAT."""
return get_total(self)
def __str__(self): def __str__(self):
return f"Car: {self.car}, Selling Price: {self.selling_price}" return f"Car: {self.car}, Selling Price: {self.selling_price}"
def save(self, *args, **kwargs): # def save(self, *args, **kwargs):
# vat_rate = settings.VAT_RATE # self.full_clean()
self.full_clean() # try:
try: # price_after_discount = self.selling_price - self.discount_amount
price_after_discount = self.selling_price - self.discount_amount # self.profit_margin = price_after_discount - self.cost_price
self.profit_margin = price_after_discount - self.cost_price # self.vat_amount = settings.VAT_RATE
except InvalidOperation as e: # except InvalidOperation as e:
raise ValidationError(_("Invalid decimal operation: %s") % str(e)) # raise ValidationError(_("Invalid decimal operation: %s") % str(e))
super().save(*args, **kwargs) # super().save(*args, **kwargs)
class Meta: class Meta:
verbose_name = _("Car Financial Details") verbose_name = _("Car Financial Details")
verbose_name_plural = _("Car Financial Details") verbose_name_plural = _("Car Financial Details")
class ExteriorColors(models.Model, LocalizedNameMixin): class ExteriorColors(models.Model, LocalizedNameMixin):
name = models.CharField(max_length=255, verbose_name=_("Name")) name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name")) arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
@ -738,6 +671,13 @@ 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
# def total(self):
# total = self.quotation_cars.aggregate(total_price=Sum(F('car__finances__selling_price') * F('quantity')))
# if total:
# return float(total["total_price"]) * 0.15 + float(total["total_price"])
# return 0
def confirm(self): def confirm(self):
"""Confirm the quotation and lock financial details.""" """Confirm the quotation and lock financial details."""
if self.status != "DRAFT": if self.status != "DRAFT":
@ -769,7 +709,10 @@ class SaleQuotationCar(models.Model):
verbose_name=_("Car") verbose_name=_("Car")
) )
quantity = models.PositiveIntegerField(default=1, verbose_name=_("Quantity")) quantity = models.PositiveIntegerField(default=1, verbose_name=_("Quantity"))
@property
def finance(self):
return self.car.finances
@property @property
def financial_details(self): def financial_details(self):
""" """
@ -790,15 +733,14 @@ class SaleQuotationCar(models.Model):
# "total_amount": car_finance.total, # "total_amount": car_finance.total,
} }
@property # @property
def total_price(self): # def total(self):
""" # """
Calculate total price dynamically based on quantity and selling price. # Calculate total price dynamically based on quantity and selling price.
""" # """
car_finance = self.car.finances # if not self.car.finances:
if not car_finance: # return Decimal("0.00")
return Decimal("0.00") # return self.car.finances.selling_price * self.quantity
return car_finance.selling_price * self.quantity
def __str__(self): def __str__(self):
return f"{self.car} - Quotation #{self.quotation.id}" return f"{self.car} - Quotation #{self.quotation.id}"

View File

@ -7,7 +7,7 @@ import json
from django_ledger.models import EntityModel from django_ledger.models import EntityModel
from .utils import get_jwt_token from inventory.utils import get_jwt_token
from pyvin import VIN from pyvin import VIN
from django.conf import settings from django.conf import settings
from openai import OpenAI from openai import OpenAI

View File

View File

@ -0,0 +1,38 @@
from decimal import Decimal
from django.conf import settings
from inventory import models
def calculate_vat(value):
"""Helper to calculate VAT dynamically for a given value."""
vat_rate = getattr(settings, 'VAT_RATE', Decimal('0.15')) # Default VAT rate
return (value * vat_rate).quantize(Decimal('0.01'))
# def get_financial_value(instance,attribute,vat=False):
# if vat:
# return calculate_vat(getattr(instance, attribute, Decimal('0.00')) if instance else Decimal('0.00'))
# return getattr(instance, attribute, Decimal('0.00')) if instance else Decimal('0.00')
def get_financial_value(name,vat=False):
val = models.AdditionalServices.objects.filter(name=name).first()
if not val:
return 0
if vat:
return (val.price * settings.VAT_RATE).quantize(Decimal('0.01'))
return val.price
def get_total_financials(instance,vat=False):
# price_after_discount = get_financial_value(instance,"selling_price",vat) - get_financial_value(instance,"discount_amount",vat)
# subtotal = (
# price_after_discount +
# get_financial_value("registration_fee") +
# get_financial_value("administration_fee",vat) +
# get_financial_value("transportation_fee",vat) +
# get_financial_value("custom_card_fee",vat))
return 1000
def get_total(instance):
total = get_total_financials(instance)
total_vat = get_total_financials(instance,vat=True)
return total + total_vat

View File

@ -1,5 +1,8 @@
import requests import requests
from django.conf import settings
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from inventory import models
from inventory.utilities.financials import get_financial_value
def get_jwt_token(): def get_jwt_token():
@ -29,3 +32,20 @@ def localize_some_words():
return None return None
def get_calculations(quotation):
context = {}
qc_len = quotation.quotation_cars.count()
cars = [x.car for x in quotation.quotation_cars.all()]
finances = models.CarFinance.objects.filter(car__in=cars)
services = models.AdditionalServices.objects.filter(additional_finances__in=finances).all()
data = [{"name":x.name,"price":x.price,"total_price":x.price * qc_len,"vated":float(x.price) * 0.15 * float(qc_len),"total_price_vat":float(x.price) + (float(x.price) * 0.15 * float(qc_len))} for x in services]
context["services"] = data
context["total_cost"] = 0
context["total_vat"] = 0
context["total_cost_vat"] = 0
for k in context["services"]:
context["total_cost"] += k["total_price"]
context["total_vat"] += k["vated"]
context["total_cost_vat"] = float(context["total_cost"])+float(context["total_vat"])
return context

View File

@ -28,6 +28,7 @@ from django.contrib import messages
from django.db.models import Sum, F, Count from django.db.models import Sum, F, Count
from inventory.mixins import AddDealerInstanceMixin from inventory.mixins import AddDealerInstanceMixin
from .services import elm, decodevin,get_make,get_model,normalize_name from .services import elm, decodevin,get_make,get_model,normalize_name
from .services import elm, decodevin, get_make, get_model, normalize_name, get_ledger_data from .services import elm, decodevin, get_make, get_model, normalize_name, get_ledger_data
from . import models, forms from . import models, forms
@ -37,7 +38,7 @@ from django.contrib.auth.decorators import user_passes_test
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.contrib.auth.models import Group from django.contrib.auth.models import Group
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from .utils import get_calculations
User = get_user_model() User = get_user_model()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -187,6 +188,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
def get_series(self, request): def get_series(self, request):
model_id = request.GET.get('model_id') model_id = request.GET.get('model_id')
year = request.GET.get('year') year = request.GET.get('year')
# Validate inputs # Validate inputs
if not model_id or not year: if not model_id or not year:
@ -206,9 +208,9 @@ class AjaxHandlerView(LoginRequiredMixin, View):
def get_trims(self, request): def get_trims(self, request):
serie_id = request.GET.get('serie_id') serie_id = request.GET.get('serie_id')
model_id = request.GET.get('model_id') # model_id = request.GET.get('model_id')
trims = models.CarTrim.objects.filter( trims = models.CarTrim.objects.filter(
id_car_serie=serie_id, id_car_model=model_id).values('id_car_trim', 'name', 'arabic_name') id_car_serie=serie_id).values('id_car_trim', 'name', 'arabic_name')
return JsonResponse(list(trims), safe=False) return JsonResponse(list(trims), safe=False)
def get_specifications(self, request): def get_specifications(self, request):
@ -403,6 +405,11 @@ class CarFinanceUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
def get_success_url(self): def get_success_url(self):
return reverse('car_detail', kwargs={'pk': self.object.car.pk}) return reverse('car_detail', kwargs={'pk': self.object.car.pk})
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = self.get_object()
return kwargs
class CarUpdateView(LoginRequiredMixin, SuccessMessageMixin,UpdateView): class CarUpdateView(LoginRequiredMixin, SuccessMessageMixin,UpdateView):
@ -547,10 +554,11 @@ class DealerUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
def get_form(self, form_class=None): def get_form(self, form_class=None):
form = super().get_form(form_class) form = super().get_form(form_class)
form.fields.pop('dealer_type') if hasattr(form.fields, 'dealer_type'):
form.fields.pop('dealer_type')
return form return form
def get_form_class(self): def get_form_class(self):
if self.request.user.dealer.is_parent: if self.request.user.dealer.dealer_type == 'Owner':
return forms.DealerForm return forms.DealerForm
else: else:
return forms.UserForm return forms.UserForm
@ -662,8 +670,10 @@ class QuotationCreateView(LoginRequiredMixin,PermissionRequiredMixin, CreateView
def form_valid(self, form): def form_valid(self, form):
dealer = self.request.user.dealer.get_parent_or_self
form.instance.dealer = dealer
quotation = form.save() quotation = form.save()
selected_cars = form.cleaned_data.get("cars") selected_cars = form.cleaned_data.get("cars")
for car in selected_cars: for car in selected_cars:
car_finance = car.finances car_finance = car.finances
if car_finance: if car_finance:
@ -686,7 +696,9 @@ class QuotationListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
def get_queryset(self): def get_queryset(self):
status = self.request.GET.get("status") status = self.request.GET.get("status")
queryset = models.SaleQuotation.objects.all() # queryset = models.SaleQuotation.objects.all()
print(self.request.user.dealer.get_parent_or_self.sales.all())
queryset = self.request.user.dealer.get_parent_or_self.sales.all()
if status: if status:
queryset = queryset.filter(status=status) queryset = queryset.filter(status=status)
return queryset return queryset
@ -697,56 +709,13 @@ class QuotationDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView
template_name = "sales/quotation_detail.html" template_name = "sales/quotation_detail.html"
context_object_name = "quotation" context_object_name = "quotation"
permission_required = ('inventory.view_salequotation',) permission_required = ('inventory.view_salequotation',)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
quotation = self.object quotation = self.object
# Totals Calculation context_result = get_calculations(quotation)
context['vat_rate'] = settings.VAT_RATE context.update(context_result)
context['total_sales_before_vat'] = sum(item.car.selling_price * item.quantity for item in quotation.quotation_cars.all())
context['vat_amount'] = sum(item.car.vat_amount * item.quantity for item in quotation.quotation_cars.all())
context['total_sales_after_vat'] = context['total_sales_before_vat'] + context['vat_amount']
# Additional Costs
total_quantity = quotation.total_quantity
context['administration_fee'] = sum(item.car.administration_fee for item in quotation.quotation_cars.all())
context['transportation_fee'] = sum(item.car.transportation_fee for item in quotation.quotation_cars.all())
context['custom_card_fee'] = sum(item.car.custom_card_fee for item in quotation.quotation_cars.all())
context['registration_fee'] = sum(item.car.registration_fee for item in quotation.quotation_cars.all())
context['administration_fee_vat'] = sum(item.car.administration_fee_vat for item in quotation.quotation_cars.all())
context['transportation_fee_vat'] = sum(item.car.transportation_fee_vat for item in quotation.quotation_cars.all())
context['custom_card_fee_vat'] = sum(item.car.custom_card_fee_vat for item in quotation.quotation_cars.all())
context['administration_fee_total'] = sum(item.car.administration_fee + context['administration_fee_vat'] for item in quotation.quotation_cars.all())
context['transportation_fee_total'] = sum(item.car.transportation_fee + context['transportation_fee_vat'] for item in quotation.quotation_cars.all())
context['custom_card_fee_total'] = sum(item.car.custom_card_fee + context['custom_card_fee_vat'] for item in quotation.quotation_cars.all())
context['registration_fee_total'] = sum(item.car.registration_fee * total_quantity for item in quotation.quotation_cars.all())
return context
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
quotation = self.object
# Totals Calculation
context['vat_rate'] = settings.VAT_RATE
context['total_sales_before_vat'] = sum(item.car.selling_price * item.quantity for item in quotation.quotation_cars.all())
context['vat_amount'] = sum(item.car.vat_amount * item.quantity for item in quotation.quotation_cars.all())
context['total_sales_after_vat'] = context['total_sales_before_vat'] + context['vat_amount']
# Additional Costs
total_quantity = quotation.total_quantity
context['administration_fee'] = sum(item.car.administration_fee for item in quotation.quotation_cars.all())
context['transportation_fee'] = sum(item.car.transportation_fee for item in quotation.quotation_cars.all())
context['custom_card_fee'] = sum(item.car.custom_card_fee for item in quotation.quotation_cars.all())
context['registration_fee'] = sum(item.car.registration_fee for item in quotation.quotation_cars.all())
context['administration_fee_vat'] = sum(item.car.administration_fee_vat for item in quotation.quotation_cars.all())
context['transportation_fee_vat'] = sum(item.car.transportation_fee_vat for item in quotation.quotation_cars.all())
context['custom_card_fee_vat'] = sum(item.car.custom_card_fee_vat for item in quotation.quotation_cars.all())
context['administration_fee_total'] = sum(item.car.administration_fee + context['administration_fee_vat'] for item in quotation.quotation_cars.all())
context['transportation_fee_total'] = sum(item.car.transportation_fee + context['transportation_fee_vat'] for item in quotation.quotation_cars.all())
context['custom_card_fee_total'] = sum(item.car.custom_card_fee + context['custom_card_fee_vat'] for item in quotation.quotation_cars.all())
context['registration_fee_total'] = sum(item.car.registration_fee * total_quantity for item in quotation.quotation_cars.all())
return context return context
@ -756,6 +725,8 @@ def confirm_quotation(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk) quotation = get_object_or_404(models.SaleQuotation, pk=pk)
try: try:
quotation.confirm() quotation.confirm()
quotation_cars = quotation.quotation_cars.annotate(total_price=F('car__total') * F('quantity'))
total_amount = quotation_cars.aggregate(total=Sum('total_price'))['total']
models.SalesOrder.objects.create( models.SalesOrder.objects.create(
quotation=quotation, quotation=quotation,
total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"], total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"],
@ -800,8 +771,8 @@ class UserListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
class UserDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView): class UserDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView):
model = models.Dealer model = models.Dealer
template_name = "users/user_detail.html" template_name = "users/user_detail.html"
context_object_name = "user" context_object_name = "user_"
permission_required = ('inventory.view_dealer',) permission_required = ('inventory.view_dealer',)
class UserCreateView(LoginRequiredMixin,PermissionRequiredMixin,SuccessMessageMixin,AddDealerInstanceMixin, CreateView): class UserCreateView(LoginRequiredMixin,PermissionRequiredMixin,SuccessMessageMixin,AddDealerInstanceMixin, CreateView):
model = models.Dealer model = models.Dealer
@ -890,23 +861,22 @@ class OrganizationDetailView(DetailView):
context_object_name = 'organization' context_object_name = 'organization'
class OrganizationCreateView(LoginRequiredMixin, CreateView): class OrganizationCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView):
model = models.Organization model = models.Organization
form_class = forms.OrganizationForm form_class = forms.OrganizationForm
template_name = 'organizations/organization_form.html' template_name = 'organizations/organization_form.html'
success_url = reverse_lazy('organization_list') success_url = reverse_lazy('organization_list')
success_message = "Organization created successfully."
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 form.instance.dealer = self.request.user.dealer.get_parent_or_self
form.save() form.save()
success_message = "Organization created successfully."
return super().form_valid(form) return super().form_valid(form)
else: else:
return form.errors return form.errors
class OrganizationUpdateView(LoginRequiredMixin, UpdateView): class OrganizationUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
model = models.Organization model = models.Organization
form_class = forms.OrganizationForm form_class = forms.OrganizationForm
template_name = 'organizations/organization_form.html' template_name = 'organizations/organization_form.html'
@ -914,7 +884,7 @@ class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
success_message = "Organization updated successfully." success_message = "Organization updated successfully."
class OrganizationDeleteView(LoginRequiredMixin, DeleteView): class OrganizationDeleteView(LoginRequiredMixin,SuccessMessageMixin, DeleteView):
model = models.Organization model = models.Organization
template_name = 'organizations/organization_confirm_delete.html' template_name = 'organizations/organization_confirm_delete.html'
success_url = reverse_lazy('organization_list') success_url = reverse_lazy('organization_list')
@ -926,30 +896,28 @@ class RepresentativeListView(LoginRequiredMixin, ListView):
template_name = 'representatives/representative_list.html' template_name = 'representatives/representative_list.html'
context_object_name = 'representatives' context_object_name = 'representatives'
class RepresentativeDetailView(DetailView): class RepresentativeDetailView(DetailView):
model = models.Representative model = models.Representative
template_name = 'representatives/representative_detail.html' template_name = 'representatives/representative_detail.html'
context_object_name = 'representative' context_object_name = 'representative'
class RepresentativeCreateView(LoginRequiredMixin, SuccessMessageMixin,CreateView):
class RepresentativeCreateView(LoginRequiredMixin, CreateView):
model = models.Representative model = models.Representative
form_class = forms.RepresentativeForm form_class = forms.RepresentativeForm
template_name = 'representatives/representative_form.html' template_name = 'representatives/representative_form.html'
success_url = reverse_lazy('representative_list') success_url = reverse_lazy('representative_list')
success_message = "Representative created successfully."
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 form.instance.dealer = self.request.user.dealer.get_parent_or_self
form.save() form.save()
success_message = "Representative created successfully."
return super().form_valid(form) return super().form_valid(form)
else: else:
return form.errors return form.errors
class RepresentativeUpdateView(LoginRequiredMixin, UpdateView): class RepresentativeUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
model = models.Representative model = models.Representative
form_class = forms.RepresentativeForm form_class = forms.RepresentativeForm
template_name = 'representatives/representative_form.html' template_name = 'representatives/representative_form.html'
@ -957,8 +925,8 @@ class RepresentativeUpdateView(LoginRequiredMixin, UpdateView):
success_message = "Representative updated successfully." success_message = "Representative updated successfully."
class RepresentativeDeleteView(LoginRequiredMixin, DeleteView): class RepresentativeDeleteView(LoginRequiredMixin,SuccessMessageMixin, DeleteView):
model = models.Representative model = models.Representative
template_name = 'representatives/representative_confirm_delete.html' template_name = 'representatives/representative_confirm_delete.html'
success_url = reverse_lazy('representative_list') success_url = reverse_lazy('representative_list')
success_message = "Representative deleted successfully." success_message = "Representative deleted successfully."

View File

@ -19,7 +19,7 @@
<span class="navbar-toggler-icon"></span> <span class="navbar-toggler-icon"></span>
</button> </button>
<div class="collapse navbar-collapse" id="headerNav"> <div class="collapse navbar-collapse" id="headerNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0"> <ul class="navbar-nav me-auto mb-2 mb-lg-0">
{% if user.is_authenticated and user.dealer.pk %} {% if user.is_authenticated and user.dealer.pk %}
<li class="nav-item"> <li class="nav-item">
{% block home %}<a class="nav-link" href="{% url 'landing_page' %}">{% trans 'home' %}</a>{% endblock %} {% block home %}<a class="nav-link" href="{% url 'landing_page' %}">{% trans 'home' %}</a>{% endblock %}

View File

@ -62,7 +62,7 @@
<div class="modal-dialog modal-lg modal-dialog-scrollable"> <div class="modal-dialog modal-lg modal-dialog-scrollable">
<div class="modal-content glossy-modal"> <div class="modal-content glossy-modal">
<div class="modal-header bg-primary text-light"> <div class="modal-header bg-primary text-light">
<h5 class="modal-title" id="specificationsModalLabel"> <h5 class="modal-title" id="specificationsModalLabel">
{% trans 'specifications'|upper %} {% trans 'specifications'|upper %}
</h5> </h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button> <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
@ -227,27 +227,23 @@
<tr> <tr>
<th>{% trans "Selling Price" %}</th> <th>{% trans "Selling Price" %}</th>
<td>{{ car.finances.selling_price }}</td> <td>{{ car.finances.selling_price }}</td>
</tr> </tr>
<tr>
<td><small class="ms-5">{% trans "Administration Fee" %}</small></td>
<td><small>{{ car.finances.administration_fee }}</small></td>
</tr>
<tr>
<td><small class="ms-5">{% trans "Registration Fee" %}</small></td>
<td><small>{{ car.finances.registration_fee }}</small></td>
</tr>
<tr>
<td><small class="ms-5">{% trans "Transportation Fee" %}</small></td>
<td><small>{{ car.finances.transportation_fee }}</small></td>
</tr>
<tr>
<td><small class="ms-5">{% trans "Custom Card Fee" %}</small></td>
<td><small>{{ car.finances.custom_card_fee }}</small></td>
</tr>
<tr> <tr>
<th>{% trans "Discount Amount" %}</th> <th>{% trans "Discount Amount" %}</th>
<td>{{ car.finances.discount_amount }} -</td> <td>{{ car.finances.discount_amount }} -</td>
</tr>
<tr>
<th>{% trans "Additional Fee" %}</th>
<td></td>
</tr> </tr>
{% if car.finances.additional_services.first.pk %}
{% for service in car.finances.additional_services.all %}
<tr>
<td><small class="ms-5">{{service.name}}</small></td>
<td><small>{{ service.price }}</small></td>
</tr>
{% endfor %}
{% endif %}
<tr> <tr>
<th>{% trans "VAT Amount" %}</th> <th>{% trans "VAT Amount" %}</th>
<td>{{ car.finances.vat_amount }}</td> <td>{{ car.finances.vat_amount }}</td>

View File

@ -624,8 +624,8 @@ async function loadSpecifications(trimId){
/*stockTypeSelect.addEventListener('change', checkFormCompletion); /*stockTypeSelect.addEventListener('change', checkFormCompletion);
mileageInput.addEventListener('input', checkFormCompletion); mileageInput.addEventListener('input', checkFormCompletion);
remarksInput.addEventListener('input', checkFormCompletion);*/ remarksInput.addEventListener('input', checkFormCompletion);*/
makeSelect.addEventListener("change", (e) => {loadModels(e.target.value)}) makeSelect.addEventListener("change", (e) => {loadModels(e.target.value, modelSelect.value)})
modelSelect.addEventListener("change", (e) => {loadSeries(e.target.value)}) modelSelect.addEventListener("change", (e) => {loadSeries(e.target.value, yearSelect.value)})
decodeVinBtn.addEventListener('click', decodeVin); decodeVinBtn.addEventListener('click', decodeVin);
}); });
const Toast = Swal.mixin({ const Toast = Swal.mixin({

View File

@ -65,12 +65,12 @@
{% else %} {% else %}
<td><span class="color-div">{% trans 'No Color' %}</span></td> <td><span class="color-div">{% trans 'No Color' %}</span></td>
<td><span class="color-div">{% trans 'No Color' %}</span></td> <td><span class="color-div">{% trans 'No Color' %}</span></td>
{% endif %} {% endif %}
{% if car.location.is_owner_showroom %} {% if car.location.is_owner_showroom %}
<td> {% trans 'Our Showroom' %}</td> <td> {% trans 'Our Showroom' %}</td>
{% else %} {% else %}
<td>{{ car.location.showroom.get_local_name }}</td> <td>{{ car.location.showroom.get_local_name }}</td>
{% endif %} {% endif %}
<td> <td>
<a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success"> <a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success">
<small>{% trans "view" %}</small> <small>{% trans "view" %}</small>

View File

@ -2,6 +2,34 @@
{% load custom_filters %} {% load custom_filters %}
{% load i18n %} {% load i18n %}
{% block content %} {% block content %}
<!-- Custom Card Modal -->
<div class="modal fade" id="customCardModal" tabindex="-1" aria-labelledby="customCardModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header bg-primary">
<h5 class="modal-title text-light" id="customCardModalLabel">{% trans 'Custom Card' %}</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
{% trans 'Confirm Approve?' %}
<div class="modal-footer">
<button type="button"
class="btn btn-sm btn-danger"
data-bs-dismiss="modal">
{% trans 'No' %}
</button>
<form method="POST" action="{% url 'confirm_quotation' quotation.id %}" class="d-inline">
{% csrf_token %}
<button type="submit" class="btn btn-success btn-sm">{% trans "Yes" %}</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="container mt-4"> <div class="container mt-4">
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
@ -45,9 +73,9 @@
<td>{{ item.car.id_car_model.get_local_name }}</td> <td>{{ item.car.id_car_model.get_local_name }}</td>
<td>{{ item.car.year }}</td> <td>{{ item.car.year }}</td>
<td>{{ item.quantity }}</td> <td>{{ item.quantity }}</td>
<td>{{ item.car.selling_price }}</td> <td>{{ item.car.finances.selling_price }}</td>
<td>{{ item.car.vat_amount }}</td> <td>{{ 0.15 }}</td>
<td>{{ item.car.total }}</td> <td>{{ item.total }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -68,40 +96,34 @@
<tr> <tr>
<th>{% trans "Additions" %}</th> <th>{% trans "Additions" %}</th>
<th>{% trans "Cost" %}</th> <th>{% trans "Cost" %}</th>
<th>{% trans "VAT" %}</th> <th>{% trans "VAT %" %}</th>
<th>{% trans "Total Cost with VAT" %}</th> <th>{% trans "Total Cost with VAT" %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for service in services %}
<tr> <tr>
<td>{% trans "Administration Fee" %}</td> <td>{{service.name}}</td>
<td>{{ administration_fee }}</td> <td>{{ service.price }}</td>
<td>{{ administration_fee_vat }}</td> <td>{{ service.vated }}</td>
<td>{{ administration_fee_total }}</td> <td>{{ service.total_price_vat }}</td>
</tr> </tr>
{% endfor %}
<tr> <tr>
<td>{% trans "Transportation Fee" %}</td> <td></td>
<td>{{ transportation_fee }}</td> <td>{{ total_cost }}</td>
<td>{{ transportation_fee_vat }}</td> <td>{{ total_vat }}</td>
<td>{{ transportation_fee_total }}</td> <td>{{ total_cost_vat }}</td>
</tr>
<tr>
<td>{% trans "Custom Card Fee" %}</td>
<td>{{ custom_card_fee }}</td>
<td>{{ custom_card_fee_vat }}</td>
<td>{{ custom_card_fee_total }}</td>
</tr>
<tr>
<td>{% trans "Registration Fee (No VAT)" %}</td>
<td>{{ registration_fee }}</td>
<td>{% trans "N/A" %}</td>
<td>{{ registration_fee_total }}</td>
</tr> </tr>
</tbody> </tbody>
</table> </table>
</div> </div>
<div class="card-footer text-end"> <div class="card-footer text-end">
<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' %}
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#customCardModal">{% trans "Approve Quotation" %}</button>
{% endif %}
</div> </div>
</div> </div>
</div> </div>

View File

@ -20,12 +20,22 @@
</thead> </thead>
<tbody> <tbody>
{% for quotation in quotations %} {% for quotation in quotations %}
<tr> <tr>
<td>{{ forloop.counter }}</td> <td>{{ forloop.counter }}</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.quotation_cars.get_financial_details.total_amount }}</td>
<td>
{% if quotation.status == 'DRAFT' %}
<span class="badge rounded-pill bg-light">{{ quotation.status }}</span>
{% elif quotation.status == 'PENDING' %}
<span class="badge rounded-pill bg-warning">{{ quotation.status }}</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 %}
</td>
<td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td> <td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td>
<td> <td>
<a href="{% url 'quotation_detail' quotation.id %}" class="btn btn-sm btn-info"> <a href="{% url 'quotation_detail' quotation.id %}" class="btn btn-sm btn-info">

View File

@ -1,4 +1,5 @@
{% extends "base.html" %} {% extends "base.html" %}
{% load static %}
{% load i18n %} {% load i18n %}
{% block title %}{{ _("View Customer") }}{% endblock title %} {% block title %}{{ _("View Customer") }}{% endblock title %}
@ -27,7 +28,7 @@
</button> </button>
<a type="button" <a type="button"
class="btn btn-sm btn-danger" class="btn btn-sm btn-danger"
href="{% url 'user_delete' user.id %}"> href="{% url 'user_delete' user_.id %}">
{% trans 'Yes' %} {% trans 'Yes' %}
</a> </a>
</div> </div>
@ -42,19 +43,19 @@
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<p><strong>{{ _("Name") }}:</strong> {{ user.name }}</p> <p><strong>{{ _("Name") }}:</strong> {{ user_.name }}</p>
<p><strong>{{ _("Arabic Name") }}:</strong> {{ user.arabic_name }}</p> <p><strong>{{ _("Arabic Name") }}:</strong> {{ user_.arabic_name }}</p>
</div> </div>
<div class="col-md-6"> <div class="col-md-6">
<p><strong>{{ _("Phone Number") }}:</strong> {{ user.phone_number }}</p> <p><strong>{{ _("Phone Number") }}:</strong> {{ user_.phone_number }}</p>
<p><strong>{{ _("Address") }}:</strong> {{ user.address }}</p> <p><strong>{{ _("Address") }}:</strong> {{ user_.address }}</p>
<p><strong>{{ _("Role") }}:</strong> {{ user.dealer_type }}</p> <p><strong>{{ _("Role") }}:</strong> {{ user_.dealer_type }}</p>
</div> </div>
</div> </div>
</div> </div>
<div class="card-footer d-flex "> <div class="card-footer d-flex ">
<a class="btn btn-sm btn-primary me-1" href="{% url 'user_update' user.id %}"> <a class="btn btn-sm btn-primary me-1" href="{% url 'user_update' user_.id %}">
<!--<i class="bi bi-pencil-square"></i> --> <!--<i class="bi bi-pencil-square"></i> -->
{{ _("Edit") }} {{ _("Edit") }}
</a> </a>

View File

@ -10,8 +10,7 @@
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4"> <div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
<main class="d-grid gap-4 p-1"> <main class="d-grid gap-4 p-1">
<div class="row g-4"> <div class="row g-4">
<div class="col-lg-6 col-xl-12"> <div class="col-lg-6 col-xl-12">
<div class="container-fluid p-2"> <div class="container-fluid p-2">
<form method="get"> <form method="get">
<div class="input-group input-group-sm"> <div class="input-group input-group-sm">