add qotation-02
This commit is contained in:
parent
88e81fadf1
commit
51fdc0b31f
Binary file not shown.
@ -1533,7 +1533,7 @@ def decode_vin(vin):
|
|||||||
# VR3USHNLWRJ521303
|
# VR3USHNLWRJ521303
|
||||||
# KNARH81E8P5194005
|
# KNARH81E8P5194005
|
||||||
# Example usage
|
# Example usage
|
||||||
vin_number = 'LGWCBE196SB652802'
|
vin_number = 'LGWEE4A53SK607775'
|
||||||
decoded_vin = decode_vin(vin_number)
|
decoded_vin = decode_vin(vin_number)
|
||||||
print(decoded_vin)
|
print(decoded_vin)
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -46,7 +46,7 @@ class CarModelAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
@admin.register(models.CarSerie)
|
@admin.register(models.CarSerie)
|
||||||
class CarSeriesAdmin(admin.ModelAdmin):
|
class CarSeriesAdmin(admin.ModelAdmin):
|
||||||
list_display = ('name', 'arabic_name', 'id_car_model')
|
list_display = ('name', 'arabic_name', 'id_car_model', )
|
||||||
search_fields = ('name', 'id_car_model__name')
|
search_fields = ('name', 'id_car_model__name')
|
||||||
list_filter = ('id_car_model__id_car_make__is_sa_import',
|
list_filter = ('id_car_model__id_car_make__is_sa_import',
|
||||||
'id_car_model__id_car_make__name',)
|
'id_car_model__id_car_make__name',)
|
||||||
@ -55,18 +55,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)
|
||||||
|
|||||||
@ -168,6 +168,7 @@ class QuotationForm(forms.ModelForm):
|
|||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
self.fields['cars'].queryset = Car.objects.filter(
|
self.fields['cars'].queryset = Car.objects.filter(
|
||||||
finances__isnull=False
|
finances__isnull=False
|
||||||
).distinct()
|
).distinct()
|
||||||
|
|||||||
BIN
inventory/management/.DS_Store
vendored
BIN
inventory/management/.DS_Store
vendored
Binary file not shown.
Binary file not shown.
Binary file not shown.
36
inventory/management/commands/translate.py
Normal file
36
inventory/management/commands/translate.py
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
from openai import OpenAI
|
||||||
|
from django.core.management.base import BaseCommand
|
||||||
|
from inventory.models import CarSerie
|
||||||
|
from django.conf import settings
|
||||||
|
|
||||||
|
|
||||||
|
class Command(BaseCommand):
|
||||||
|
help = 'Translates to Arabic and saves them in arabic_name field.'
|
||||||
|
|
||||||
|
def handle(self, *args, **kwargs):
|
||||||
|
client = OpenAI(api_key=settings.OPENAI_API_KEY)
|
||||||
|
car_serie = CarSerie.objects.filter(id_car_model__car__id_car_make__is_sa_import=True)[:500]
|
||||||
|
total = car_serie.count()
|
||||||
|
print(f'Translating {total} names...')
|
||||||
|
for index, car_serie in enumerate(car_serie, start=1):
|
||||||
|
try:
|
||||||
|
completion = client.chat.completions.create(
|
||||||
|
model="gpt-4",
|
||||||
|
messages=[
|
||||||
|
{
|
||||||
|
"role": "system",
|
||||||
|
"content": "You are a translation assistant that translates English to Arabic."
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": car_serie.name
|
||||||
|
}
|
||||||
|
],
|
||||||
|
temperature=0.3,
|
||||||
|
)
|
||||||
|
translation = completion.choices[0].message.content.strip()
|
||||||
|
car_serie.arabic_name = translation
|
||||||
|
car_serie.save()
|
||||||
|
print(f"[{index}/{total}] Translated '{car_serie.name}' to '{translation}'")
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error translating '{car_serie.name}': {e}")
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-10 11:45
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0004_remove_carfinance_administration_vat_amount_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='carfinance',
|
||||||
|
options={'verbose_name': 'Car Financial Details'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='total',
|
||||||
|
field=models.DecimalField(blank=True, decimal_places=2, default=Decimal('0.00'), max_digits=14, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
18
inventory/migrations/0006_alter_car_status.py
Normal file
18
inventory/migrations/0006_alter_car_status.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-10 14:09
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0005_alter_carfinance_options_alter_carfinance_total'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='car',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(choices=[('available', 'Available'), ('sold', 'Sold'), ('hold', 'Hold'), ('damaged', 'Damaged'), ('reserved', 'Reserved')], default='available', max_length=10, verbose_name='Status'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,43 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-10 22:52
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0006_alter_car_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='salequotation',
|
||||||
|
name='amount',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=100000, max_digits=10, verbose_name='Amount'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='salequotation',
|
||||||
|
name='dealer',
|
||||||
|
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='sales', to='inventory.dealer'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='salequotationcar',
|
||||||
|
name='dealer',
|
||||||
|
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, related_name='sale_cars', to='inventory.dealer', verbose_name='Dealer'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='salequotationcar',
|
||||||
|
name='price',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=12000.0, editable=False, max_digits=10, verbose_name='Price'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='salequotationcar',
|
||||||
|
name='quantity',
|
||||||
|
field=models.PositiveIntegerField(default=1, verbose_name='Quantity'),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
inventory/migrations/0008_carfinance_vat_amount.py
Normal file
19
inventory/migrations/0008_carfinance_vat_amount.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-10 23:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0007_salequotation_amount_salequotation_dealer_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='vat_amount',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=2300, editable=False, max_digits=14, verbose_name='Vat Amount'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
19
inventory/migrations/0009_alter_salequotation_amount.py
Normal file
19
inventory/migrations/0009_alter_salequotation_amount.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-11 12:09
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0008_carfinance_vat_amount'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salequotation',
|
||||||
|
name='amount',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, verbose_name='Amount'),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
inventory/migrations/0010_alter_salequotation_dealer.py
Normal file
19
inventory/migrations/0010_alter_salequotation_dealer.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-11 12:16
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0009_alter_salequotation_amount'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='salequotation',
|
||||||
|
name='dealer',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='sales', to='inventory.dealer'),
|
||||||
|
),
|
||||||
|
]
|
||||||
17
inventory/migrations/0011_remove_salequotationcar_dealer.py
Normal file
17
inventory/migrations/0011_remove_salequotationcar_dealer.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-11 12:17
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0010_alter_salequotation_dealer'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='salequotationcar',
|
||||||
|
name='dealer',
|
||||||
|
),
|
||||||
|
]
|
||||||
17
inventory/migrations/0012_remove_salequotationcar_price.py
Normal file
17
inventory/migrations/0012_remove_salequotationcar_price.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 5.1.4 on 2024-12-11 12:18
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0011_remove_salequotationcar_dealer'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='salequotationcar',
|
||||||
|
name='price',
|
||||||
|
),
|
||||||
|
]
|
||||||
Binary file not shown.
@ -112,6 +112,7 @@ class CarStatusChoices(models.TextChoices):
|
|||||||
SOLD = 'sold', _('Sold')
|
SOLD = 'sold', _('Sold')
|
||||||
HOLD = 'hold', _('Hold')
|
HOLD = 'hold', _('Hold')
|
||||||
DAMAGED = 'damaged', _('Damaged')
|
DAMAGED = 'damaged', _('Damaged')
|
||||||
|
RESERVED = 'reserved', _('Reserved')
|
||||||
|
|
||||||
|
|
||||||
class CarStockTypeChoices(models.TextChoices):
|
class CarStockTypeChoices(models.TextChoices):
|
||||||
@ -171,13 +172,13 @@ class Car(models.Model):
|
|||||||
)
|
)
|
||||||
status = models.CharField(
|
status = models.CharField(
|
||||||
max_length=10,
|
max_length=10,
|
||||||
choices=CarStatusChoices.choices,
|
choices=CarStatusChoices,
|
||||||
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,
|
||||||
default=CarStockTypeChoices.NEW,
|
default=CarStockTypeChoices.NEW,
|
||||||
verbose_name=_("Stock Type")
|
verbose_name=_("Stock Type")
|
||||||
)
|
)
|
||||||
@ -204,6 +205,11 @@ class Car(models.Model):
|
|||||||
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
|
||||||
|
def discount_amount(self):
|
||||||
|
finance = self.finances.first()
|
||||||
|
return finance.discount_amount if finance else Decimal('0.00')
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def vat_amount(self):
|
def vat_amount(self):
|
||||||
finance = self.finances.first()
|
finance = self.finances.first()
|
||||||
@ -238,6 +244,10 @@ class CarFinance(models.Model):
|
|||||||
decimal_places=2,
|
decimal_places=2,
|
||||||
verbose_name=_("Profit Margin"),
|
verbose_name=_("Profit Margin"),
|
||||||
editable=False)
|
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"),
|
registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
|
||||||
@ -249,17 +259,19 @@ class CarFinance(models.Model):
|
|||||||
custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
|
custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
|
||||||
default=Decimal('0.00'))
|
default=Decimal('0.00'))
|
||||||
vat_rate = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.15'), verbose_name=_("VAT Rate"),)
|
vat_rate = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.15'), verbose_name=_("VAT Rate"),)
|
||||||
|
total = models.DecimalField(max_digits=14, decimal_places=2, default=Decimal('0.00'), null=True, blank=True)
|
||||||
|
|
||||||
class Meta:
|
def __str__(self):
|
||||||
verbose_name = _("Car Financial Details")
|
return f"{self.selling_price}"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
self.full_clean()
|
self.full_clean()
|
||||||
try:
|
try:
|
||||||
self.profit_margin = self.selling_price - self.cost_price - self.discount_amount
|
|
||||||
services = self.administration_fee + self.transportation_fee + self.custom_card_fee
|
services = self.administration_fee + self.transportation_fee + self.custom_card_fee
|
||||||
price_after_discount = self.selling_price - self.discount_amount
|
price_after_discount = self.selling_price - self.discount_amount
|
||||||
total_vat_amount = (price_after_discount + services) * self.vat_rate
|
total_vat_amount = (price_after_discount + services) * self.vat_rate
|
||||||
|
self.vat_amount = self.selling_price * self.vat_rate
|
||||||
|
self.profit_margin = self.selling_price - self.cost_price - self.discount_amount - self.registration_fee
|
||||||
self.total = price_after_discount + services + total_vat_amount + self.registration_fee
|
self.total = price_after_discount + services + total_vat_amount + self.registration_fee
|
||||||
|
|
||||||
except InvalidOperation as e:
|
except InvalidOperation as e:
|
||||||
@ -267,10 +279,14 @@ class CarFinance(models.Model):
|
|||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Car Financial Details")
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def total_vat_amount(self):
|
def total_vat_amount(self):
|
||||||
return self.total if self.total else Decimal('0.00')
|
services = self.administration_fee + self.transportation_fee + self.custom_card_fee
|
||||||
|
price_after_discount = self.selling_price - self.discount_amount
|
||||||
|
return (price_after_discount + services) * self.vat_rate
|
||||||
|
|
||||||
|
|
||||||
class ExteriorColors(models.Model, LocalizedNameMixin):
|
class ExteriorColors(models.Model, LocalizedNameMixin):
|
||||||
@ -413,6 +429,10 @@ class Customer(models.Model):
|
|||||||
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}"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def get_full_name(self):
|
||||||
|
return f"{self.first_name} {self.middle_name} {self.last_name}"
|
||||||
|
|
||||||
|
|
||||||
class SaleQuotation(models.Model):
|
class SaleQuotation(models.Model):
|
||||||
STATUS_CHOICES = [
|
STATUS_CHOICES = [
|
||||||
@ -420,8 +440,9 @@ class SaleQuotation(models.Model):
|
|||||||
("CONFIRMED", _("Confirmed")),
|
("CONFIRMED", _("Confirmed")),
|
||||||
("CANCELED", _("Canceled")),
|
("CANCELED", _("Canceled")),
|
||||||
]
|
]
|
||||||
|
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name='sales', null=True)
|
||||||
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="quotations", verbose_name=_("Customer"))
|
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name="quotations", verbose_name=_("Customer"))
|
||||||
|
amount = models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=10, verbose_name=_("Amount"))
|
||||||
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
|
||||||
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="DRAFT", verbose_name=_("Status"))
|
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="DRAFT", verbose_name=_("Status"))
|
||||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("Created At"))
|
||||||
@ -457,6 +478,11 @@ class SaleQuotationCar(models.Model):
|
|||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
verbose_name=_("Car")
|
verbose_name=_("Car")
|
||||||
)
|
)
|
||||||
|
# dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="sale_cars", verbose_name=_("Dealer"))
|
||||||
|
quantity = models.PositiveIntegerField(default=1, verbose_name=_("Quantity"))
|
||||||
|
# price = models.DecimalField(decimal_places=2, max_digits=10, verbose_name=_("Price"), editable=False)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def get_financial_details(self):
|
def get_financial_details(self):
|
||||||
"""Retrieve financial details dynamically from CarFinance."""
|
"""Retrieve financial details dynamically from CarFinance."""
|
||||||
@ -488,128 +514,3 @@ class SalesOrder(models.Model):
|
|||||||
return f"Sales Order #{self.id} from Quotation #{self.quotation.id}"
|
return f"Sales Order #{self.id} from Quotation #{self.quotation.id}"
|
||||||
|
|
||||||
|
|
||||||
# Create Entity
|
|
||||||
@receiver(post_save, sender=Dealer)
|
|
||||||
def create_ledger_entity(sender, instance, created, **kwargs):
|
|
||||||
if created:
|
|
||||||
entity = EntityModel.objects.create(
|
|
||||||
name=instance.name,
|
|
||||||
admin=instance.user,
|
|
||||||
address_1=instance.address,
|
|
||||||
fy_start_month=1,
|
|
||||||
accrual_method=True,
|
|
||||||
depth=0,
|
|
||||||
)
|
|
||||||
|
|
||||||
default_coa = entity.create_chart_of_accounts(assign_as_default=True,
|
|
||||||
commit=True,
|
|
||||||
coa_name=_("Chart of Accounts"))
|
|
||||||
if default_coa:
|
|
||||||
entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
|
|
||||||
print(f"Ledger entity created for Dealer: {instance.name}")
|
|
||||||
|
|
||||||
|
|
||||||
# # Create Vendor
|
|
||||||
@receiver(post_save, sender=Vendor)
|
|
||||||
def create_ledger_vendor(sender, instance, created, **kwargs):
|
|
||||||
|
|
||||||
if created:
|
|
||||||
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
|
||||||
|
|
||||||
vendor = VendorModel.objects.update_or_create(
|
|
||||||
entity_model=entity,
|
|
||||||
vendor_name=instance.name,
|
|
||||||
vendor_number=instance.crn,
|
|
||||||
address_1=instance.address,
|
|
||||||
phone=instance.phone_number,
|
|
||||||
tax_id_number=instance.vrn,
|
|
||||||
active=True,
|
|
||||||
hidden=False,
|
|
||||||
additional_info={
|
|
||||||
"arabic_name": instance.arabic_name,
|
|
||||||
"contact_person": instance.contact_person,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"VendorModel created for Vendor: {instance.name}")
|
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=Customer)
|
|
||||||
def create_customer(sender, instance, created, **kwargs):
|
|
||||||
|
|
||||||
if created:
|
|
||||||
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
|
||||||
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
|
|
||||||
|
|
||||||
customer = CustomerModel.objects.create(
|
|
||||||
entity_model=entity,
|
|
||||||
customer_name=name,
|
|
||||||
customer_number=instance.national_id,
|
|
||||||
address_1=instance.address,
|
|
||||||
phone=instance.phone_number,
|
|
||||||
email=instance.email,
|
|
||||||
sales_tax_rate=0.15,
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"Customer created: {name}")
|
|
||||||
|
|
||||||
|
|
||||||
# # Create Item
|
|
||||||
# @receiver(post_save, sender=Car)
|
|
||||||
# def create_item_model(sender, instance, created, **kwargs):
|
|
||||||
# item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}"
|
|
||||||
# uom_name = _("Car")
|
|
||||||
# unit_abbr = _("C")
|
|
||||||
#
|
|
||||||
# uom, uom_created = UnitOfMeasureModel.objects.get_or_create(
|
|
||||||
# name=uom_name,
|
|
||||||
# unit_abbr=unit_abbr
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# if uom_created:
|
|
||||||
# print(f"UOM created: {uom_name}")
|
|
||||||
# else:
|
|
||||||
# print(f"Using existing UOM: {uom_name}")
|
|
||||||
#
|
|
||||||
# entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
|
||||||
#
|
|
||||||
# inventory_account = AccountModel.objects.first()
|
|
||||||
# cogs_account = AccountModel.objects.first()
|
|
||||||
# earnings_account = AccountModel.objects.first()
|
|
||||||
#
|
|
||||||
# item = ItemModel.objects.create(
|
|
||||||
# entity=entity,
|
|
||||||
# uom=uom,
|
|
||||||
# name=item_name,
|
|
||||||
# item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY,
|
|
||||||
# item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
|
|
||||||
# item_id=instance.vin,
|
|
||||||
# sold_as_unit=True,
|
|
||||||
# inventory_received=1.00,
|
|
||||||
# inventory_received_value=0.00,
|
|
||||||
# inventory_account=inventory_account,
|
|
||||||
# for_inventory=True,
|
|
||||||
# is_product_or_service=True,
|
|
||||||
# cogs_account=cogs_account,
|
|
||||||
# earnings_account=earnings_account,
|
|
||||||
# is_active=True,
|
|
||||||
# additional_info={
|
|
||||||
# "remarks": instance.remarks,
|
|
||||||
# "status": instance.status,
|
|
||||||
# "stock_type": instance.stock_type,
|
|
||||||
# "mileage": instance.mileage,
|
|
||||||
# },
|
|
||||||
# )
|
|
||||||
#
|
|
||||||
# print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}")
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# # update price - CarFinance
|
|
||||||
# @receiver(post_save, sender=CarFinance)
|
|
||||||
# def update_item_model_cost(sender, instance, created, **kwargs):
|
|
||||||
#
|
|
||||||
# ItemModel.objects.filter(item_id=instance.car.vin).update(
|
|
||||||
# inventory_received_value=instance.cost_price,
|
|
||||||
# default_amount=instance.cost_price,
|
|
||||||
# )
|
|
||||||
# print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
|
|
||||||
|
|||||||
@ -68,3 +68,11 @@ def calculate_stock_value():
|
|||||||
total_value = sum(car.selling_price for car in cars)
|
total_value = sum(car.selling_price for car in cars)
|
||||||
return total_value
|
return total_value
|
||||||
|
|
||||||
|
|
||||||
|
# from django_ledger.models import EntityModel
|
||||||
|
#
|
||||||
|
# def get_purchase_orders():
|
||||||
|
# entity = EntityModel.objects.get(name='Marwan2')
|
||||||
|
# items = entity.
|
||||||
|
# print(items.values())
|
||||||
|
# return items
|
||||||
|
|||||||
@ -1,20 +1,175 @@
|
|||||||
# from django.db.models.signals import post_save, post_delete
|
from random import randint
|
||||||
# from django.dispatch import receiver
|
|
||||||
# from . import models
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# @receiver(post_save, sender=models.CarReservation)
|
|
||||||
# def update_car_status_on_reservation(sender, instance, created, **kwargs):
|
|
||||||
# if created:
|
|
||||||
# car = instance.car
|
|
||||||
# car.status = models.CarStatusChoices.RESERVED
|
|
||||||
# car.save()
|
|
||||||
#
|
|
||||||
#
|
|
||||||
# @receiver(post_delete, sender=models.CarReservation)
|
|
||||||
# def update_car_status_on_reservation_delete(sender, instance, **kwargs):
|
|
||||||
# car = instance.car
|
|
||||||
# if not car.get_current_reservation():
|
|
||||||
# car.status = models.CarStatusChoices.AVAILABLE
|
|
||||||
# car.save()
|
|
||||||
|
|
||||||
|
from django.db.models.signals import post_save, post_delete
|
||||||
|
from django.dispatch import receiver
|
||||||
|
from django_ledger.models import EntityModel, VendorModel, CustomerModel, UnitOfMeasureModel
|
||||||
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from . import models
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=models.CarReservation)
|
||||||
|
def update_car_status_on_reservation(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
car = instance.car
|
||||||
|
car.status = models.CarStatusChoices.RESERVED
|
||||||
|
car.save()
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_delete, sender=models.CarReservation)
|
||||||
|
def update_car_status_on_reservation_delete(sender, instance, **kwargs):
|
||||||
|
car = instance.car
|
||||||
|
if not car.get_current_reservation():
|
||||||
|
car.status = models.CarStatusChoices.AVAILABLE
|
||||||
|
car.save()
|
||||||
|
|
||||||
|
|
||||||
|
# Create Entity
|
||||||
|
@receiver(post_save, sender=models.Dealer)
|
||||||
|
def create_ledger_entity(sender, instance, created, **kwargs):
|
||||||
|
if created:
|
||||||
|
entity = EntityModel.objects.create(
|
||||||
|
name=instance.name,
|
||||||
|
admin=instance.user,
|
||||||
|
address_1=instance.address,
|
||||||
|
fy_start_month=1,
|
||||||
|
accrual_method=True,
|
||||||
|
depth=0,
|
||||||
|
)
|
||||||
|
|
||||||
|
default_coa = entity.create_chart_of_accounts(assign_as_default=True,
|
||||||
|
commit=True,
|
||||||
|
coa_name=_("Chart of Accounts"))
|
||||||
|
# entity.create_account(
|
||||||
|
# coa_model=coa,
|
||||||
|
# code=1010,
|
||||||
|
# role='asset_ca_cash',
|
||||||
|
# name=_('Cash'),
|
||||||
|
# balance_type="debit",
|
||||||
|
# )
|
||||||
|
# entity.create_account(
|
||||||
|
# coa_model=coa,
|
||||||
|
# code=1200,
|
||||||
|
# role='asset_ca_inv',
|
||||||
|
# name=_('Inventory'),
|
||||||
|
# balance_type="debit",
|
||||||
|
# active=True)
|
||||||
|
|
||||||
|
|
||||||
|
if default_coa:
|
||||||
|
entity.populate_default_coa(activate_accounts=True, coa_model=default_coa)
|
||||||
|
|
||||||
|
uom_name = _("Unit")
|
||||||
|
unit_abbr = _("U")
|
||||||
|
|
||||||
|
entity.create_uom(uom_name, unit_abbr)
|
||||||
|
|
||||||
|
print(f"Ledger entity created for Dealer: {instance.name}")
|
||||||
|
|
||||||
|
|
||||||
|
# # Create Vendor
|
||||||
|
@receiver(post_save, sender=models.Vendor)
|
||||||
|
def create_ledger_vendor(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
|
if created:
|
||||||
|
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||||
|
|
||||||
|
entity.create_vendor(
|
||||||
|
vendor_name=instance.name,
|
||||||
|
vendor_number=instance.crn,
|
||||||
|
address_1=instance.address,
|
||||||
|
phone=instance.phone_number,
|
||||||
|
tax_id_number=instance.vrn,
|
||||||
|
active=True,
|
||||||
|
hidden=False,
|
||||||
|
additional_info={
|
||||||
|
"arabic_name": instance.arabic_name,
|
||||||
|
"contact_person": instance.contact_person,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
print(f"VendorModel created for Vendor: {instance.name}")
|
||||||
|
|
||||||
|
|
||||||
|
@receiver(post_save, sender=models.Customer)
|
||||||
|
def create_customer(sender, instance, created, **kwargs):
|
||||||
|
|
||||||
|
if created:
|
||||||
|
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||||
|
name = f"{instance.first_name} {instance.middle_name} {instance.last_name}"
|
||||||
|
|
||||||
|
entity.create_customer(
|
||||||
|
customer_name=name,
|
||||||
|
customer_number=instance.national_id,
|
||||||
|
address_1=instance.address,
|
||||||
|
phone=instance.phone_number,
|
||||||
|
email=instance.email,
|
||||||
|
sales_tax_rate=0.15,
|
||||||
|
active=True,
|
||||||
|
hidden=False,
|
||||||
|
additional_info={}
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"Customer created: {name}")
|
||||||
|
|
||||||
|
|
||||||
|
# Create Item
|
||||||
|
@receiver(post_save, sender=models.Car)
|
||||||
|
def create_item_model(sender, instance, created, **kwargs):
|
||||||
|
item_name = f"{instance.year} - {instance.id_car_make} - {instance.id_car_model} - {instance.id_car_trim}"
|
||||||
|
uom_name = _("Car")
|
||||||
|
unit_abbr = _("C")
|
||||||
|
|
||||||
|
uom, uom_created = UnitOfMeasureModel.objects.get_or_create(
|
||||||
|
name=uom_name,
|
||||||
|
unit_abbr=unit_abbr
|
||||||
|
)
|
||||||
|
|
||||||
|
if uom_created:
|
||||||
|
print(f"UOM created: {uom_name}")
|
||||||
|
else:
|
||||||
|
print(f"Using existing UOM: {uom_name}")
|
||||||
|
|
||||||
|
entity = EntityModel.objects.filter(name=instance.dealer.name).first()
|
||||||
|
|
||||||
|
inventory_account = AccountModel.objects.first()
|
||||||
|
cogs_account = AccountModel.objects.first()
|
||||||
|
earnings_account = AccountModel.objects.first()
|
||||||
|
|
||||||
|
entity.create_i
|
||||||
|
|
||||||
|
item = ItemModel.objects.create(
|
||||||
|
entity=entity,
|
||||||
|
uom=uom,
|
||||||
|
name=item_name,
|
||||||
|
item_role=ItemModelAbstract.ITEM_ROLE_INVENTORY,
|
||||||
|
item_type=ItemModelAbstract.ITEM_TYPE_MATERIAL,
|
||||||
|
item_id=instance.vin,
|
||||||
|
sold_as_unit=True,
|
||||||
|
inventory_received=1.00,
|
||||||
|
inventory_received_value=0.00,
|
||||||
|
inventory_account=inventory_account,
|
||||||
|
for_inventory=True,
|
||||||
|
is_product_or_service=True,
|
||||||
|
cogs_account=cogs_account,
|
||||||
|
earnings_account=earnings_account,
|
||||||
|
is_active=True,
|
||||||
|
additional_info={
|
||||||
|
"remarks": instance.remarks,
|
||||||
|
"status": instance.status,
|
||||||
|
"stock_type": instance.stock_type,
|
||||||
|
"mileage": instance.mileage,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"ItemModel {'created' if created else 'updated'} for Car: {item.name}")
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # update price - CarFinance
|
||||||
|
# @receiver(post_save, sender=CarFinance)
|
||||||
|
# def update_item_model_cost(sender, instance, created, **kwargs):
|
||||||
|
#
|
||||||
|
# ItemModel.objects.filter(item_id=instance.car.vin).update(
|
||||||
|
# inventory_received_value=instance.cost_price,
|
||||||
|
# default_amount=instance.cost_price,
|
||||||
|
# )
|
||||||
|
# print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
|
||||||
|
|||||||
@ -153,7 +153,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
manufacturer_name = vin_info.Make.strip()
|
manufacturer_name = vin_info.Make.strip()
|
||||||
model_name = vin_info.Model.strip()
|
model_name = 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 model_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()
|
||||||
@ -164,15 +164,11 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
elif method_name == 'ELM':
|
elif method_name == 'ELM':
|
||||||
elm_data = vin_info.get('data', {})
|
elm_data = vin_info.get('data', {})
|
||||||
manufacturer_name = elm_data.get('maker', '').strip()
|
manufacturer_name = elm_data.get('maker', '').strip()
|
||||||
print(manufacturer_name)
|
|
||||||
model_name = elm_data.get('model', '').strip()
|
model_name = elm_data.get('model', '').strip()
|
||||||
print(model_name)
|
|
||||||
year_model = elm_data.get('modelYear', '').strip()
|
year_model = elm_data.get('modelYear', '').strip()
|
||||||
print(year_model)
|
|
||||||
if not manufacturer_name or not model_name or not year_model:
|
if not manufacturer_name or not model_name or not year_model:
|
||||||
raise ValueError('ELM returned incomplete data.')
|
raise ValueError('ELM returned incomplete data.')
|
||||||
|
|
||||||
# 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}")
|
||||||
break
|
break
|
||||||
@ -188,19 +184,27 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
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}"
|
||||||
)
|
)
|
||||||
regex_make= manufacturer_name.replace(" ", "[- ]?")
|
regex_make= manufacturer_name.replace(" ", "[- ]?")
|
||||||
car_make = models.CarMake.objects.filter(name__iregex=regex_make).first()
|
car_make = (models.CarMake.objects
|
||||||
|
.filter(name__iregex=regex_make, is_sa_import=True)
|
||||||
|
.first())
|
||||||
if not car_make:
|
if not car_make:
|
||||||
return JsonResponse({'success': False, 'error': 'Manufacturer not found in the database.'}, status=404)
|
return JsonResponse({'success': False, 'error': 'Manufacturer not found in the database.'}, status=404)
|
||||||
|
|
||||||
vin_data['make_id'] = car_make.id_car_make
|
vin_data['make_id'] = car_make.id_car_make
|
||||||
vin_data['name'] = car_make.name
|
vin_data['name'] = car_make.name
|
||||||
vin_data['arabic_name'] = car_make.arabic_name
|
vin_data['arabic_name'] = car_make.arabic_name
|
||||||
|
|
||||||
# car_model = models.CarModel.objects.filter(id_car_make=car_make.id_car_make, name__contains=model_name).first()
|
|
||||||
regex_pattern = model_name.replace(" ", "[- ]?")
|
regex_pattern = model_name.replace(" ", "[- ]?")
|
||||||
car_model = models.CarModel.objects.filter(id_car_make=car_make.id_car_make, name__iregex=regex_pattern).first()
|
car_model = (models.CarModel.objects
|
||||||
|
.filter(id_car_make=car_make.id_car_make,
|
||||||
|
name__iregex=regex_pattern)
|
||||||
|
.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
|
||||||
@ -208,7 +212,11 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
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,
|
||||||
|
id_car_make__is_sa_import=True)
|
||||||
|
.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):
|
||||||
@ -685,6 +693,7 @@ class QuotationCreateView(LoginRequiredMixin, CreateView):
|
|||||||
template_name = 'sales/quotation_form.html'
|
template_name = 'sales/quotation_form.html'
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
form.instance.dealer = self.request.user.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:
|
||||||
|
|||||||
3
ledger_testing.py
Normal file
3
ledger_testing.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
BIN
static/images/logos/suppliers/muhammad-yousef-naghi.png
Normal file
BIN
static/images/logos/suppliers/muhammad-yousef-naghi.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
BIN
static/images/logos/suppliers/unnamed.png
Normal file
BIN
static/images/logos/suppliers/unnamed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 86 KiB |
BIN
static/images/logos/vendors/Alamjdouie-Hyundai-01_OX3eq7o.png
vendored
Normal file
BIN
static/images/logos/vendors/Alamjdouie-Hyundai-01_OX3eq7o.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
BIN
static/images/logos/vendors/Aljomaih-Automotive-Company-3_3uzBd9i.png
vendored
Normal file
BIN
static/images/logos/vendors/Aljomaih-Automotive-Company-3_3uzBd9i.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 31 KiB |
BIN
static/images/logos/vendors/muhammad-yousef-naghi.png
vendored
Normal file
BIN
static/images/logos/vendors/muhammad-yousef-naghi.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
@ -55,9 +55,9 @@ small, .small {
|
|||||||
<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({
|
/* $('.form-select').select2({
|
||||||
theme: 'bootstrap4',
|
theme: 'bootstrap4',
|
||||||
});
|
});*/
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Fetch all the forms with the "needs-validation" class
|
// Fetch all the forms with the "needs-validation" class
|
||||||
|
|||||||
@ -21,12 +21,12 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="btn-group">
|
||||||
<button type="button"
|
<button type="button"
|
||||||
class="btn btn-secondary"
|
class="btn btn-sm btn-secondary"
|
||||||
data-bs-dismiss="modal">
|
data-bs-dismiss="modal">
|
||||||
{% trans 'No' %}
|
{% trans 'No' %}
|
||||||
</button>
|
</button>
|
||||||
<a type="button"
|
<a type="button"
|
||||||
class="btn btn-danger"
|
class="btn btn-sm btn-danger"
|
||||||
href="{% url 'customer_delete' customer.id %}">
|
href="{% url 'customer_delete' customer.id %}">
|
||||||
{% trans 'Yes' %}
|
{% trans 'Yes' %}
|
||||||
</a>
|
</a>
|
||||||
@ -56,17 +56,17 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer d-flex ">
|
<div class="card-footer d-flex ">
|
||||||
<a class="btn btn-primary me-1" href="{% url 'customer_update' customer.id %}">
|
<a class="btn btn-sm btn-primary me-1" href="{% url 'customer_update' customer.id %}">
|
||||||
<!--<i class="bi bi-pencil-square"></i> -->
|
<!--<i class="bi bi-pencil-square"></i> -->
|
||||||
{{ _("Edit") }}
|
{{ _("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-danger me-1"
|
<a class="btn btn-sm btn-danger me-1"
|
||||||
data-bs-toggle="modal"
|
data-bs-toggle="modal"
|
||||||
data-bs-target="#deleteModal">
|
data-bs-target="#deleteModal">
|
||||||
<!--<i class="bi bi-trash-fill"></i>-->
|
<!--<i class="bi bi-trash-fill"></i>-->
|
||||||
{{ _("Delete") }}
|
{{ _("Delete") }}
|
||||||
</a>
|
</a>
|
||||||
<a class="btn btn-secondary"
|
<a class="btn btn-sm btn-secondary"
|
||||||
href="{% url 'customer_list' %}">
|
href="{% url 'customer_list' %}">
|
||||||
<!--<i class="bi bi-arrow-left-square-fill"></i>-->
|
<!--<i class="bi bi-arrow-left-square-fill"></i>-->
|
||||||
{% trans "Back to List" %}
|
{% trans "Back to List" %}
|
||||||
|
|||||||
BIN
templates/partials/.DS_Store
vendored
Normal file
BIN
templates/partials/.DS_Store
vendored
Normal file
Binary file not shown.
@ -12,7 +12,7 @@
|
|||||||
<h5>{% trans "Customer Details" %}</h5>
|
<h5>{% trans "Customer Details" %}</h5>
|
||||||
<p>
|
<p>
|
||||||
<strong>{% trans "Name" %}:</strong>
|
<strong>{% trans "Name" %}:</strong>
|
||||||
{{ quotation.customer.first_name }} {{ quotation.customer.last_name_name }}</p>
|
{{ quotation.customer.get_full_name }}</p>
|
||||||
<p><strong>{% trans "Address" %}:</strong> {{ quotation.customer.address }}</p>
|
<p><strong>{% trans "Address" %}:</strong> {{ quotation.customer.address }}</p>
|
||||||
<p><strong>{% trans "VAT No" %}:</strong> {{ quotation.customer.vat_number }}</p>
|
<p><strong>{% trans "VAT No" %}:</strong> {{ quotation.customer.vat_number }}</p>
|
||||||
</div>
|
</div>
|
||||||
@ -32,7 +32,6 @@
|
|||||||
<th>Model</th>
|
<th>Model</th>
|
||||||
<th>Selling Price</th>
|
<th>Selling Price</th>
|
||||||
<th>VAT</th>
|
<th>VAT</th>
|
||||||
<th>Total Before VAT</th>
|
|
||||||
<th>Total</th>
|
<th>Total</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -42,18 +41,17 @@
|
|||||||
<td>{{ item.car.vin }}</td>
|
<td>{{ item.car.vin }}</td>
|
||||||
<td>{{ item.car.id_car_model.get_local_name }}</td>
|
<td>{{ item.car.id_car_model.get_local_name }}</td>
|
||||||
<td>{{ item.car.selling_price }}</td>
|
<td>{{ item.car.selling_price }}</td>
|
||||||
<td>{{ item.car.total_vat_amount }}</td>
|
<td>{{ item.car.vat_amount }}</td>
|
||||||
<td>{{ item.car.total_before_vat }}</td>
|
|
||||||
<td>{{ item.car.total }}</td>
|
<td>{{ item.car.total }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="3">Totals</th>
|
<th colspan="2">Totals</th>
|
||||||
<th>{{ item.car.total_vat_amount }}</th>
|
<th>{{ item.car.finances.total_vat_amount }}</th>
|
||||||
<th>{{ item.car.total_before_vat }}</th>
|
<th>{{ item.car.finances.total_before_vat }}</th>
|
||||||
<th>{{ item.car.total }}</th>
|
<th>{{ item.car.finances.total }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
@ -24,7 +24,7 @@
|
|||||||
<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.total }}</td>
|
<td>{{ quotation.quotation_cars.get_financial_details.total_amount }}</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">
|
||||||
|
|||||||
2
templates/vendors/vendors_list.html
vendored
2
templates/vendors/vendors_list.html
vendored
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<div class="container-fluid p-3">
|
<div class="container-fluid p-3">
|
||||||
<div class="card shadow-sm">
|
<div class="card shadow rounded">
|
||||||
<div class="card-header d-flex justify-content-between align-items-center">
|
<div class="card-header d-flex justify-content-between align-items-center">
|
||||||
<h6 class="mb-0">{{ _("Vendors")|capfirst }}</h6>
|
<h6 class="mb-0">{{ _("Vendors")|capfirst }}</h6>
|
||||||
<form method="get" class="d-inline-block">
|
<form method="get" class="d-inline-block">
|
||||||
|
|||||||
110
templates/vendors/view_vendor.html
vendored
110
templates/vendors/view_vendor.html
vendored
@ -1,56 +1,74 @@
|
|||||||
{% extends 'base.html' %}
|
{% extends 'base.html' %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block title %}{% trans "View Vendor" %}{% endblock title %}
|
{% block title %}{% trans "View Vendor" %}{% endblock title %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<!-- Delete Modal -->
|
||||||
<!-- Delete Modal -->
|
<div class="modal fade" id="deleteModal"
|
||||||
<div class="modal fade" id="deleteModal"
|
data-bs-backdrop="static"
|
||||||
data-bs-backdrop="static"
|
data-bs-keyboard="false"
|
||||||
data-bs-keyboard="false"
|
tabindex="-1"
|
||||||
tabindex="-1"
|
aria-labelledby="deleteModalLabel"
|
||||||
aria-labelledby="deleteModalLabel"
|
aria-hidden="true">
|
||||||
aria-hidden="true">
|
<div class="modal-dialog modal-sm">
|
||||||
<div class="modal-dialog modal-sm ">
|
<div class="modal-content">
|
||||||
<div class="modal-content rounded">
|
<div class="modal-header bg-danger text-white">
|
||||||
<div class="modal-body d-flex justify-content-center">
|
<h5 class="modal-title" id="deleteModalLabel">
|
||||||
<h1 class="text-danger me-2"><i class="bi bi-exclamation-diamond-fill"></i></h1>
|
<i class="bi bi-exclamation-diamond-fill"></i>
|
||||||
<span class="text-danger">
|
{% trans "Delete Vendor" %}
|
||||||
{% trans "Are you sure you want to delete this vendor?" %}
|
</h5>
|
||||||
</span>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
</div>
|
</div>
|
||||||
<div class="btn-group">
|
<div class="modal-body text-center">
|
||||||
<button type="button"
|
<p class="mb-0 text-danger fw-bold">
|
||||||
class="btn btn-secondary"
|
{% trans "Are you sure you want to delete this vendor?" %}
|
||||||
data-bs-dismiss="modal">
|
</p>
|
||||||
{% trans 'No' %}
|
</div>
|
||||||
</button>
|
<div class="modal-footer justify-content-center">
|
||||||
<a type="button"
|
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
|
||||||
class="btn btn-danger"
|
{% trans "No" %}
|
||||||
href="{% url 'vendor_delete' vendor.id %}">
|
</button>
|
||||||
{% trans 'Yes' %}
|
<a type="button" class="btn btn-danger btn-sm" href="{% url 'vendor_delete' vendor.id %}">
|
||||||
</a>
|
{% trans "Yes" %}
|
||||||
</div>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="container">
|
</div>
|
||||||
<h1>{% trans "Vendor Details" %}</h1>
|
|
||||||
<ul>
|
<!-- Vendor Details -->
|
||||||
<li><strong>{% trans "Name" %}:</strong>
|
<div class="container mt-4">
|
||||||
|
<div class="card shadow rounded">
|
||||||
{{ vendor.get_local_name }}
|
<div class="card-header bg-primary text-white">
|
||||||
</li>
|
<h4 class="mb-0">{% trans "Vendor Details" %}</h4>
|
||||||
<li><strong>{% trans "Contact Person" %}:</strong> {{ vendor.contact_person }}</li>
|
</div>
|
||||||
<li><strong>{% trans "Phone Number" %}:</strong> {{ vendor.phone_number }}</li>
|
<div class="card-body">
|
||||||
<li><strong>{% trans "Email" %}:</strong> {{ vendor.email }}</li>
|
<ul class="list-group list-group-flush">
|
||||||
<li><strong>{% trans "Address" %}:</strong> {{ vendor.address }}</li>
|
<li class="list-group-item">
|
||||||
</ul>
|
<strong>{% trans "Name" %}:</strong> {{ vendor.get_local_name }}
|
||||||
<a class="btn btn-sm btn-primary" href="{% url 'vendor_update' vendor.id %}">{% trans "Edit" %}</a>
|
</li>
|
||||||
<a class="btn btn-sm btn-danger me-1"
|
<li class="list-group-item">
|
||||||
data-bs-toggle="modal"
|
<strong>{% trans "Contact Person" %}:</strong> {{ vendor.contact_person }}
|
||||||
data-bs-target="#deleteModal">
|
</li>
|
||||||
<!--<i class="bi bi-trash-fill"></i>-->
|
<li class="list-group-item">
|
||||||
{{ _("Delete") }}
|
<strong>{% trans "Phone Number" %}:</strong> {{ vendor.phone_number }}
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<strong>{% trans "Email" %}:</strong> {{ vendor.email }}
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<strong>{% trans "Address" %}:</strong> {{ vendor.address }}
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer d-flex justify-content-between">
|
||||||
|
<a class="btn btn-sm btn-primary" href="{% url 'vendor_update' vendor.id %}">
|
||||||
|
{% trans "Edit" %}
|
||||||
</a>
|
</a>
|
||||||
|
<button class="btn btn-sm btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">
|
||||||
|
{% trans "Delete" %}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|||||||
52
testapi.py
52
testapi.py
@ -1,20 +1,8 @@
|
|||||||
|
|
||||||
# from vin import VIN
|
from vin import VIN
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
#
|
|
||||||
# vin_no = 'VR7ED9HP6SJ522156'
|
|
||||||
#
|
|
||||||
# details = {
|
|
||||||
# # 'Description': VIN(vin_no).description,
|
|
||||||
# 'make': VIN(vin_no).make,
|
|
||||||
# 'year': VIN(vin_no).model_year,
|
|
||||||
# 'model': VIN(vin_no).model,
|
|
||||||
# 'trim': VIN(vin_no).trim,
|
|
||||||
# 'Series': VIN(vin_no).series,
|
|
||||||
# 'body_class': VIN(vin_no).body_class,
|
|
||||||
# 'Type': VIN(vin_no).vehicle_type,
|
|
||||||
# 'Electrification level': VIN(vin_no).electrification_level,
|
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
# print(details)
|
# print(details)
|
||||||
@ -62,7 +50,7 @@ import json
|
|||||||
#
|
#
|
||||||
# from pyvin import VIN
|
# from pyvin import VIN
|
||||||
#
|
#
|
||||||
# number_vin = 'VR7ED9HP6SJ522156'
|
# number_vin = '5LMCJ2D93NUL03460'
|
||||||
#
|
#
|
||||||
# vehicle = VIN(number_vin)
|
# vehicle = VIN(number_vin)
|
||||||
# if vehicle:
|
# if vehicle:
|
||||||
@ -84,9 +72,25 @@ import json
|
|||||||
# print(e)
|
# print(e)
|
||||||
# else:
|
# else:
|
||||||
# print("No vehicle found")
|
# print("No vehicle found")
|
||||||
# vin_info_py(number_vin)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
vin_no = '5LMCJ2D93NUL03460'
|
||||||
|
|
||||||
|
details = {
|
||||||
|
# 'Description': VIN(vin_no).description,
|
||||||
|
'make': VIN(vin_no).make,
|
||||||
|
'year': VIN(vin_no).model_year,
|
||||||
|
'model': VIN(vin_no).model,
|
||||||
|
'trim': VIN(vin_no).trim,
|
||||||
|
'Series': VIN(vin_no).series,
|
||||||
|
'body_class': VIN(vin_no).body_class,
|
||||||
|
'Type': VIN(vin_no).vehicle_type,
|
||||||
|
'Electrification level': VIN(vin_no).electrification_level,
|
||||||
|
}
|
||||||
|
print(details)
|
||||||
|
|
||||||
# from vpic import Client
|
# from vpic import Client
|
||||||
#
|
#
|
||||||
# c = Client()
|
# c = Client()
|
||||||
@ -120,11 +124,11 @@ import json
|
|||||||
#
|
#
|
||||||
# except Exception as e:
|
# except Exception as e:
|
||||||
# print(e)
|
# print(e)
|
||||||
vin_no = "LS5A3DKR0SA966230"
|
# vin_no = "LS5A3DKR0SA966230"
|
||||||
|
#
|
||||||
|
#
|
||||||
url = "https://vin17.com/Search/query?vin=+"+vin_no
|
# url = "https://vin17.com/Search/query?vin=+"+vin_no
|
||||||
|
#
|
||||||
response = requests.request("GET", url)
|
# response = requests.request("GET", url)
|
||||||
car_info = json.loads(response.text)
|
# car_info = json.loads(response.text)
|
||||||
print(car_info)
|
# print(car_info)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user