add search to select fields, redirect logged in user to landingpage if they are authenticated
This commit is contained in:
parent
9eba42aa91
commit
2dfbb5fa9a
20
.vscode/launch.json
vendored
Normal file
20
.vscode/launch.json
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"name": "Python Debugger: Django",
|
||||||
|
"type": "debugpy",
|
||||||
|
"request": "launch",
|
||||||
|
"args": [
|
||||||
|
"runserver",
|
||||||
|
"0.0.0.0:8888"
|
||||||
|
],
|
||||||
|
"django": true,
|
||||||
|
"autoStartBrowser": false,
|
||||||
|
"program": "${workspaceFolder}/manage.py"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@ -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.109','10.10.1.120', 'localhost', '127.0.0.1', '192.168.1.135', '172.20.10.4']
|
||||||
|
|
||||||
# Application definition
|
# Application definition
|
||||||
|
|
||||||
@ -110,9 +110,9 @@ WSGI_APPLICATION = 'car_inventory.wsgi.application'
|
|||||||
DATABASES = {
|
DATABASES = {
|
||||||
"default": {
|
"default": {
|
||||||
"ENGINE": "django_prometheus.db.backends.postgresql",
|
"ENGINE": "django_prometheus.db.backends.postgresql",
|
||||||
"NAME": "secondhaikal",
|
"NAME": "haikal",
|
||||||
"USER": "f95166",
|
"USER": "haikal",
|
||||||
"PASSWORD": "Kfsh&rc9788",
|
"PASSWORD": "haikal",
|
||||||
"HOST": "localhost",
|
"HOST": "localhost",
|
||||||
"PORT": 5432,
|
"PORT": 5432,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,6 @@ admin.site.register(models.CarRegistration)
|
|||||||
admin.site.register(models.CustomCard)
|
admin.site.register(models.CustomCard)
|
||||||
admin.site.register(models.CarSpecificationValue)
|
admin.site.register(models.CarSpecificationValue)
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.CarMake)
|
@admin.register(models.CarMake)
|
||||||
class CarMakeAdmin(admin.ModelAdmin):
|
class CarMakeAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'arabic_name', 'is_sa_import')
|
list_display = ('name', 'arabic_name', 'is_sa_import')
|
||||||
@ -51,18 +50,18 @@ class CarSeriesAdmin(admin.ModelAdmin):
|
|||||||
verbose_name = "Car Series"
|
verbose_name = "Car Series"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.CarTrim)
|
# @admin.register(models.CarTrim)
|
||||||
class CarTrimAdmin(admin.ModelAdmin):
|
# class CarTrimAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name',
|
# list_display = ('name',
|
||||||
'id_car_serie__name',
|
# 'id_car_serie__name',
|
||||||
'id_car_serie__id_car_model__name',
|
# 'id_car_serie__id_car_model__name',
|
||||||
'id_car_serie__id_car_model__id_car_make__name')
|
# 'id_car_serie__id_car_model__id_car_make__name')
|
||||||
search_fields = ('name', 'arabic_name', 'id_car_serie__id_car_model__name')
|
# search_fields = ('name', 'arabic_name', 'id_car_serie__id_car_model__name')
|
||||||
list_filter = ('id_car_serie__id_car_model__id_car_make__is_sa_import',
|
# list_filter = ('id_car_serie__id_car_model__id_car_make__is_sa_import',
|
||||||
'id_car_serie__id_car_model__id_car_make__name')
|
# 'id_car_serie__id_car_model__id_car_make__name')
|
||||||
|
|
||||||
class Meta:
|
# class Meta:
|
||||||
verbose_name = "Car Trim"
|
# verbose_name = "Car Trim"
|
||||||
|
|
||||||
|
|
||||||
@admin.register(models.CarSpecification)
|
@admin.register(models.CarSpecification)
|
||||||
|
|||||||
37
inventory/management/commands/seed_customer.py
Normal file
37
inventory/management/commands/seed_customer.py
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from faker import Faker
|
||||||
|
from inventory.models import Customer, Dealer
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Seed the Customer model with 20 records'
|
||||||
|
|
||||||
|
def handle(self, *args, **kwargs):
|
||||||
|
fake = Faker()
|
||||||
|
dealers = Dealer.objects.all()
|
||||||
|
|
||||||
|
if not dealers.exists():
|
||||||
|
self.stdout.write(self.style.ERROR('No dealers found. Please create dealers first.'))
|
||||||
|
return
|
||||||
|
|
||||||
|
for _ in range(20):
|
||||||
|
dealer = fake.random_element(elements=dealers)
|
||||||
|
first_name = fake.first_name()
|
||||||
|
middle_name = fake.first_name() if fake.boolean() else ''
|
||||||
|
last_name = fake.last_name()
|
||||||
|
email = fake.unique.email()
|
||||||
|
national_id = fake.unique.bothify(text='##########')
|
||||||
|
phone_number = fake.unique.phone_number()
|
||||||
|
address = fake.address()
|
||||||
|
|
||||||
|
Customer.objects.create(
|
||||||
|
dealer=dealer,
|
||||||
|
first_name=first_name,
|
||||||
|
middle_name=middle_name,
|
||||||
|
last_name=last_name,
|
||||||
|
email=email,
|
||||||
|
national_id=national_id,
|
||||||
|
phone_number=phone_number,
|
||||||
|
address=address
|
||||||
|
)
|
||||||
|
|
||||||
|
self.stdout.write(self.style.SUCCESS('Successfully seeded 20 customers.'))
|
||||||
@ -14,7 +14,6 @@ from django_ledger.models import (
|
|||||||
UnitOfMeasureModel,
|
UnitOfMeasureModel,
|
||||||
CustomerModel,
|
CustomerModel,
|
||||||
ItemModelQuerySet,
|
ItemModelQuerySet,
|
||||||
|
|
||||||
)
|
)
|
||||||
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
|
||||||
@ -27,7 +26,7 @@ class CarMake(models.Model, LocalizedNameMixin):
|
|||||||
id_car_make = models.AutoField(primary_key=True)
|
id_car_make = models.AutoField(primary_key=True)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
arabic_name = models.CharField(max_length=255)
|
arabic_name = models.CharField(max_length=255)
|
||||||
logo = models.ImageField(_('logo'), upload_to='car_make', blank=True, null=True)
|
logo = models.ImageField(_("logo"), upload_to="car_make", blank=True, null=True)
|
||||||
is_sa_import = models.BooleanField(default=False)
|
is_sa_import = models.BooleanField(default=False)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
@ -39,7 +38,7 @@ class CarMake(models.Model, LocalizedNameMixin):
|
|||||||
|
|
||||||
class CarModel(models.Model, LocalizedNameMixin):
|
class CarModel(models.Model, LocalizedNameMixin):
|
||||||
id_car_model = models.AutoField(primary_key=True)
|
id_car_model = models.AutoField(primary_key=True)
|
||||||
id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column='id_car_make')
|
id_car_make = models.ForeignKey(CarMake, models.DO_NOTHING, db_column="id_car_make")
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
arabic_name = models.CharField(max_length=255)
|
arabic_name = models.CharField(max_length=255)
|
||||||
|
|
||||||
@ -52,7 +51,9 @@ class CarModel(models.Model, LocalizedNameMixin):
|
|||||||
|
|
||||||
class CarSerie(models.Model, LocalizedNameMixin):
|
class CarSerie(models.Model, LocalizedNameMixin):
|
||||||
id_car_serie = models.AutoField(primary_key=True)
|
id_car_serie = models.AutoField(primary_key=True)
|
||||||
id_car_model = models.ForeignKey(CarModel, models.DO_NOTHING, db_column='id_car_model')
|
id_car_model = models.ForeignKey(
|
||||||
|
CarModel, models.DO_NOTHING, db_column="id_car_model"
|
||||||
|
)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
arabic_name = models.CharField(max_length=255)
|
arabic_name = models.CharField(max_length=255)
|
||||||
|
|
||||||
@ -65,7 +66,9 @@ class CarSerie(models.Model, LocalizedNameMixin):
|
|||||||
|
|
||||||
class CarTrim(models.Model, LocalizedNameMixin):
|
class CarTrim(models.Model, LocalizedNameMixin):
|
||||||
id_car_trim = models.AutoField(primary_key=True)
|
id_car_trim = models.AutoField(primary_key=True)
|
||||||
id_car_serie = models.ForeignKey(CarSerie, models.DO_NOTHING, db_column='id_car_serie')
|
id_car_serie = models.ForeignKey(
|
||||||
|
CarSerie, models.DO_NOTHING, db_column="id_car_serie"
|
||||||
|
)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
arabic_name = models.CharField(max_length=255)
|
arabic_name = models.CharField(max_length=255)
|
||||||
start_production_year = models.IntegerField(blank=True, null=True)
|
start_production_year = models.IntegerField(blank=True, null=True)
|
||||||
@ -82,7 +85,9 @@ class CarSpecification(models.Model, LocalizedNameMixin):
|
|||||||
id_car_specification = models.AutoField(primary_key=True)
|
id_car_specification = models.AutoField(primary_key=True)
|
||||||
name = models.CharField(max_length=255)
|
name = models.CharField(max_length=255)
|
||||||
arabic_name = models.CharField(max_length=255)
|
arabic_name = models.CharField(max_length=255)
|
||||||
id_parent = models.ForeignKey('self', models.DO_NOTHING, db_column='id_parent', blank=True, null=True)
|
id_parent = models.ForeignKey(
|
||||||
|
"self", models.DO_NOTHING, db_column="id_parent", blank=True, null=True
|
||||||
|
)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.name
|
return self.name
|
||||||
@ -93,8 +98,10 @@ class CarSpecification(models.Model, LocalizedNameMixin):
|
|||||||
|
|
||||||
class CarSpecificationValue(models.Model):
|
class CarSpecificationValue(models.Model):
|
||||||
id_car_specification_value = models.AutoField(primary_key=True)
|
id_car_specification_value = models.AutoField(primary_key=True)
|
||||||
id_car_trim = models.ForeignKey(CarTrim, models.DO_NOTHING, db_column='id_car_trim')
|
id_car_trim = models.ForeignKey(CarTrim, models.DO_NOTHING, db_column="id_car_trim")
|
||||||
id_car_specification = models.ForeignKey(CarSpecification, models.DO_NOTHING, db_column='id_car_specification')
|
id_car_specification = models.ForeignKey(
|
||||||
|
CarSpecification, models.DO_NOTHING, db_column="id_car_specification"
|
||||||
|
)
|
||||||
value = models.CharField(max_length=500)
|
value = models.CharField(max_length=500)
|
||||||
unit = models.CharField(max_length=255, blank=True, null=True)
|
unit = models.CharField(max_length=255, blank=True, null=True)
|
||||||
|
|
||||||
@ -107,24 +114,21 @@ class CarSpecificationValue(models.Model):
|
|||||||
|
|
||||||
# Car Model
|
# Car Model
|
||||||
class CarStatusChoices(models.TextChoices):
|
class CarStatusChoices(models.TextChoices):
|
||||||
AVAILABLE = 'available', _('Available')
|
AVAILABLE = "available", _("Available")
|
||||||
SOLD = 'sold', _('Sold')
|
SOLD = "sold", _("Sold")
|
||||||
HOLD = 'hold', _('Hold')
|
HOLD = "hold", _("Hold")
|
||||||
DAMAGED = 'damaged', _('Damaged')
|
DAMAGED = "damaged", _("Damaged")
|
||||||
|
|
||||||
|
|
||||||
class CarStockTypeChoices(models.TextChoices):
|
class CarStockTypeChoices(models.TextChoices):
|
||||||
NEW = 'new', _('New')
|
NEW = "new", _("New")
|
||||||
USED = 'used', _('Used')
|
USED = "used", _("Used")
|
||||||
|
|
||||||
|
|
||||||
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(
|
||||||
"Dealer",
|
"Dealer", models.DO_NOTHING, related_name="cars", verbose_name=_("Dealer")
|
||||||
models.DO_NOTHING,
|
|
||||||
related_name='cars',
|
|
||||||
verbose_name=_("Dealer")
|
|
||||||
)
|
)
|
||||||
|
|
||||||
vendor = models.ForeignKey(
|
vendor = models.ForeignKey(
|
||||||
@ -132,53 +136,53 @@ class Car(models.Model):
|
|||||||
models.DO_NOTHING,
|
models.DO_NOTHING,
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
related_name='cars',
|
related_name="cars",
|
||||||
verbose_name=_("Vendor")
|
verbose_name=_("Vendor"),
|
||||||
)
|
)
|
||||||
id_car_make = models.ForeignKey(
|
id_car_make = models.ForeignKey(
|
||||||
CarMake,
|
CarMake,
|
||||||
models.DO_NOTHING,
|
models.DO_NOTHING,
|
||||||
db_column='id_car_make',
|
db_column="id_car_make",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_("Make")
|
verbose_name=_("Make"),
|
||||||
)
|
)
|
||||||
id_car_model = models.ForeignKey(
|
id_car_model = models.ForeignKey(
|
||||||
CarModel,
|
CarModel,
|
||||||
models.DO_NOTHING,
|
models.DO_NOTHING,
|
||||||
db_column='id_car_model',
|
db_column="id_car_model",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_("Model")
|
verbose_name=_("Model"),
|
||||||
)
|
)
|
||||||
year = models.IntegerField(verbose_name=_("Year"))
|
year = models.IntegerField(verbose_name=_("Year"))
|
||||||
id_car_serie = models.ForeignKey(
|
id_car_serie = models.ForeignKey(
|
||||||
CarSerie,
|
CarSerie,
|
||||||
models.DO_NOTHING,
|
models.DO_NOTHING,
|
||||||
db_column='id_car_serie',
|
db_column="id_car_serie",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_("Series")
|
verbose_name=_("Series"),
|
||||||
)
|
)
|
||||||
id_car_trim = models.ForeignKey(
|
id_car_trim = models.ForeignKey(
|
||||||
CarTrim,
|
CarTrim,
|
||||||
models.DO_NOTHING,
|
models.DO_NOTHING,
|
||||||
db_column='id_car_trim',
|
db_column="id_car_trim",
|
||||||
null=True,
|
null=True,
|
||||||
blank=True,
|
blank=True,
|
||||||
verbose_name=_("Trim")
|
verbose_name=_("Trim"),
|
||||||
)
|
)
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=10,
|
max_length=10,
|
||||||
choices=CarStatusChoices.choices,
|
choices=CarStatusChoices.choices,
|
||||||
default=CarStatusChoices.AVAILABLE,
|
default=CarStatusChoices.AVAILABLE,
|
||||||
verbose_name=_("Status")
|
verbose_name=_("Status"),
|
||||||
)
|
)
|
||||||
stock_type = models.CharField(
|
stock_type = models.CharField(
|
||||||
max_length=10,
|
max_length=10,
|
||||||
choices=CarStockTypeChoices.choices,
|
choices=CarStockTypeChoices.choices,
|
||||||
default=CarStockTypeChoices.NEW,
|
default=CarStockTypeChoices.NEW,
|
||||||
verbose_name=_("Stock Type")
|
verbose_name=_("Stock Type"),
|
||||||
)
|
)
|
||||||
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
||||||
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
|
||||||
@ -201,21 +205,23 @@ class Car(models.Model):
|
|||||||
@property
|
@property
|
||||||
def selling_price(self):
|
def selling_price(self):
|
||||||
finance = self.finances.first()
|
finance = self.finances.first()
|
||||||
return finance.selling_price if finance else Decimal('0.00')
|
return finance.selling_price if finance else Decimal("0.00")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def vat_amount(self):
|
def vat_amount(self):
|
||||||
finance = self.finances.first()
|
finance = self.finances.first()
|
||||||
return finance.vat_amount if finance else Decimal('0.00')
|
return finance.vat_amount if finance else Decimal("0.00")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total(self):
|
def total(self):
|
||||||
finance = self.finances.first()
|
finance = self.finances.first()
|
||||||
return finance.total if finance else Decimal('0.00')
|
return finance.total if finance else Decimal("0.00")
|
||||||
|
|
||||||
|
|
||||||
class CarReservation(models.Model):
|
class CarReservation(models.Model):
|
||||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations')
|
car = models.ForeignKey(
|
||||||
|
"Car", on_delete=models.CASCADE, related_name="reservations"
|
||||||
|
)
|
||||||
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE)
|
||||||
reserved_at = models.DateTimeField(auto_now_add=True)
|
reserved_at = models.DateTimeField(auto_now_add=True)
|
||||||
reserved_until = models.DateTimeField()
|
reserved_until = models.DateTimeField()
|
||||||
@ -224,19 +230,31 @@ class CarReservation(models.Model):
|
|||||||
return self.reserved_until > now()
|
return self.reserved_until > now()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
unique_together = ('car', 'reserved_until')
|
unique_together = ("car", "reserved_until")
|
||||||
ordering = ['-reserved_at']
|
ordering = ["-reserved_at"]
|
||||||
|
|
||||||
|
|
||||||
# Car Finance Model
|
# Car Finance Model
|
||||||
class CarFinance(models.Model):
|
class CarFinance(models.Model):
|
||||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='finances')
|
car = models.ForeignKey(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(
|
||||||
profit_margin = models.DecimalField(max_digits=10, decimal_places=2, verbose_name=_("Profit Margin"))
|
max_digits=14, decimal_places=2, verbose_name=_("Cost Price")
|
||||||
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"), editable=False)
|
)
|
||||||
vat_rate = models.DecimalField(max_digits=10, decimal_places=2, default=0.15, verbose_name=_("VAT Rate"))
|
profit_margin = models.DecimalField(
|
||||||
vat_amount = models.DecimalField(max_digits=12, decimal_places=2, verbose_name=_("VAT Amount"), editable=False)
|
max_digits=10, decimal_places=2, verbose_name=_("Profit Margin")
|
||||||
total = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Total Amount"), editable=False)
|
)
|
||||||
|
selling_price = models.DecimalField(
|
||||||
|
max_digits=14, decimal_places=2, verbose_name=_("Selling Price"), editable=False
|
||||||
|
)
|
||||||
|
vat_rate = models.DecimalField(
|
||||||
|
max_digits=10, decimal_places=2, default=0.15, verbose_name=_("VAT Rate")
|
||||||
|
)
|
||||||
|
vat_amount = models.DecimalField(
|
||||||
|
max_digits=12, decimal_places=2, verbose_name=_("VAT Amount"), editable=False
|
||||||
|
)
|
||||||
|
total = models.DecimalField(
|
||||||
|
max_digits=14, decimal_places=2, verbose_name=_("Total Amount"), editable=False
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Car Financial Details")
|
verbose_name = _("Car Financial Details")
|
||||||
@ -249,16 +267,18 @@ class CarFinance(models.Model):
|
|||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Car Financial Details for {self.car}: Selling Price {self.selling_price}"
|
return (
|
||||||
|
f"Car Financial Details for {self.car}: Selling Price {self.selling_price}"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# Colors Model
|
# Colors Model
|
||||||
class CarColors(models.Model):
|
class CarColors(models.Model):
|
||||||
class ColorType(models.TextChoices):
|
class ColorType(models.TextChoices):
|
||||||
EXTERIOR = 'exterior', _("Exterior")
|
EXTERIOR = "exterior", _("Exterior")
|
||||||
INTERIOR = 'interior', _("Interior")
|
INTERIOR = "interior", _("Interior")
|
||||||
|
|
||||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='colors')
|
car = models.ForeignKey("Car", on_delete=models.CASCADE, related_name="colors")
|
||||||
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"))
|
||||||
rgb = models.CharField(max_length=24, blank=True, null=True, verbose_name=_("RGB"))
|
rgb = models.CharField(max_length=24, blank=True, null=True, verbose_name=_("RGB"))
|
||||||
@ -266,7 +286,7 @@ class CarColors(models.Model):
|
|||||||
max_length=10,
|
max_length=10,
|
||||||
choices=ColorType.choices,
|
choices=ColorType.choices,
|
||||||
default=ColorType.EXTERIOR,
|
default=ColorType.EXTERIOR,
|
||||||
verbose_name=_("Color Type")
|
verbose_name=_("Color Type"),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -279,7 +299,12 @@ class CarColors(models.Model):
|
|||||||
|
|
||||||
# Custom Card Model
|
# Custom Card Model
|
||||||
class CustomCard(models.Model):
|
class CustomCard(models.Model):
|
||||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='custom_cards', verbose_name=_("Car"))
|
car = models.ForeignKey(
|
||||||
|
Car,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="custom_cards",
|
||||||
|
verbose_name=_("Car"),
|
||||||
|
)
|
||||||
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
|
custom_number = models.CharField(max_length=255, verbose_name=_("Custom Number"))
|
||||||
custom_date = models.DateField(verbose_name=_("Custom Date"))
|
custom_date = models.DateField(verbose_name=_("Custom Date"))
|
||||||
|
|
||||||
@ -293,7 +318,12 @@ class CustomCard(models.Model):
|
|||||||
|
|
||||||
# Car Registration Model
|
# Car Registration Model
|
||||||
class CarRegistration(models.Model):
|
class CarRegistration(models.Model):
|
||||||
car = models.ForeignKey(Car, on_delete=models.CASCADE, related_name='registrations', verbose_name=_("Car"))
|
car = models.ForeignKey(
|
||||||
|
Car,
|
||||||
|
on_delete=models.CASCADE,
|
||||||
|
related_name="registrations",
|
||||||
|
verbose_name=_("Car"),
|
||||||
|
)
|
||||||
plate_number = models.IntegerField(verbose_name=_("Plate Number"))
|
plate_number = models.IntegerField(verbose_name=_("Plate Number"))
|
||||||
text1 = models.CharField(max_length=1, verbose_name=_("Text 1"))
|
text1 = models.CharField(max_length=1, verbose_name=_("Text 1"))
|
||||||
text2 = models.CharField(max_length=1, verbose_name=_("Text 2"))
|
text2 = models.CharField(max_length=1, verbose_name=_("Text 2"))
|
||||||
@ -319,14 +349,20 @@ class TimestampedModel(models.Model):
|
|||||||
|
|
||||||
# Dealer Model
|
# Dealer Model
|
||||||
class Dealer(models.Model, LocalizedNameMixin):
|
class Dealer(models.Model, LocalizedNameMixin):
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='dealer')
|
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name="dealer")
|
||||||
crn = models.CharField(max_length=10, verbose_name=_("Commercial Registration Number"))
|
crn = models.CharField(
|
||||||
|
max_length=10, verbose_name=_("Commercial Registration Number")
|
||||||
|
)
|
||||||
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
|
vrn = models.CharField(max_length=15, verbose_name=_("VAT Registration Number"))
|
||||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||||
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
|
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
|
||||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
address = models.CharField(
|
||||||
logo = models.ImageField(upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo"))
|
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||||
|
)
|
||||||
|
logo = models.ImageField(
|
||||||
|
upload_to="logos/users", blank=True, null=True, verbose_name=_("Logo")
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Dealer")
|
verbose_name = _("Dealer")
|
||||||
@ -338,14 +374,20 @@ class Dealer(models.Model, LocalizedNameMixin):
|
|||||||
|
|
||||||
# Vendor Model
|
# Vendor Model
|
||||||
class Vendor(models.Model, LocalizedNameMixin):
|
class Vendor(models.Model, LocalizedNameMixin):
|
||||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='vendors')
|
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="vendors")
|
||||||
crn = models.CharField(max_length=10, unique=True, verbose_name=_("Commercial Registration Number"))
|
crn = models.CharField(
|
||||||
vrn = models.CharField(max_length=15, unique=True, verbose_name=_("VAT Registration Number"))
|
max_length=10, unique=True, verbose_name=_("Commercial Registration Number")
|
||||||
|
)
|
||||||
|
vrn = models.CharField(
|
||||||
|
max_length=15, unique=True, verbose_name=_("VAT Registration Number")
|
||||||
|
)
|
||||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||||
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
name = models.CharField(max_length=255, verbose_name=_("English Name"))
|
||||||
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person"))
|
contact_person = models.CharField(max_length=100, verbose_name=_("Contact Person"))
|
||||||
phone_number = PhoneNumberField(region='SA', verbose_name=_("Phone Number"))
|
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
|
||||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
address = models.CharField(
|
||||||
|
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||||
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Vendor")
|
verbose_name = _("Vendor")
|
||||||
@ -357,14 +399,24 @@ class Vendor(models.Model, LocalizedNameMixin):
|
|||||||
|
|
||||||
# Customer Model
|
# Customer Model
|
||||||
class Customer(models.Model):
|
class Customer(models.Model):
|
||||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='customers')
|
dealer = models.ForeignKey(
|
||||||
|
Dealer, on_delete=models.CASCADE, related_name="customers"
|
||||||
|
)
|
||||||
first_name = models.CharField(max_length=50, verbose_name=_("First Name"))
|
first_name = models.CharField(max_length=50, verbose_name=_("First Name"))
|
||||||
middle_name = models.CharField(max_length=50, blank=True, null=True, verbose_name=_("Middle Name"))
|
middle_name = models.CharField(
|
||||||
|
max_length=50, blank=True, null=True, verbose_name=_("Middle Name")
|
||||||
|
)
|
||||||
last_name = models.CharField(max_length=50, verbose_name=_("Last Name"))
|
last_name = models.CharField(max_length=50, verbose_name=_("Last Name"))
|
||||||
email = models.EmailField(unique=True, verbose_name=_("Email"))
|
email = models.EmailField(unique=True, verbose_name=_("Email"))
|
||||||
national_id = models.CharField(max_length=10, unique=True, verbose_name=_("National ID"))
|
national_id = models.CharField(
|
||||||
phone_number = PhoneNumberField(region='SA', unique=True, verbose_name=_("Phone Number"))
|
max_length=10, unique=True, verbose_name=_("National ID")
|
||||||
address = models.CharField(max_length=200, blank=True, null=True, verbose_name=_("Address"))
|
)
|
||||||
|
phone_number = PhoneNumberField(
|
||||||
|
region="SA", unique=True, verbose_name=_("Phone Number")
|
||||||
|
)
|
||||||
|
address = models.CharField(
|
||||||
|
max_length=200, blank=True, null=True, verbose_name=_("Address")
|
||||||
|
)
|
||||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
created = models.DateTimeField(auto_now_add=True, verbose_name=_("Created"))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
@ -372,10 +424,9 @@ class Customer(models.Model):
|
|||||||
verbose_name_plural = _("Customers")
|
verbose_name_plural = _("Customers")
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
middle = f" {self.middle_name}" if self.middle_name else ''
|
middle = f" {self.middle_name}" if self.middle_name else ""
|
||||||
return f"{self.first_name}{middle} {self.last_name}"
|
return f"{self.first_name}{middle} {self.last_name}"
|
||||||
|
|
||||||
|
|
||||||
# Create Entity
|
# Create Entity
|
||||||
@receiver(post_save, sender=Dealer)
|
@receiver(post_save, sender=Dealer)
|
||||||
def create_ledger_entity(sender, instance, created, **kwargs):
|
def create_ledger_entity(sender, instance, created, **kwargs):
|
||||||
|
|||||||
@ -25,7 +25,6 @@ def decode_vin_pyvin(vin):
|
|||||||
print(data)
|
print(data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
# vehicle-info
|
# vehicle-info
|
||||||
# c2729afb
|
# c2729afb
|
||||||
# 6d397471920412d672af1b8a02ca52ea
|
# 6d397471920412d672af1b8a02ca52ea
|
||||||
|
|||||||
@ -62,7 +62,4 @@ urlpatterns = [
|
|||||||
path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'),
|
path('cars/reserve/<int:car_id>/', views.reserve_car_view, name='reserve_car'),
|
||||||
path('reservations/<int:reservation_id>/', views.manage_reservation, name='reservations'),
|
path('reservations/<int:reservation_id>/', views.manage_reservation, name='reservations'),
|
||||||
path('cars/<int:car_pk>/add-custom-card/', views.CustomCardCreateView.as_view(), name='add_custom_card'),
|
path('cars/<int:car_pk>/add-custom-card/', views.CustomCardCreateView.as_view(), name='add_custom_card'),
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ from django.views.generic import (
|
|||||||
CreateView,
|
CreateView,
|
||||||
UpdateView,
|
UpdateView,
|
||||||
DeleteView,
|
DeleteView,
|
||||||
TemplateView
|
TemplateView,
|
||||||
)
|
)
|
||||||
from django.utils import timezone, translation
|
from django.utils import timezone, translation
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
@ -34,17 +34,24 @@ logging.basicConfig(level=logging.INFO)
|
|||||||
|
|
||||||
|
|
||||||
def switch_language(request):
|
def switch_language(request):
|
||||||
language = request.GET.get('language', 'en')
|
language = request.GET.get("language", "en")
|
||||||
referer = request.META.get('HTTP_REFERER', '/')
|
referer = request.META.get("HTTP_REFERER", "/")
|
||||||
parsed_url = urlparse(referer)
|
parsed_url = urlparse(referer)
|
||||||
path_parts = parsed_url.path.split('/')
|
path_parts = parsed_url.path.split("/")
|
||||||
|
|
||||||
if path_parts[1] in dict(settings.LANGUAGES):
|
if path_parts[1] in dict(settings.LANGUAGES):
|
||||||
path_parts.pop(1)
|
path_parts.pop(1)
|
||||||
|
|
||||||
new_path = '/'.join(path_parts)
|
new_path = "/".join(path_parts)
|
||||||
new_url = urlunparse(
|
new_url = urlunparse(
|
||||||
(parsed_url.scheme, parsed_url.netloc, new_path, parsed_url.params, parsed_url.query, parsed_url.fragment)
|
(
|
||||||
|
parsed_url.scheme,
|
||||||
|
parsed_url.netloc,
|
||||||
|
new_path,
|
||||||
|
parsed_url.params,
|
||||||
|
parsed_url.query,
|
||||||
|
parsed_url.fragment,
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if language in dict(settings.LANGUAGES):
|
if language in dict(settings.LANGUAGES):
|
||||||
@ -53,89 +60,96 @@ def switch_language(request):
|
|||||||
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language)
|
response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language)
|
||||||
translation.activate(language)
|
translation.activate(language)
|
||||||
request.session[settings.LANGUAGE_COOKIE_NAME] = language
|
request.session[settings.LANGUAGE_COOKIE_NAME] = language
|
||||||
logger.debug(f"Language switched to: {language}, Session: {request.session[settings.LANGUAGE_COOKIE_NAME]}")
|
logger.debug(
|
||||||
|
f"Language switched to: {language}, Session: {request.session[settings.LANGUAGE_COOKIE_NAME]}"
|
||||||
|
)
|
||||||
return response
|
return response
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Invalid language code: {language}")
|
logger.warning(f"Invalid language code: {language}")
|
||||||
return redirect('/')
|
return redirect("/")
|
||||||
|
|
||||||
|
|
||||||
class HomeView(LoginRequiredMixin, TemplateView):
|
class HomeView(LoginRequiredMixin, TemplateView):
|
||||||
template_name = 'index.html'
|
template_name = "index.html"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
if not hasattr(request.user, 'dealer') or not request.user.is_authenticated:
|
if not hasattr(request.user, "dealer") or not request.user.is_authenticated:
|
||||||
messages.error(request, _('You are not associated with any dealer.'))
|
messages.error(request, _("You are not associated with any dealer."))
|
||||||
return redirect('welcome')
|
return redirect("welcome")
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
|
|
||||||
total_cars = models.Car.objects.count()
|
total_cars = models.Car.objects.count()
|
||||||
total_reservations = models.CarReservation.objects.filter(reserved_until__gte=timezone.now()).count()
|
total_reservations = models.CarReservation.objects.filter(
|
||||||
|
reserved_until__gte=timezone.now()
|
||||||
|
).count()
|
||||||
stats = models.CarFinance.objects.aggregate(
|
stats = models.CarFinance.objects.aggregate(
|
||||||
total_cost_price=Sum('cost_price'),
|
total_cost_price=Sum("cost_price"),
|
||||||
total_selling_price=Sum('selling_price'),
|
total_selling_price=Sum("selling_price"),
|
||||||
)
|
)
|
||||||
total_cost_price = stats['total_cost_price'] or 0
|
total_cost_price = stats["total_cost_price"] or 0
|
||||||
total_selling_price = stats['total_selling_price'] or 0
|
total_selling_price = stats["total_selling_price"] or 0
|
||||||
total_profit = total_selling_price - total_cost_price
|
total_profit = total_selling_price - total_cost_price
|
||||||
|
|
||||||
context['total_cars'] = total_cars
|
context["total_cars"] = total_cars
|
||||||
context['total_reservations'] = total_reservations
|
context["total_reservations"] = total_reservations
|
||||||
context['total_cost_price'] = total_cost_price
|
context["total_cost_price"] = total_cost_price
|
||||||
context['total_selling_price'] = total_selling_price
|
context["total_selling_price"] = total_selling_price
|
||||||
context['total_profit'] = total_profit
|
context["total_profit"] = total_profit
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class WelcomeView(TemplateView):
|
class WelcomeView(TemplateView):
|
||||||
template_name = "welcome.html"
|
template_name = "welcome.html"
|
||||||
|
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if hasattr(request.user, "dealer") and request.user.is_authenticated:
|
||||||
|
return redirect("landing_page")
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CarCreateView(LoginRequiredMixin, CreateView):
|
class CarCreateView(LoginRequiredMixin, CreateView):
|
||||||
model = models.Car
|
model = models.Car
|
||||||
form_class = forms.CarForm
|
form_class = forms.CarForm
|
||||||
template_name = 'inventory/car_form.html'
|
template_name = "inventory/car_form.html"
|
||||||
success_url = reverse_lazy('inventory_stats')
|
success_url = reverse_lazy("inventory_stats")
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.dealer = self.request.user.dealer
|
form.instance.dealer = self.request.user.dealer
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, 'Car saved successfully.')
|
messages.success(self.request, "Car saved successfully.")
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class AjaxHandlerView(LoginRequiredMixin, View):
|
class AjaxHandlerView(LoginRequiredMixin, View):
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
action = request.GET.get('action')
|
action = request.GET.get("action")
|
||||||
handlers = {
|
handlers = {
|
||||||
'decode_vin': self.decode_vin,
|
"decode_vin": self.decode_vin,
|
||||||
'get_models': self.get_models,
|
"get_models": self.get_models,
|
||||||
'get_series': self.get_series,
|
"get_series": self.get_series,
|
||||||
'get_trims': self.get_trims,
|
"get_trims": self.get_trims,
|
||||||
'get_specifications': self.get_specifications,
|
"get_specifications": self.get_specifications,
|
||||||
}
|
}
|
||||||
handler = handlers.get(action)
|
handler = handlers.get(action)
|
||||||
if handler:
|
if handler:
|
||||||
return handler(request)
|
return handler(request)
|
||||||
else:
|
else:
|
||||||
return JsonResponse({'error': 'Invalid action'}, status=400)
|
return JsonResponse({"error": "Invalid action"}, status=400)
|
||||||
|
|
||||||
def decode_vin(self, request):
|
def decode_vin(self, request):
|
||||||
vin_no = request.GET.get('vin_no')
|
vin_no = request.GET.get("vin_no")
|
||||||
if not vin_no or len(vin_no.strip()) != 17:
|
if not vin_no or len(vin_no.strip()) != 17:
|
||||||
return JsonResponse({'success': False, 'error': 'Invalid VIN number provided.'}, status=400)
|
return JsonResponse(
|
||||||
|
{"success": False, "error": "Invalid VIN number provided."}, status=400
|
||||||
|
)
|
||||||
vin_no = vin_no.strip()
|
vin_no = vin_no.strip()
|
||||||
vin_data = {}
|
vin_data = {}
|
||||||
decoding_method = ''
|
decoding_method = ""
|
||||||
|
|
||||||
decoding_methods = [
|
decoding_methods = [("PYVIN", decode_vin_pyvin), ("VIN", VIN), ("ELM", elm)]
|
||||||
('PYVIN', decode_vin_pyvin),
|
|
||||||
('VIN', VIN),
|
|
||||||
('ELM', elm)
|
|
||||||
]
|
|
||||||
|
|
||||||
manufacturer_name = model_name_before = model_name = year_model = None
|
manufacturer_name = model_name_before = model_name = year_model = None
|
||||||
|
|
||||||
@ -143,25 +157,33 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
try:
|
try:
|
||||||
vin_info = decode_function(vin_no)
|
vin_info = decode_function(vin_no)
|
||||||
if vin_info:
|
if vin_info:
|
||||||
if method_name == 'PYVIN':
|
if method_name == "PYVIN":
|
||||||
manufacturer_name = vin_info.Make.strip()
|
manufacturer_name = vin_info.Make.strip()
|
||||||
model_name_before = vin_info.Model.strip()
|
model_name_before = vin_info.Model.strip()
|
||||||
year_model = vin_info.ModelYear
|
year_model = vin_info.ModelYear
|
||||||
if not manufacturer_name or not year_model:
|
if not manufacturer_name or not year_model:
|
||||||
raise ValueError('PYVIN returned incomplete data.')
|
raise ValueError("PYVIN returned incomplete data.")
|
||||||
elif method_name == 'VIN':
|
elif method_name == "VIN":
|
||||||
manufacturer_name = vin_info.make.strip()
|
manufacturer_name = vin_info.make.strip()
|
||||||
model_name_before = vin_info.model.strip()
|
model_name_before = vin_info.model.strip()
|
||||||
year_model = vin_info.model_year
|
year_model = vin_info.model_year
|
||||||
if not manufacturer_name or not model_name_before or not year_model:
|
if (
|
||||||
raise ValueError('VIN returned incomplete data.')
|
not manufacturer_name
|
||||||
elif method_name == 'ELM':
|
or not model_name_before
|
||||||
elm_data = vin_info.get('data', {})
|
or not year_model
|
||||||
manufacturer_name = elm_data.get('maker', '').strip()
|
):
|
||||||
model_name_before = elm_data.get('model', '').strip()
|
raise ValueError("VIN returned incomplete data.")
|
||||||
year_model = elm_data.get('modelYear', '').strip()
|
elif method_name == "ELM":
|
||||||
if not manufacturer_name or not model_name_before or not year_model:
|
elm_data = vin_info.get("data", {})
|
||||||
raise ValueError('ELM returned incomplete data.')
|
manufacturer_name = elm_data.get("maker", "").strip()
|
||||||
|
model_name_before = elm_data.get("model", "").strip()
|
||||||
|
year_model = elm_data.get("modelYear", "").strip()
|
||||||
|
if (
|
||||||
|
not manufacturer_name
|
||||||
|
or not model_name_before
|
||||||
|
or not year_model
|
||||||
|
):
|
||||||
|
raise ValueError("ELM returned incomplete data.")
|
||||||
model_name = normalize_name(model_name_before)
|
model_name = normalize_name(model_name_before)
|
||||||
decoding_method = method_name
|
decoding_method = method_name
|
||||||
print(f"decoded by {method_name}")
|
print(f"decoded by {method_name}")
|
||||||
@ -169,75 +191,101 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
else:
|
else:
|
||||||
logger.warning(f"{method_name} returned no data for {vin_no}.")
|
logger.warning(f"{method_name} returned no data for {vin_no}.")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.warning(f"VIN decoding with {method_name} failed for {vin_no}: {e}")
|
logger.warning(
|
||||||
|
f"VIN decoding with {method_name} failed for {vin_no}: {e}"
|
||||||
|
)
|
||||||
|
|
||||||
if not manufacturer_name or not model_name or not year_model:
|
if not manufacturer_name or not model_name or not year_model:
|
||||||
return JsonResponse({'success': False, 'error': 'VIN not found in all sources.'}, status=404)
|
return JsonResponse(
|
||||||
|
{"success": False, "error": "VIN not found in all sources."}, status=404
|
||||||
|
)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
f"VIN decoded using {decoding_method}: Make={manufacturer_name}, Model={model_name}, Year={year_model}"
|
f"VIN decoded using {decoding_method}: Make={manufacturer_name}, Model={model_name}, Year={year_model}"
|
||||||
)
|
)
|
||||||
|
|
||||||
car_make = models.CarMake.objects.filter(name__icontains=manufacturer_name).first()
|
car_make = models.CarMake.objects.filter(
|
||||||
|
name__icontains=manufacturer_name
|
||||||
|
).first()
|
||||||
if not car_make:
|
if not car_make:
|
||||||
return JsonResponse({'success': False, 'error': 'Manufacturer not found in the database.'}, status=404)
|
return JsonResponse(
|
||||||
vin_data['make_id'] = car_make.id_car_make
|
{"success": False, "error": "Manufacturer not found in the database."},
|
||||||
vin_data['name'] = car_make.name
|
status=404,
|
||||||
vin_data['arabic_name'] = car_make.arabic_name
|
)
|
||||||
|
vin_data["make_id"] = car_make.id_car_make
|
||||||
|
vin_data["name"] = car_make.name
|
||||||
|
vin_data["arabic_name"] = car_make.arabic_name
|
||||||
|
|
||||||
car_model = models.CarModel.objects.filter(id_car_make=car_make.id_car_make, name__icontains=model_name).first()
|
car_model = models.CarModel.objects.filter(
|
||||||
|
id_car_make=car_make.id_car_make, name__icontains=model_name
|
||||||
|
).first()
|
||||||
|
|
||||||
if not car_model:
|
if not car_model:
|
||||||
return JsonResponse({'success': False, 'error': 'Model not found for the given manufacturer.'}, status=404)
|
return JsonResponse(
|
||||||
|
{
|
||||||
|
"success": False,
|
||||||
|
"error": "Model not found for the given manufacturer.",
|
||||||
|
},
|
||||||
|
status=404,
|
||||||
|
)
|
||||||
|
|
||||||
vin_data['model_id'] = car_model.id_car_model
|
vin_data["model_id"] = car_model.id_car_model
|
||||||
vin_data['year'] = year_model
|
vin_data["year"] = year_model
|
||||||
return JsonResponse({'success': True, 'data': vin_data})
|
return JsonResponse({"success": True, "data": vin_data})
|
||||||
|
|
||||||
def get_models(self, request):
|
def get_models(self, request):
|
||||||
make_id = request.GET.get('make_id')
|
make_id = request.GET.get("make_id")
|
||||||
car_models = models.CarModel.objects.filter(id_car_make=make_id).values('id_car_model', 'name', 'arabic_name')
|
car_models = models.CarModel.objects.filter(id_car_make=make_id).values(
|
||||||
|
"id_car_model", "name", "arabic_name"
|
||||||
|
)
|
||||||
return JsonResponse(list(car_models), safe=False)
|
return JsonResponse(list(car_models), safe=False)
|
||||||
|
|
||||||
def get_series(self, request):
|
def get_series(self, request):
|
||||||
model_id = request.GET.get('model_id')
|
model_id = request.GET.get("model_id")
|
||||||
series = models.CarSerie.objects.filter(
|
series = models.CarSerie.objects.filter(
|
||||||
id_car_model=model_id,
|
id_car_model=model_id,
|
||||||
).values('id_car_serie', 'name', 'arabic_name')
|
).values("id_car_serie", "name", "arabic_name")
|
||||||
return JsonResponse(list(series), safe=False)
|
return JsonResponse(list(series), safe=False)
|
||||||
|
|
||||||
def get_trims(self, request):
|
def get_trims(self, request):
|
||||||
serie_id = request.GET.get('serie_id')
|
serie_id = request.GET.get("serie_id")
|
||||||
trims = models.CarTrim.objects.filter(
|
trims = models.CarTrim.objects.filter(id_car_serie=serie_id).values(
|
||||||
id_car_serie=serie_id
|
"id_car_trim", "name", "arabic_name"
|
||||||
).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):
|
||||||
trim_id = request.GET.get('trim_id')
|
trim_id = request.GET.get("trim_id")
|
||||||
car_spec_values = models.CarSpecificationValue.objects.filter(id_car_trim=trim_id)
|
car_spec_values = models.CarSpecificationValue.objects.filter(
|
||||||
|
id_car_trim=trim_id
|
||||||
|
)
|
||||||
lang = translation.get_language()
|
lang = translation.get_language()
|
||||||
specs_by_parent = {}
|
specs_by_parent = {}
|
||||||
for value in car_spec_values:
|
for value in car_spec_values:
|
||||||
specification = value.id_car_specification
|
specification = value.id_car_specification
|
||||||
parent = specification.id_parent
|
parent = specification.id_parent
|
||||||
parent_id = parent.id_car_specification if parent else 0
|
parent_id = parent.id_car_specification if parent else 0
|
||||||
if lang == 'ar':
|
if lang == "ar":
|
||||||
parent_name = parent.arabic_name if parent else "Root"
|
parent_name = parent.arabic_name if parent else "Root"
|
||||||
else:
|
else:
|
||||||
parent_name = parent.name if parent else "Root"
|
parent_name = parent.name if parent else "Root"
|
||||||
if parent_id not in specs_by_parent:
|
if parent_id not in specs_by_parent:
|
||||||
specs_by_parent[parent_id] = {'parent_name': parent_name, 'specifications': []}
|
specs_by_parent[parent_id] = {
|
||||||
|
"parent_name": parent_name,
|
||||||
|
"specifications": [],
|
||||||
|
}
|
||||||
spec_data = {
|
spec_data = {
|
||||||
'specification_id': specification.id_car_specification,
|
"specification_id": specification.id_car_specification,
|
||||||
's_name': specification.arabic_name if lang == 'ar' else specification.name,
|
"s_name": specification.arabic_name
|
||||||
's_value': value.value,
|
if lang == "ar"
|
||||||
's_unit': value.unit if value.unit else "",
|
else specification.name,
|
||||||
'trim_name': value.id_car_trim.name
|
"s_value": value.value,
|
||||||
|
"s_unit": value.unit if value.unit else "",
|
||||||
|
"trim_name": value.id_car_trim.name,
|
||||||
}
|
}
|
||||||
specs_by_parent[parent_id]['specifications'].append(spec_data)
|
specs_by_parent[parent_id]["specifications"].append(spec_data)
|
||||||
serialized_specs = [
|
serialized_specs = [
|
||||||
{'parent_name': v['parent_name'], 'specifications': v['specifications']}
|
{"parent_name": v["parent_name"], "specifications": v["specifications"]}
|
||||||
for v in specs_by_parent.values()
|
for v in specs_by_parent.values()
|
||||||
]
|
]
|
||||||
return JsonResponse(serialized_specs, safe=False)
|
return JsonResponse(serialized_specs, safe=False)
|
||||||
@ -245,22 +293,23 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
class CarInventory(LoginRequiredMixin, ListView):
|
class CarInventory(LoginRequiredMixin, ListView):
|
||||||
model = models.Car
|
model = models.Car
|
||||||
home_label = _('inventory')
|
home_label = _("inventory")
|
||||||
template_name = 'inventory/car_inventory.html'
|
template_name = "inventory/car_inventory.html"
|
||||||
context_object_name = 'cars'
|
context_object_name = "cars"
|
||||||
paginate_by = 10
|
paginate_by = 10
|
||||||
ordering = ['receiving_date']
|
ordering = ["receiving_date"]
|
||||||
|
|
||||||
def get_queryset(self, *args, **kwargs):
|
def get_queryset(self, *args, **kwargs):
|
||||||
query = self.request.GET.get('q')
|
query = self.request.GET.get("q")
|
||||||
make_id = self.kwargs['make_id']
|
make_id = self.kwargs["make_id"]
|
||||||
model_id = self.kwargs['model_id']
|
model_id = self.kwargs["model_id"]
|
||||||
trim_id = self.kwargs['trim_id']
|
trim_id = self.kwargs["trim_id"]
|
||||||
cars = models.Car.objects.filter(
|
cars = models.Car.objects.filter(
|
||||||
dealer__user=self.request.user,
|
dealer__user=self.request.user,
|
||||||
id_car_make=make_id,
|
id_car_make=make_id,
|
||||||
id_car_model=model_id,
|
id_car_model=model_id,
|
||||||
id_car_trim=trim_id,).order_by('receiving_date')
|
id_car_trim=trim_id,
|
||||||
|
).order_by("receiving_date")
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
cars = cars.filter(Q(vin__icontains=query))
|
cars = cars.filter(Q(vin__icontains=query))
|
||||||
@ -268,10 +317,10 @@ class CarInventory(LoginRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['query'] = self.request.GET.get('q', '')
|
context["query"] = self.request.GET.get("q", "")
|
||||||
context['make_id'] = self.kwargs['make_id']
|
context["make_id"] = self.kwargs["make_id"]
|
||||||
context['model_id'] = self.kwargs['model_id']
|
context["model_id"] = self.kwargs["model_id"]
|
||||||
context['trim_id'] = self.kwargs['trim_id']
|
context["trim_id"] = self.kwargs["trim_id"]
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -282,11 +331,11 @@ def inventory_stats_view(request):
|
|||||||
# Annotate total cars by make, model, and trim
|
# Annotate total cars by make, model, and trim
|
||||||
cars = (
|
cars = (
|
||||||
models.Car.objects.filter(dealer=dealer)
|
models.Car.objects.filter(dealer=dealer)
|
||||||
.select_related('id_car_make', 'id_car_model', 'id_car_trim')
|
.select_related("id_car_make", "id_car_model", "id_car_trim")
|
||||||
.annotate(
|
.annotate(
|
||||||
make_total=Count('id_car_make'),
|
make_total=Count("id_car_make"),
|
||||||
model_total=Count('id_car_model'),
|
model_total=Count("id_car_model"),
|
||||||
trim_total=Count('id_car_trim')
|
trim_total=Count("id_car_trim"),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -295,123 +344,128 @@ def inventory_stats_view(request):
|
|||||||
for car in cars:
|
for car in cars:
|
||||||
# Make Level
|
# Make Level
|
||||||
make = car.id_car_make
|
make = car.id_car_make
|
||||||
|
|
||||||
if make.id_car_make not in inventory:
|
if make.id_car_make not in inventory:
|
||||||
inventory[make.id_car_make] = {
|
inventory[make.id_car_make] = {
|
||||||
'make_id': make.id_car_make,
|
"make_id": make.id_car_make,
|
||||||
'make_name': make.get_local_name(),
|
"make_name": make.get_local_name(),
|
||||||
'total_cars': 0,
|
"total_cars": 0,
|
||||||
'models': {}
|
"models": {},
|
||||||
}
|
}
|
||||||
inventory[make.id_car_make]['total_cars'] += 1
|
inventory[make.id_car_make]["total_cars"] += 1
|
||||||
|
|
||||||
# Model Level
|
# Model Level
|
||||||
model = car.id_car_model
|
model = car.id_car_model
|
||||||
if model and model.id_car_model not in inventory[make.id_car_make]['models']:
|
if model and model.id_car_model not in inventory[make.id_car_make]["models"]:
|
||||||
inventory[make.id_car_make]['models'][model.id_car_model] = {
|
inventory[make.id_car_make]["models"][model.id_car_model] = {
|
||||||
'model_id': model.id_car_model,
|
"model_id": model.id_car_model,
|
||||||
'model_name': model.get_local_name(),
|
"model_name": model.get_local_name(),
|
||||||
'total_cars': 0,
|
"total_cars": 0,
|
||||||
'trims': {}
|
"trims": {},
|
||||||
}
|
}
|
||||||
inventory[make.id_car_make]['models'][model.id_car_model]['total_cars'] += 1
|
inventory[make.id_car_make]["models"][model.id_car_model]["total_cars"] += 1
|
||||||
|
|
||||||
# Trim Level
|
# Trim Level
|
||||||
trim = car.id_car_trim
|
trim = car.id_car_trim
|
||||||
if trim and trim.id_car_trim not in inventory[make.id_car_make]['models'][model.id_car_model]['trims']:
|
if (
|
||||||
inventory[make.id_car_make]['models'][model.id_car_model]['trims'][trim.id_car_trim] = {
|
trim
|
||||||
'trim_id': trim.id_car_trim,
|
and trim.id_car_trim
|
||||||
'trim_name': trim.name,
|
not in inventory[make.id_car_make]["models"][model.id_car_model]["trims"]
|
||||||
'total_cars': 0
|
):
|
||||||
}
|
inventory[make.id_car_make]["models"][model.id_car_model]["trims"][
|
||||||
inventory[make.id_car_make]['models'][model.id_car_model]['trims'][trim.id_car_trim]['total_cars'] += 1
|
trim.id_car_trim
|
||||||
|
] = {"trim_id": trim.id_car_trim, "trim_name": trim.name, "total_cars": 0}
|
||||||
|
inventory[make.id_car_make]["models"][model.id_car_model]["trims"][
|
||||||
|
trim.id_car_trim
|
||||||
|
]["total_cars"] += 1
|
||||||
|
|
||||||
# Convert to a list for easier template rendering
|
# Convert to a list for easier template rendering
|
||||||
result = {
|
result = {
|
||||||
'total_cars': cars.count(),
|
"total_cars": cars.count(),
|
||||||
'makes': [
|
"makes": [
|
||||||
{
|
{
|
||||||
'make_id': make_data['make_id'],
|
"make_id": make_data["make_id"],
|
||||||
'make_name': make_data['make_name'],
|
"make_name": make_data["make_name"],
|
||||||
'total_cars': make_data['total_cars'],
|
"total_cars": make_data["total_cars"],
|
||||||
'models': [
|
"models": [
|
||||||
{
|
{
|
||||||
'model_id': model_data['model_id'],
|
"model_id": model_data["model_id"],
|
||||||
'model_name': model_data['model_name'],
|
"model_name": model_data["model_name"],
|
||||||
'total_cars': model_data['total_cars'],
|
"total_cars": model_data["total_cars"],
|
||||||
'trims': list(model_data['trims'].values())
|
"trims": list(model_data["trims"].values()),
|
||||||
}
|
}
|
||||||
for model_data in make_data['models'].values()
|
for model_data in make_data["models"].values()
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
for make_data in inventory.values()
|
for make_data in inventory.values()
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'inventory/inventory_stats.html', {'inventory': result})
|
return render(request, "inventory/inventory_stats.html", {"inventory": result})
|
||||||
|
|
||||||
|
|
||||||
class CarDetailView(LoginRequiredMixin, DetailView):
|
class CarDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = models.Car
|
model = models.Car
|
||||||
template_name = 'inventory/car_detail.html'
|
template_name = "inventory/car_detail.html"
|
||||||
context_object_name = 'car'
|
context_object_name = "car"
|
||||||
|
|
||||||
|
|
||||||
class CarFinanceCreateView(LoginRequiredMixin, CreateView):
|
class CarFinanceCreateView(LoginRequiredMixin, CreateView):
|
||||||
model = models.CarFinance
|
model = models.CarFinance
|
||||||
form_class = forms.CarFinanceForm
|
form_class = forms.CarFinanceForm
|
||||||
template_name = 'inventory/car_finance_form.html'
|
template_name = "inventory/car_finance_form.html"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.car = get_object_or_404(models.Car, pk=self.kwargs['car_pk'])
|
self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
form.instance.car = self.car
|
form.instance.car = self.car
|
||||||
messages.success(self.request, _('Car finance details saved successfully.'))
|
messages.success(self.request, _("Car finance details saved successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('car_detail', kwargs={'pk': self.car.pk})
|
return reverse("car_detail", kwargs={"pk": self.car.pk})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['car'] = self.car
|
context["car"] = self.car
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class CarFinanceUpdateView(LoginRequiredMixin, UpdateView):
|
class CarFinanceUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
model = models.CarFinance
|
model = models.CarFinance
|
||||||
form_class = forms.CarFinanceForm
|
form_class = forms.CarFinanceForm
|
||||||
template_name = 'inventory/car_finance_form.html'
|
template_name = "inventory/car_finance_form.html"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Car finance updated successfully.'))
|
messages.success(self.request, _("Car finance updated successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
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})
|
||||||
|
|
||||||
|
|
||||||
class CarUpdateView(LoginRequiredMixin, UpdateView):
|
class CarUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
model = models.Car
|
model = models.Car
|
||||||
form_class = forms.CarUpdateForm
|
form_class = forms.CarUpdateForm
|
||||||
template_name = 'inventory/car_edit.html'
|
template_name = "inventory/car_edit.html"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Car updated successfully.'))
|
messages.success(self.request, _("Car updated successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('car_detail', kwargs={'pk': self.object.pk})
|
return reverse("car_detail", kwargs={"pk": self.object.pk})
|
||||||
|
|
||||||
|
|
||||||
class CarDeleteView(LoginRequiredMixin, DeleteView):
|
class CarDeleteView(LoginRequiredMixin, DeleteView):
|
||||||
model = models.Car
|
model = models.Car
|
||||||
template_name = 'inventory/car_confirm_delete.html'
|
template_name = "inventory/car_confirm_delete.html"
|
||||||
success_url = reverse_lazy('inventory_stats')
|
success_url = reverse_lazy("inventory_stats")
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
messages.success(request, _('Car deleted successfully.'))
|
messages.success(request, _("Car deleted successfully."))
|
||||||
return super().delete(request, *args, **kwargs)
|
return super().delete(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@ -421,26 +475,26 @@ class CustomCardCreateView(LoginRequiredMixin, CreateView):
|
|||||||
template_name = "inventory/add_custom_card.html"
|
template_name = "inventory/add_custom_card.html"
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
car = get_object_or_404(models.Car, pk=self.kwargs['car_pk'])
|
car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
||||||
form.instance.car = car
|
form.instance.car = car
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['car'] = get_object_or_404(models.Car, pk=self.kwargs['car_pk'])
|
context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
messages.success(self.request, _("Custom Card added successfully."))
|
messages.success(self.request, _("Custom Card added successfully."))
|
||||||
return reverse_lazy('car_detail', kwargs={'pk': self.kwargs['car_pk']})
|
return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]})
|
||||||
|
|
||||||
|
|
||||||
class CarColorCreateView(LoginRequiredMixin, CreateView):
|
class CarColorCreateView(LoginRequiredMixin, CreateView):
|
||||||
model = models.CarColors
|
model = models.CarColors
|
||||||
template_name = 'inventory/color_palette.html'
|
template_name = "inventory/color_palette.html"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.car = get_object_or_404(models.Car, pk=self.kwargs['car_pk'])
|
self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
||||||
self.available_colors = self.fetch_available_colors()
|
self.available_colors = self.fetch_available_colors()
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
@ -448,41 +502,45 @@ class CarColorCreateView(LoginRequiredMixin, CreateView):
|
|||||||
class ColorPickerForm(ModelForm):
|
class ColorPickerForm(ModelForm):
|
||||||
color = ChoiceField(
|
color = ChoiceField(
|
||||||
choices=self.get_color_choices(),
|
choices=self.get_color_choices(),
|
||||||
widget=RadioSelect(attrs={'class': 'color-picker'}),
|
widget=RadioSelect(attrs={"class": "color-picker"}),
|
||||||
label=_("Select a Color"),
|
label=_("Select a Color"),
|
||||||
)
|
)
|
||||||
color_type = ChoiceField(
|
color_type = ChoiceField(
|
||||||
choices=models.CarColors.ColorType.choices,
|
choices=models.CarColors.ColorType.choices,
|
||||||
widget=RadioSelect(attrs={'class': 'color-type-picker'}),
|
widget=RadioSelect(attrs={"class": "color-type-picker"}),
|
||||||
label=_("Select Color Type"),
|
label=_("Select Color Type"),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = models.CarColors
|
model = models.CarColors
|
||||||
fields = ['color', 'color_type']
|
fields = ["color", "color_type"]
|
||||||
|
|
||||||
return ColorPickerForm
|
return ColorPickerForm
|
||||||
|
|
||||||
def fetch_available_colors(self):
|
def fetch_available_colors(self):
|
||||||
car_data = {
|
car_data = {
|
||||||
'make': self.car.id_car_make.name,
|
"make": self.car.id_car_make.name,
|
||||||
'model': self.car.id_car_model.name,
|
"model": self.car.id_car_model.name,
|
||||||
'year': str(self.car.year),
|
"year": str(self.car.year),
|
||||||
}
|
}
|
||||||
return fetch_colors(car_data) or []
|
return fetch_colors(car_data) or []
|
||||||
|
|
||||||
def get_color_choices(self):
|
def get_color_choices(self):
|
||||||
return [(color['rgb'], color['name']) for color in self.available_colors]
|
return [(color["rgb"], color["name"]) for color in self.available_colors]
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
selected_rgb = form.cleaned_data['color']
|
selected_rgb = form.cleaned_data["color"]
|
||||||
selected_name = next(
|
selected_name = next(
|
||||||
(color['name'] for color in self.available_colors if color['rgb'] == selected_rgb),
|
(
|
||||||
None
|
color["name"]
|
||||||
|
for color in self.available_colors
|
||||||
|
if color["rgb"] == selected_rgb
|
||||||
|
),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not selected_name:
|
if not selected_name:
|
||||||
messages.error(self.request, _('Invalid color selection.'))
|
messages.error(self.request, _("Invalid color selection."))
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
# Assign the car and selected color details
|
# Assign the car and selected color details
|
||||||
@ -490,26 +548,26 @@ class CarColorCreateView(LoginRequiredMixin, CreateView):
|
|||||||
form.instance.rgb = selected_rgb
|
form.instance.rgb = selected_rgb
|
||||||
form.instance.name = selected_name
|
form.instance.name = selected_name
|
||||||
form.instance.arabic_name = translate(selected_name)
|
form.instance.arabic_name = translate(selected_name)
|
||||||
form.instance.color_type = form.cleaned_data['color_type']
|
form.instance.color_type = form.cleaned_data["color_type"]
|
||||||
|
|
||||||
messages.success(self.request, _('Color added successfully.'))
|
messages.success(self.request, _("Color added successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('car_detail', kwargs={'pk': self.car.pk})
|
return reverse("car_detail", kwargs={"pk": self.car.pk})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['car'] = self.car
|
context["car"] = self.car
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class CarColorUpdateView(LoginRequiredMixin, UpdateView):
|
class CarColorUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
model = forms.CarColors
|
model = forms.CarColors
|
||||||
template_name = 'inventory/color_palette.html'
|
template_name = "inventory/color_palette.html"
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
self.car = get_object_or_404(models.Car, pk=self.kwargs['car_pk'])
|
self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"])
|
||||||
self.available_colors = self.fetch_available_colors()
|
self.available_colors = self.fetch_available_colors()
|
||||||
return super().dispatch(request, *args, **kwargs)
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
@ -517,50 +575,55 @@ class CarColorUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
class ColorPickerForm(ModelForm):
|
class ColorPickerForm(ModelForm):
|
||||||
color = ChoiceField(
|
color = ChoiceField(
|
||||||
choices=self.get_color_choices(),
|
choices=self.get_color_choices(),
|
||||||
widget=RadioSelect(attrs={'class': 'color-picker'}),
|
widget=RadioSelect(attrs={"class": "color-picker"}),
|
||||||
label=_("Select a Color"),
|
label=_("Select a Color"),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = forms.CarColors
|
model = forms.CarColors
|
||||||
fields = ['color']
|
fields = ["color"]
|
||||||
|
|
||||||
return ColorPickerForm
|
return ColorPickerForm
|
||||||
|
|
||||||
def fetch_available_colors(self):
|
def fetch_available_colors(self):
|
||||||
car_data = {
|
car_data = {
|
||||||
'make': self.car.id_car_make.name,
|
"make": self.car.id_car_make.name,
|
||||||
'model': self.car.id_car_model.name,
|
"model": self.car.id_car_model.name,
|
||||||
'year': str(self.car.year),
|
"year": str(self.car.year),
|
||||||
}
|
}
|
||||||
return fetch_colors(car_data) or []
|
return fetch_colors(car_data) or []
|
||||||
|
|
||||||
def get_color_choices(self):
|
def get_color_choices(self):
|
||||||
return [(color['rgb'], color['name']) for color in self.available_colors]
|
return [(color["rgb"], color["name"]) for color in self.available_colors]
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
selected_rgb = form.cleaned_data['color']
|
selected_rgb = form.cleaned_data["color"]
|
||||||
selected_name = next(
|
selected_name = next(
|
||||||
(color['name'] for color in self.available_colors if color['rgb'] == selected_rgb),
|
(
|
||||||
None
|
color["name"]
|
||||||
|
for color in self.available_colors
|
||||||
|
if color["rgb"] == selected_rgb
|
||||||
|
),
|
||||||
|
None,
|
||||||
)
|
)
|
||||||
|
|
||||||
if not selected_name:
|
if not selected_name:
|
||||||
messages.error(self.request, _('Invalid color selection.'))
|
messages.error(self.request, _("Invalid color selection."))
|
||||||
return self.form_invalid(form)
|
return self.form_invalid(form)
|
||||||
|
|
||||||
form.instance.rgb = selected_rgb
|
form.instance.rgb = selected_rgb
|
||||||
form.instance.name = selected_name
|
form.instance.name = selected_name
|
||||||
form.instance.arabic_name = translate(selected_name)
|
form.instance.arabic_name = translate(selected_name)
|
||||||
|
|
||||||
messages.success(self.request, _('Exterior color updated successfully.'))
|
messages.success(self.request, _("Exterior color updated successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('car_detail', kwargs={'pk': self.car.pk})
|
return reverse("car_detail", kwargs={"pk": self.car.pk})
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['car'] = self.car
|
context["car"] = self.car
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@ -570,26 +633,28 @@ def reserve_car_view(request, car_id):
|
|||||||
car = get_object_or_404(models.Car, pk=car_id)
|
car = get_object_or_404(models.Car, pk=car_id)
|
||||||
if car.is_reserved():
|
if car.is_reserved():
|
||||||
messages.error(request, _("This car is already reserved."))
|
messages.error(request, _("This car is already reserved."))
|
||||||
return redirect('car_detail', pk=car.pk)
|
return redirect("car_detail", pk=car.pk)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
||||||
models.CarReservation.objects.create(
|
models.CarReservation.objects.create(
|
||||||
car=car,
|
car=car, reserved_by=request.user, reserved_until=reserved_until
|
||||||
reserved_by=request.user,
|
|
||||||
reserved_until=reserved_until
|
|
||||||
)
|
)
|
||||||
messages.success(request, _("Car reserved successfully."))
|
messages.success(request, _("Car reserved successfully."))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
messages.error(request, f"Error reserving car: {e}")
|
messages.error(request, f"Error reserving car: {e}")
|
||||||
|
|
||||||
return redirect('car_detail', pk=car.pk)
|
return redirect("car_detail", pk=car.pk)
|
||||||
return JsonResponse({"success": False, "message": "Invalid request method."}, status=400)
|
return JsonResponse(
|
||||||
|
{"success": False, "message": "Invalid request method."}, status=400
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@login_required
|
@login_required
|
||||||
def manage_reservation(request, reservation_id):
|
def manage_reservation(request, reservation_id):
|
||||||
reservation = get_object_or_404(models.CarReservation, pk=reservation_id, reserved_by=request.user)
|
reservation = get_object_or_404(
|
||||||
|
models.CarReservation, pk=reservation_id, reserved_by=request.user
|
||||||
|
)
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
action = request.POST.get("action")
|
action = request.POST.get("action")
|
||||||
@ -597,105 +662,109 @@ def manage_reservation(request, reservation_id):
|
|||||||
reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24)
|
||||||
reservation.save()
|
reservation.save()
|
||||||
messages.success(request, _("Reservation renewed successfully."))
|
messages.success(request, _("Reservation renewed successfully."))
|
||||||
return redirect('car_detail', pk=reservation.car.pk)
|
return redirect("car_detail", pk=reservation.car.pk)
|
||||||
|
|
||||||
elif action == "cancel":
|
elif action == "cancel":
|
||||||
reservation.delete()
|
reservation.delete()
|
||||||
messages.success(request, _("Reservation canceled successfully."))
|
messages.success(request, _("Reservation canceled successfully."))
|
||||||
return redirect('car_detail', pk=reservation.car.pk)
|
return redirect("car_detail", pk=reservation.car.pk)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
return JsonResponse({"success": False, "message": _("Invalid action.")}, status=400)
|
return JsonResponse(
|
||||||
|
{"success": False, "message": _("Invalid action.")}, status=400
|
||||||
|
)
|
||||||
|
|
||||||
return JsonResponse({"success": False, "message": _("Invalid request method.")}, status=400)
|
return JsonResponse(
|
||||||
|
{"success": False, "message": _("Invalid request method.")}, status=400
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class DealerListView(LoginRequiredMixin, ListView):
|
class DealerListView(LoginRequiredMixin, ListView):
|
||||||
model = models.Dealer
|
model = models.Dealer
|
||||||
template_name = 'dealer_list.html'
|
template_name = "dealer_list.html"
|
||||||
context_object_name = 'dealers'
|
context_object_name = "dealers"
|
||||||
|
|
||||||
|
|
||||||
class DealerDetailView(LoginRequiredMixin, DetailView):
|
class DealerDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = models.Dealer
|
model = models.Dealer
|
||||||
template_name = 'dealers/dealer_detail.html'
|
template_name = "dealers/dealer_detail.html"
|
||||||
context_object_name = 'dealer'
|
context_object_name = "dealer"
|
||||||
|
|
||||||
|
|
||||||
class DealerCreateView(LoginRequiredMixin, CreateView):
|
class DealerCreateView(LoginRequiredMixin, CreateView):
|
||||||
model = models.Dealer
|
model = models.Dealer
|
||||||
form_class = forms.DealerForm
|
form_class = forms.DealerForm
|
||||||
template_name = 'dealer_form.html'
|
template_name = "dealer_form.html"
|
||||||
success_url = reverse_lazy('dealer_list')
|
success_url = reverse_lazy("dealer_list")
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Dealer created successfully.'))
|
messages.success(self.request, _("Dealer created successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class DealerUpdateView(LoginRequiredMixin, UpdateView):
|
class DealerUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
model = models.Dealer
|
model = models.Dealer
|
||||||
form_class = forms.DealerForm
|
form_class = forms.DealerForm
|
||||||
template_name = 'dealers/dealer_form.html'
|
template_name = "dealers/dealer_form.html"
|
||||||
success_url = reverse_lazy('dealer_detail')
|
success_url = reverse_lazy("dealer_detail")
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
messages.success(self.request, _('Dealer updated successfully.'))
|
messages.success(self.request, _("Dealer updated successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
|
|
||||||
|
|
||||||
class DealerDeleteView(LoginRequiredMixin, DeleteView):
|
class DealerDeleteView(LoginRequiredMixin, DeleteView):
|
||||||
model = models.Dealer
|
model = models.Dealer
|
||||||
template_name = 'dealer_confirm_delete.html'
|
template_name = "dealer_confirm_delete.html"
|
||||||
success_url = reverse_lazy('dealer_list')
|
success_url = reverse_lazy("dealer_list")
|
||||||
|
|
||||||
def delete(self, request, *args, **kwargs):
|
def delete(self, request, *args, **kwargs):
|
||||||
messages.success(request, _('Dealer deleted successfully.'))
|
messages.success(request, _("Dealer deleted successfully."))
|
||||||
return super().delete(request, *args, **kwargs)
|
return super().delete(request, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class CustomerListView(LoginRequiredMixin, ListView):
|
class CustomerListView(LoginRequiredMixin, ListView):
|
||||||
model = models.Customer
|
model = models.Customer
|
||||||
home_label = _('customers')
|
home_label = _("customers")
|
||||||
context_object_name = 'customers'
|
context_object_name = "customers"
|
||||||
paginate_by = 10
|
paginate_by = 10
|
||||||
template_name = "customers/customer_list.html"
|
template_name = "customers/customer_list.html"
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
query = self.request.GET.get('q')
|
query = self.request.GET.get("q")
|
||||||
customers = models.Customer.objects.filter(dealer__user=self.request.user)
|
customers = models.Customer.objects.filter(dealer__user=self.request.user)
|
||||||
|
|
||||||
if query:
|
if query:
|
||||||
customers = customers.filter(
|
customers = customers.filter(
|
||||||
Q(national_id__icontains=query) |
|
Q(national_id__icontains=query)
|
||||||
Q(first_name__icontains=query) |
|
| Q(first_name__icontains=query)
|
||||||
Q(last_name__icontains=query)
|
| Q(last_name__icontains=query)
|
||||||
)
|
)
|
||||||
return customers
|
return customers
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['query'] = self.request.GET.get('q', '')
|
context["query"] = self.request.GET.get("q", "")
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
class CustomerDetailView(LoginRequiredMixin, DetailView):
|
class CustomerDetailView(LoginRequiredMixin, DetailView):
|
||||||
model = models.Customer
|
model = models.Customer
|
||||||
template_name = 'customers/view_customer.html'
|
template_name = "customers/view_customer.html"
|
||||||
context_object_name = 'customer'
|
context_object_name = "customer"
|
||||||
|
|
||||||
|
|
||||||
class CustomerCreateView(LoginRequiredMixin, CreateView):
|
class CustomerCreateView(LoginRequiredMixin, CreateView):
|
||||||
model = models.Customer
|
model = models.Customer
|
||||||
form_class = forms.CustomerForm
|
form_class = forms.CustomerForm
|
||||||
template_name = 'customers/customer_form.html'
|
template_name = "customers/customer_form.html"
|
||||||
success_url = reverse_lazy('customer_list')
|
success_url = reverse_lazy("customer_list")
|
||||||
|
|
||||||
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
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, _('Customer created successfully.'))
|
messages.success(self.request, _("Customer created successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
else:
|
else:
|
||||||
return form.errors
|
return form.errors
|
||||||
@ -704,14 +773,14 @@ class CustomerCreateView(LoginRequiredMixin, CreateView):
|
|||||||
class CustomerUpdateView(LoginRequiredMixin, UpdateView):
|
class CustomerUpdateView(LoginRequiredMixin, UpdateView):
|
||||||
model = models.Customer
|
model = models.Customer
|
||||||
form_class = forms.CustomerForm
|
form_class = forms.CustomerForm
|
||||||
template_name = 'customers/customer_form.html'
|
template_name = "customers/customer_form.html"
|
||||||
success_url = reverse_lazy('customer_list')
|
success_url = reverse_lazy("customer_list")
|
||||||
|
|
||||||
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
|
||||||
form.save()
|
form.save()
|
||||||
messages.success(self.request, _('Customer updated successfully.'))
|
messages.success(self.request, _("Customer updated successfully."))
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
else:
|
else:
|
||||||
return form.errors
|
return form.errors
|
||||||
@ -721,6 +790,7 @@ class CustomerUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
def delete_customer(request, pk):
|
def delete_customer(request, pk):
|
||||||
customer = get_object_or_404(models.Customer, pk=pk)
|
customer = get_object_or_404(models.Customer, pk=pk)
|
||||||
customer.delete()
|
customer.delete()
|
||||||
|
<<<<<<< HEAD
|
||||||
messages.success(request, _('Customer deleted successfully.'))
|
messages.success(request, _('Customer deleted successfully.'))
|
||||||
return redirect('customer_list')
|
return redirect('customer_list')
|
||||||
|
|
||||||
@ -778,3 +848,7 @@ def delete_vendor(request, pk):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
=======
|
||||||
|
messages.success(request, _("Customer deleted successfully."))
|
||||||
|
return redirect("customer_list")
|
||||||
|
>>>>>>> c96865f (add search to select fields, redirect logged in user to landingpage if they are authenticated)
|
||||||
|
|||||||
@ -16,7 +16,7 @@ dill==0.3.9
|
|||||||
distro==1.9.0
|
distro==1.9.0
|
||||||
dj-rest-auth==7.0.0
|
dj-rest-auth==7.0.0
|
||||||
dj-shop-cart==7.1.1
|
dj-shop-cart==7.1.1
|
||||||
Django==5.1.4
|
Django
|
||||||
django-allauth==65.3.0
|
django-allauth==65.3.0
|
||||||
django-autoslug==1.9.9
|
django-autoslug==1.9.9
|
||||||
django-bootstrap5==24.3
|
django-bootstrap5==24.3
|
||||||
@ -74,7 +74,6 @@ platformdirs==4.3.6
|
|||||||
prometheus_client==0.21.1
|
prometheus_client==0.21.1
|
||||||
psycopg==3.2.3
|
psycopg==3.2.3
|
||||||
psycopg-binary==3.2.3
|
psycopg-binary==3.2.3
|
||||||
psycopg-c==3.2.3
|
|
||||||
py-moneyed==3.0
|
py-moneyed==3.0
|
||||||
pycodestyle==2.12.1
|
pycodestyle==2.12.1
|
||||||
pycparser==2.22
|
pycparser==2.22
|
||||||
|
|||||||
BIN
static/images/logos/users/MainAfter.jpg
Normal file
BIN
static/images/logos/users/MainAfter.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
@ -16,10 +16,15 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<link href="{% static 'css/themes/cosmo/_bootswatch.scss' %}" rel="stylesheet" />
|
<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" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css" />
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@ttskch/select2-bootstrap4-theme@x.x.x/dist/select2-bootstrap4.min.css">
|
||||||
|
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.8/dist/umd/popper.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/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://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||||
<style>
|
<style>
|
||||||
body {
|
body {
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
@ -45,9 +50,11 @@
|
|||||||
<!-- JavaScript Files -->
|
<!-- JavaScript Files -->
|
||||||
|
|
||||||
<script type="text/javascript" src="{% static 'js/main.js' %}"></script>
|
<script type="text/javascript" src="{% static 'js/main.js' %}"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', () => {
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
$('.form-select').select2({
|
||||||
|
theme: 'bootstrap4',
|
||||||
|
});
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Fetch all the forms with the "needs-validation" class
|
// Fetch all the forms with the "needs-validation" class
|
||||||
|
|||||||
@ -436,6 +436,7 @@ async function decodeVin() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function updateFields(vinData) {
|
async function updateFields(vinData) {
|
||||||
|
console.log(vinData)
|
||||||
if (vinData.make_id) {
|
if (vinData.make_id) {
|
||||||
makeSelect.value = vinData.make_id;
|
makeSelect.value = vinData.make_id;
|
||||||
await loadModels(vinData.make_id);
|
await loadModels(vinData.make_id);
|
||||||
|
|||||||
@ -114,4 +114,3 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
get_stats
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% load static %}
|
{% load static %}
|
||||||
{% load custom_filters %}
|
{% load custom_filters %}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user