update
This commit is contained in:
commit
791e74b3d3
@ -1535,6 +1535,4 @@ def decode_vin(vin):
|
|||||||
# Example usage
|
# Example usage
|
||||||
vin_number = 'VYFED9HP0SJ519559'
|
vin_number = 'VYFED9HP0SJ519559'
|
||||||
decoded_vin = decode_vin(vin_number)
|
decoded_vin = decode_vin(vin_number)
|
||||||
print(decoded_vin)
|
print(decoded_vin)
|
||||||
|
|
||||||
|
|
||||||
@ -23,6 +23,8 @@ admin.site.register(models.CarLocation)
|
|||||||
admin.site.register(models.CarReservation)
|
admin.site.register(models.CarReservation)
|
||||||
admin.site.register(models.Organization)
|
admin.site.register(models.Organization)
|
||||||
admin.site.register(models.Representative)
|
admin.site.register(models.Representative)
|
||||||
|
admin.site.register(models.CarTrim)
|
||||||
|
admin.site.register(models.AdditionalServices)
|
||||||
|
|
||||||
@admin.register(models.CarMake)
|
@admin.register(models.CarMake)
|
||||||
class CarMakeAdmin(admin.ModelAdmin):
|
class CarMakeAdmin(admin.ModelAdmin):
|
||||||
|
|||||||
@ -17,7 +17,8 @@ from .models import (
|
|||||||
CarLocation,
|
CarLocation,
|
||||||
Organization,
|
Organization,
|
||||||
Representative,
|
Representative,
|
||||||
SaleQuotationCar
|
SaleQuotationCar,
|
||||||
|
AdditionalServices
|
||||||
|
|
||||||
)
|
)
|
||||||
from django.forms import ModelMultipleChoiceField
|
from django.forms import ModelMultipleChoiceField
|
||||||
@ -112,11 +113,24 @@ class CarUpdateForm(forms.ModelForm, AddClassMixin):
|
|||||||
|
|
||||||
|
|
||||||
class CarFinanceForm(AddClassMixin, forms.ModelForm):
|
class CarFinanceForm(AddClassMixin, forms.ModelForm):
|
||||||
|
additional_finances = forms.ModelMultipleChoiceField(
|
||||||
|
queryset=AdditionalServices.objects.all(),
|
||||||
|
widget=forms.CheckboxSelectMultiple(attrs={'class': 'form-check-input'}),
|
||||||
|
required=False
|
||||||
|
)
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CarFinance
|
model = CarFinance
|
||||||
exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate']
|
exclude = ['car', 'additional_finances','profit_margin', 'vat_amount', 'total', 'vat_rate','additional_services']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
if self.instance.pk:
|
||||||
|
self.fields['additional_finances'].initial = self.instance.additional_services.all()
|
||||||
|
def save(self, commit=True):
|
||||||
|
instance = super().save()
|
||||||
|
instance.additional_services.set(self.cleaned_data['additional_finances'])
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
class CarLocationForm(forms.ModelForm):
|
class CarLocationForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|||||||
17
inventory/migrations/0031_remove_carfinance_vat_amount.py
Normal file
17
inventory/migrations/0031_remove_carfinance_vat_amount.py
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-18 10:14
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0030_alter_carfinance_options_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='vat_amount',
|
||||||
|
),
|
||||||
|
]
|
||||||
19
inventory/migrations/0032_carfinance_vat_amount.py
Normal file
19
inventory/migrations/0032_carfinance_vat_amount.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-18 10:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0031_remove_carfinance_vat_amount'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='vat_amount',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=14, verbose_name='Vat Amount'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
19
inventory/migrations/0033_alter_carfinance_vat_amount.py
Normal file
19
inventory/migrations/0033_alter_carfinance_vat_amount.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-18 10:19
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0032_carfinance_vat_amount'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='vat_amount',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=14, verbose_name='Vat Amount'),
|
||||||
|
),
|
||||||
|
]
|
||||||
59
inventory/migrations/0034_additionalservices_and_more.py
Normal file
59
inventory/migrations/0034_additionalservices_and_more.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-18 15:59
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0033_alter_carfinance_vat_amount'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='AdditionalServices',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('name', models.CharField(max_length=255, verbose_name='Name')),
|
||||||
|
('description', models.TextField(verbose_name='Description')),
|
||||||
|
('price', models.DecimalField(decimal_places=2, max_digits=14, verbose_name='Price')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Additional Services',
|
||||||
|
'verbose_name_plural': 'Additional Services',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='administration_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='custom_card_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='discount_amount',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='profit_margin',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='registration_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='transportation_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='vat_amount',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='car',
|
||||||
|
name='additional_services',
|
||||||
|
field=models.ManyToManyField(related_name='cars', to='inventory.additionalservices'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,50 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-18 16:00
|
||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0034_additionalservices_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='administration_fee',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Administration Fee'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='custom_card_fee',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Custom Card Fee'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='discount_amount',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Discount Amount'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='profit_margin',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=0, editable=False, max_digits=14, verbose_name='Profit Margin'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='registration_fee',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Registration Fee'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='transportation_fee',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), max_digits=14, verbose_name='Transportation Fee'),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='vat_amount',
|
||||||
|
field=models.DecimalField(decimal_places=2, default=Decimal('0.00'), editable=False, max_digits=14, verbose_name='Vat Amount'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-18 16:02
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0035_carfinance_administration_fee_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='car',
|
||||||
|
name='additional_services',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='additional_services',
|
||||||
|
field=models.ManyToManyField(related_name='cars', to='inventory.additionalservices'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-19 09:21
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0036_remove_car_additional_services_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='additional_services',
|
||||||
|
field=models.ManyToManyField(related_name='additional_finances', to='inventory.additionalservices'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-19 09:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0037_alter_carfinance_additional_services'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='administration_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='custom_card_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='profit_margin',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='registration_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='transportation_fee',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='vat_amount',
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='additional_services',
|
||||||
|
field=models.ManyToManyField(blank=True, null=True, related_name='additional_finances', to='inventory.additionalservices'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-19 09:33
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0038_remove_carfinance_administration_fee_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='carfinance',
|
||||||
|
name='additional_services',
|
||||||
|
field=models.ManyToManyField(blank=True, related_name='additional_finances', to='inventory.additionalservices'),
|
||||||
|
),
|
||||||
|
]
|
||||||
19
inventory/migrations/0040_additionalservices_display_name.py
Normal file
19
inventory/migrations/0040_additionalservices_display_name.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.17 on 2024-12-19 12:01
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('inventory', '0039_alter_carfinance_additional_services'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='additionalservices',
|
||||||
|
name='display_name',
|
||||||
|
field=models.CharField(default=1, max_length=255, verbose_name='Display Name'),
|
||||||
|
preserve_default=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -1,6 +1,7 @@
|
|||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db import models, transaction
|
from django.db import models, transaction
|
||||||
|
from django.db.models import Sum, F, Count
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db.models.signals import pre_save, post_save
|
from django.db.models.signals import pre_save, post_save
|
||||||
from django.dispatch import receiver
|
from django.dispatch import receiver
|
||||||
@ -22,6 +23,7 @@ from django.core.exceptions import ValidationError
|
|||||||
from phonenumber_field.modelfields import PhoneNumberField
|
from phonenumber_field.modelfields import PhoneNumberField
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
from django.utils.timezone import now
|
from django.utils.timezone import now
|
||||||
|
from .utilities.financials import get_financial_value, get_total, get_total_financials
|
||||||
|
|
||||||
from .mixins import LocalizedNameMixin
|
from .mixins import LocalizedNameMixin
|
||||||
|
|
||||||
@ -140,6 +142,19 @@ class DEALER_TYPES(models.TextChoices):
|
|||||||
Sales = "sales", _("Sales")
|
Sales = "sales", _("Sales")
|
||||||
|
|
||||||
|
|
||||||
|
class AdditionalServices(models.Model):
|
||||||
|
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||||
|
display_name = models.CharField(max_length=255, verbose_name=_("Display Name"))
|
||||||
|
description = models.TextField(verbose_name=_("Description"))
|
||||||
|
price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Price"))
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
verbose_name = _("Additional Services")
|
||||||
|
verbose_name_plural = _("Additional Services")
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.name + " - " + str(self.price)
|
||||||
|
|
||||||
class Car(models.Model):
|
class Car(models.Model):
|
||||||
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
||||||
dealer = models.ForeignKey(
|
dealer = models.ForeignKey(
|
||||||
@ -217,90 +232,6 @@ class Car(models.Model):
|
|||||||
active_reservations = self.reservations.filter(reserved_until__gt=now())
|
active_reservations = self.reservations.filter(reserved_until__gt=now())
|
||||||
return active_reservations.exists()
|
return active_reservations.exists()
|
||||||
|
|
||||||
@property
|
|
||||||
def finance(self):
|
|
||||||
"""Fetch the first related CarFinance object, or return None."""
|
|
||||||
return self.finances # Assuming a related_name of 'finances'
|
|
||||||
|
|
||||||
def _get_financial_value(self, attribute):
|
|
||||||
"""Reusable method to safely get financial values."""
|
|
||||||
finance = self.finance
|
|
||||||
return getattr(finance, attribute, Decimal('0.00')) if finance else Decimal('0.00')
|
|
||||||
|
|
||||||
def _calculate_vat(self, value):
|
|
||||||
"""Helper to calculate VAT dynamically for a given value."""
|
|
||||||
vat_rate = getattr(settings, 'VAT_RATE', Decimal('0.15')) # Default VAT rate
|
|
||||||
return (value * vat_rate).quantize(Decimal('0.01'))
|
|
||||||
|
|
||||||
@property
|
|
||||||
def cost_price(self):
|
|
||||||
return self._get_financial_value('cost_price')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def selling_price(self):
|
|
||||||
return self._get_financial_value('selling_price')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def registration_fee(self):
|
|
||||||
return self._get_financial_value('registration_fee')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def administration_fee(self):
|
|
||||||
return self._get_financial_value('administration_fee')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def transportation_fee(self):
|
|
||||||
return self._get_financial_value('transportation_fee')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def custom_card_fee(self):
|
|
||||||
return self._get_financial_value('custom_card_fee')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def discount_amount(self):
|
|
||||||
return self._get_financial_value('discount_amount')
|
|
||||||
|
|
||||||
@property
|
|
||||||
def vat_amount(self):
|
|
||||||
"""Dynamically calculate VAT for the selling price after discount."""
|
|
||||||
price_after_discount = self.selling_price - self.discount_amount
|
|
||||||
return self._calculate_vat(price_after_discount)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def administration_fee_vat(self):
|
|
||||||
return self._calculate_vat(self.administration_fee)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def transportation_fee_vat(self):
|
|
||||||
return self._calculate_vat(self.transportation_fee)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def custom_card_fee_vat(self):
|
|
||||||
return self._calculate_vat(self.custom_card_fee)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total_vat_amount(self):
|
|
||||||
"""Sum up the VAT for all applicable fields."""
|
|
||||||
return (
|
|
||||||
self.vat_amount +
|
|
||||||
self.administration_fee_vat +
|
|
||||||
self.transportation_fee_vat +
|
|
||||||
self.custom_card_fee_vat
|
|
||||||
)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def total(self):
|
|
||||||
"""Calculate the total amount including VAT."""
|
|
||||||
price_after_discount = self.selling_price - self.discount_amount
|
|
||||||
subtotal = (
|
|
||||||
price_after_discount +
|
|
||||||
self.registration_fee +
|
|
||||||
self.administration_fee +
|
|
||||||
self.transportation_fee +
|
|
||||||
self.custom_card_fee
|
|
||||||
)
|
|
||||||
return subtotal + self.total_vat_amount
|
|
||||||
|
|
||||||
# class CarData(models.Model):
|
# class CarData(models.Model):
|
||||||
# vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
# vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
|
||||||
# make = models.CharField(max_length=255, verbose_name=_("Make"))
|
# make = models.CharField(max_length=255, verbose_name=_("Make"))
|
||||||
@ -328,7 +259,6 @@ class Car(models.Model):
|
|||||||
# receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
|
# receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class CarReservation(models.Model):
|
class CarReservation(models.Model):
|
||||||
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car"))
|
car = models.ForeignKey('Car', on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Car"))
|
||||||
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By"))
|
reserved_by = models.ForeignKey(User, on_delete=models.CASCADE, related_name='reservations', verbose_name=_("Reserved By"))
|
||||||
@ -344,49 +274,52 @@ class CarReservation(models.Model):
|
|||||||
verbose_name = _("Car Reservation")
|
verbose_name = _("Car Reservation")
|
||||||
verbose_name_plural = _("Car Reservations")
|
verbose_name_plural = _("Car Reservations")
|
||||||
|
|
||||||
|
|
||||||
# Car Finance Model
|
# Car Finance Model
|
||||||
class CarFinance(models.Model):
|
class CarFinance(models.Model):
|
||||||
|
additional_services = models.ManyToManyField(AdditionalServices, related_name="additional_finances",blank=True)
|
||||||
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances')
|
car = models.OneToOneField(Car, on_delete=models.CASCADE, related_name='finances')
|
||||||
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
|
cost_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Cost Price"))
|
||||||
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
|
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
|
||||||
profit_margin = models.DecimalField(max_digits=14,
|
|
||||||
decimal_places=2,
|
|
||||||
verbose_name=_("Profit Margin"),
|
|
||||||
editable=False)
|
|
||||||
vat_amount = models.DecimalField(max_digits=14,
|
|
||||||
decimal_places=2,
|
|
||||||
verbose_name=_("Vat Amount"),
|
|
||||||
editable=False)
|
|
||||||
discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
|
discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
|
||||||
default=Decimal('0.00'))
|
default=Decimal('0.00'))
|
||||||
registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
|
# profit_margin = models.DecimalField(max_digits=14,
|
||||||
default=Decimal('0.00'))
|
# decimal_places=2,
|
||||||
administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"),
|
# verbose_name=_("Profit Margin"),
|
||||||
default=Decimal('0.00'))
|
# editable=False)
|
||||||
transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"),
|
# vat_amount = models.DecimalField(max_digits=14,
|
||||||
default=Decimal('0.00'))
|
# decimal_places=2,
|
||||||
custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
|
# verbose_name=_("Vat Amount"),
|
||||||
default=Decimal('0.00'))
|
# editable=False,default=Decimal('0.00'))
|
||||||
|
# registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
|
||||||
|
# default=Decimal('0.00'))
|
||||||
|
# administration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Administration Fee"),
|
||||||
|
# default=Decimal('0.00'))
|
||||||
|
# transportation_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Transportation Fee"),
|
||||||
|
# default=Decimal('0.00'))
|
||||||
|
# custom_card_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Custom Card Fee"),
|
||||||
|
# default=Decimal('0.00'))
|
||||||
|
@property
|
||||||
|
def total(self):
|
||||||
|
"""Calculate the total amount including VAT."""
|
||||||
|
return get_total(self)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Car: {self.car}, Selling Price: {self.selling_price}"
|
return f"Car: {self.car}, Selling Price: {self.selling_price}"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
# def save(self, *args, **kwargs):
|
||||||
# vat_rate = settings.VAT_RATE
|
# self.full_clean()
|
||||||
self.full_clean()
|
# try:
|
||||||
try:
|
# price_after_discount = self.selling_price - self.discount_amount
|
||||||
price_after_discount = self.selling_price - self.discount_amount
|
# self.profit_margin = price_after_discount - self.cost_price
|
||||||
self.profit_margin = price_after_discount - self.cost_price
|
# self.vat_amount = settings.VAT_RATE
|
||||||
except InvalidOperation as e:
|
# except InvalidOperation as e:
|
||||||
raise ValidationError(_("Invalid decimal operation: %s") % str(e))
|
# raise ValidationError(_("Invalid decimal operation: %s") % str(e))
|
||||||
super().save(*args, **kwargs)
|
# super().save(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("Car Financial Details")
|
verbose_name = _("Car Financial Details")
|
||||||
verbose_name_plural = _("Car Financial Details")
|
verbose_name_plural = _("Car Financial Details")
|
||||||
|
|
||||||
|
|
||||||
class ExteriorColors(models.Model, LocalizedNameMixin):
|
class ExteriorColors(models.Model, LocalizedNameMixin):
|
||||||
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
name = models.CharField(max_length=255, verbose_name=_("Name"))
|
||||||
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
|
||||||
@ -738,6 +671,13 @@ class SaleQuotation(models.Model):
|
|||||||
total_quantity = self.quotation_cars.aggregate(total=Sum('quantity'))['total']
|
total_quantity = self.quotation_cars.aggregate(total=Sum('quantity'))['total']
|
||||||
return total_quantity or 0
|
return total_quantity or 0
|
||||||
|
|
||||||
|
# @property
|
||||||
|
# def total(self):
|
||||||
|
# total = self.quotation_cars.aggregate(total_price=Sum(F('car__finances__selling_price') * F('quantity')))
|
||||||
|
# if total:
|
||||||
|
# return float(total["total_price"]) * 0.15 + float(total["total_price"])
|
||||||
|
# return 0
|
||||||
|
|
||||||
def confirm(self):
|
def confirm(self):
|
||||||
"""Confirm the quotation and lock financial details."""
|
"""Confirm the quotation and lock financial details."""
|
||||||
if self.status != "DRAFT":
|
if self.status != "DRAFT":
|
||||||
@ -769,7 +709,10 @@ class SaleQuotationCar(models.Model):
|
|||||||
verbose_name=_("Car")
|
verbose_name=_("Car")
|
||||||
)
|
)
|
||||||
quantity = models.PositiveIntegerField(default=1, verbose_name=_("Quantity"))
|
quantity = models.PositiveIntegerField(default=1, verbose_name=_("Quantity"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def finance(self):
|
||||||
|
return self.car.finances
|
||||||
@property
|
@property
|
||||||
def financial_details(self):
|
def financial_details(self):
|
||||||
"""
|
"""
|
||||||
@ -790,15 +733,14 @@ class SaleQuotationCar(models.Model):
|
|||||||
# "total_amount": car_finance.total,
|
# "total_amount": car_finance.total,
|
||||||
}
|
}
|
||||||
|
|
||||||
@property
|
# @property
|
||||||
def total_price(self):
|
# def total(self):
|
||||||
"""
|
# """
|
||||||
Calculate total price dynamically based on quantity and selling price.
|
# Calculate total price dynamically based on quantity and selling price.
|
||||||
"""
|
# """
|
||||||
car_finance = self.car.finances
|
# if not self.car.finances:
|
||||||
if not car_finance:
|
# return Decimal("0.00")
|
||||||
return Decimal("0.00")
|
# return self.car.finances.selling_price * self.quantity
|
||||||
return car_finance.selling_price * self.quantity
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.car} - Quotation #{self.quotation.id}"
|
return f"{self.car} - Quotation #{self.quotation.id}"
|
||||||
|
|||||||
@ -7,7 +7,7 @@ import json
|
|||||||
|
|
||||||
from django_ledger.models import EntityModel
|
from django_ledger.models import EntityModel
|
||||||
|
|
||||||
from .utils import get_jwt_token
|
from inventory.utils import get_jwt_token
|
||||||
from pyvin import VIN
|
from pyvin import VIN
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from openai import OpenAI
|
from openai import OpenAI
|
||||||
|
|||||||
0
inventory/utilities/__init__.py
Normal file
0
inventory/utilities/__init__.py
Normal file
38
inventory/utilities/financials.py
Normal file
38
inventory/utilities/financials.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
|
||||||
|
from decimal import Decimal
|
||||||
|
from django.conf import settings
|
||||||
|
from inventory import models
|
||||||
|
|
||||||
|
def calculate_vat(value):
|
||||||
|
"""Helper to calculate VAT dynamically for a given value."""
|
||||||
|
vat_rate = getattr(settings, 'VAT_RATE', Decimal('0.15')) # Default VAT rate
|
||||||
|
return (value * vat_rate).quantize(Decimal('0.01'))
|
||||||
|
# def get_financial_value(instance,attribute,vat=False):
|
||||||
|
# if vat:
|
||||||
|
# return calculate_vat(getattr(instance, attribute, Decimal('0.00')) if instance else Decimal('0.00'))
|
||||||
|
# return getattr(instance, attribute, Decimal('0.00')) if instance else Decimal('0.00')
|
||||||
|
|
||||||
|
def get_financial_value(name,vat=False):
|
||||||
|
val = models.AdditionalServices.objects.filter(name=name).first()
|
||||||
|
if not val:
|
||||||
|
return 0
|
||||||
|
if vat:
|
||||||
|
return (val.price * settings.VAT_RATE).quantize(Decimal('0.01'))
|
||||||
|
return val.price
|
||||||
|
|
||||||
|
|
||||||
|
def get_total_financials(instance,vat=False):
|
||||||
|
# price_after_discount = get_financial_value(instance,"selling_price",vat) - get_financial_value(instance,"discount_amount",vat)
|
||||||
|
# subtotal = (
|
||||||
|
# price_after_discount +
|
||||||
|
# get_financial_value("registration_fee") +
|
||||||
|
# get_financial_value("administration_fee",vat) +
|
||||||
|
# get_financial_value("transportation_fee",vat) +
|
||||||
|
# get_financial_value("custom_card_fee",vat))
|
||||||
|
|
||||||
|
return 1000
|
||||||
|
|
||||||
|
def get_total(instance):
|
||||||
|
total = get_total_financials(instance)
|
||||||
|
total_vat = get_total_financials(instance,vat=True)
|
||||||
|
return total + total_vat
|
||||||
@ -1,5 +1,8 @@
|
|||||||
import requests
|
import requests
|
||||||
|
from django.conf import settings
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from inventory import models
|
||||||
|
from inventory.utilities.financials import get_financial_value
|
||||||
|
|
||||||
|
|
||||||
def get_jwt_token():
|
def get_jwt_token():
|
||||||
@ -29,3 +32,20 @@ def localize_some_words():
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def get_calculations(quotation):
|
||||||
|
context = {}
|
||||||
|
qc_len = quotation.quotation_cars.count()
|
||||||
|
cars = [x.car for x in quotation.quotation_cars.all()]
|
||||||
|
finances = models.CarFinance.objects.filter(car__in=cars)
|
||||||
|
|
||||||
|
services = models.AdditionalServices.objects.filter(additional_finances__in=finances).all()
|
||||||
|
data = [{"name":x.name,"price":x.price,"total_price":x.price * qc_len,"vated":float(x.price) * 0.15 * float(qc_len),"total_price_vat":float(x.price) + (float(x.price) * 0.15 * float(qc_len))} for x in services]
|
||||||
|
context["services"] = data
|
||||||
|
context["total_cost"] = 0
|
||||||
|
context["total_vat"] = 0
|
||||||
|
context["total_cost_vat"] = 0
|
||||||
|
for k in context["services"]:
|
||||||
|
context["total_cost"] += k["total_price"]
|
||||||
|
context["total_vat"] += k["vated"]
|
||||||
|
context["total_cost_vat"] = float(context["total_cost"])+float(context["total_vat"])
|
||||||
|
return context
|
||||||
@ -28,6 +28,7 @@ from django.contrib import messages
|
|||||||
from django.db.models import Sum, F, Count
|
from django.db.models import Sum, F, Count
|
||||||
|
|
||||||
from inventory.mixins import AddDealerInstanceMixin
|
from inventory.mixins import AddDealerInstanceMixin
|
||||||
|
|
||||||
from .services import elm, decodevin,get_make,get_model,normalize_name
|
from .services import elm, decodevin,get_make,get_model,normalize_name
|
||||||
from .services import elm, decodevin, get_make, get_model, normalize_name, get_ledger_data
|
from .services import elm, decodevin, get_make, get_model, normalize_name, get_ledger_data
|
||||||
from . import models, forms
|
from . import models, forms
|
||||||
@ -37,7 +38,7 @@ from django.contrib.auth.decorators import user_passes_test
|
|||||||
from django.contrib.messages.views import SuccessMessageMixin
|
from django.contrib.messages.views import SuccessMessageMixin
|
||||||
from django.contrib.auth.models import Group
|
from django.contrib.auth.models import Group
|
||||||
from django.contrib.auth import get_user_model
|
from django.contrib.auth import get_user_model
|
||||||
|
from .utils import get_calculations
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@ -187,6 +188,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
def get_series(self, request):
|
def get_series(self, request):
|
||||||
model_id = request.GET.get('model_id')
|
model_id = request.GET.get('model_id')
|
||||||
year = request.GET.get('year')
|
year = request.GET.get('year')
|
||||||
|
|
||||||
|
|
||||||
# Validate inputs
|
# Validate inputs
|
||||||
if not model_id or not year:
|
if not model_id or not year:
|
||||||
@ -206,9 +208,9 @@ class AjaxHandlerView(LoginRequiredMixin, View):
|
|||||||
|
|
||||||
def get_trims(self, request):
|
def get_trims(self, request):
|
||||||
serie_id = request.GET.get('serie_id')
|
serie_id = request.GET.get('serie_id')
|
||||||
model_id = request.GET.get('model_id')
|
# model_id = request.GET.get('model_id')
|
||||||
trims = models.CarTrim.objects.filter(
|
trims = models.CarTrim.objects.filter(
|
||||||
id_car_serie=serie_id, id_car_model=model_id).values('id_car_trim', 'name', 'arabic_name')
|
id_car_serie=serie_id).values('id_car_trim', 'name', 'arabic_name')
|
||||||
return JsonResponse(list(trims), safe=False)
|
return JsonResponse(list(trims), safe=False)
|
||||||
|
|
||||||
def get_specifications(self, request):
|
def get_specifications(self, request):
|
||||||
@ -403,6 +405,11 @@ class CarFinanceUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
|
|||||||
|
|
||||||
def get_success_url(self):
|
def get_success_url(self):
|
||||||
return reverse('car_detail', kwargs={'pk': self.object.car.pk})
|
return reverse('car_detail', kwargs={'pk': self.object.car.pk})
|
||||||
|
|
||||||
|
def get_form_kwargs(self):
|
||||||
|
kwargs = super().get_form_kwargs()
|
||||||
|
kwargs['instance'] = self.get_object()
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
|
||||||
class CarUpdateView(LoginRequiredMixin, SuccessMessageMixin,UpdateView):
|
class CarUpdateView(LoginRequiredMixin, SuccessMessageMixin,UpdateView):
|
||||||
@ -547,10 +554,11 @@ class DealerUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
|
|||||||
|
|
||||||
def get_form(self, form_class=None):
|
def get_form(self, form_class=None):
|
||||||
form = super().get_form(form_class)
|
form = super().get_form(form_class)
|
||||||
form.fields.pop('dealer_type')
|
if hasattr(form.fields, 'dealer_type'):
|
||||||
|
form.fields.pop('dealer_type')
|
||||||
return form
|
return form
|
||||||
def get_form_class(self):
|
def get_form_class(self):
|
||||||
if self.request.user.dealer.is_parent:
|
if self.request.user.dealer.dealer_type == 'Owner':
|
||||||
return forms.DealerForm
|
return forms.DealerForm
|
||||||
else:
|
else:
|
||||||
return forms.UserForm
|
return forms.UserForm
|
||||||
@ -662,8 +670,10 @@ class QuotationCreateView(LoginRequiredMixin,PermissionRequiredMixin, CreateView
|
|||||||
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
|
dealer = self.request.user.dealer.get_parent_or_self
|
||||||
|
form.instance.dealer = dealer
|
||||||
quotation = form.save()
|
quotation = form.save()
|
||||||
selected_cars = form.cleaned_data.get("cars")
|
selected_cars = form.cleaned_data.get("cars")
|
||||||
for car in selected_cars:
|
for car in selected_cars:
|
||||||
car_finance = car.finances
|
car_finance = car.finances
|
||||||
if car_finance:
|
if car_finance:
|
||||||
@ -686,7 +696,9 @@ class QuotationListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
status = self.request.GET.get("status")
|
status = self.request.GET.get("status")
|
||||||
queryset = models.SaleQuotation.objects.all()
|
# queryset = models.SaleQuotation.objects.all()
|
||||||
|
print(self.request.user.dealer.get_parent_or_self.sales.all())
|
||||||
|
queryset = self.request.user.dealer.get_parent_or_self.sales.all()
|
||||||
if status:
|
if status:
|
||||||
queryset = queryset.filter(status=status)
|
queryset = queryset.filter(status=status)
|
||||||
return queryset
|
return queryset
|
||||||
@ -697,56 +709,13 @@ class QuotationDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView
|
|||||||
template_name = "sales/quotation_detail.html"
|
template_name = "sales/quotation_detail.html"
|
||||||
context_object_name = "quotation"
|
context_object_name = "quotation"
|
||||||
permission_required = ('inventory.view_salequotation',)
|
permission_required = ('inventory.view_salequotation',)
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
quotation = self.object
|
quotation = self.object
|
||||||
|
|
||||||
# Totals Calculation
|
context_result = get_calculations(quotation)
|
||||||
context['vat_rate'] = settings.VAT_RATE
|
context.update(context_result)
|
||||||
context['total_sales_before_vat'] = sum(item.car.selling_price * item.quantity for item in quotation.quotation_cars.all())
|
|
||||||
context['vat_amount'] = sum(item.car.vat_amount * item.quantity for item in quotation.quotation_cars.all())
|
|
||||||
context['total_sales_after_vat'] = context['total_sales_before_vat'] + context['vat_amount']
|
|
||||||
|
|
||||||
# Additional Costs
|
|
||||||
total_quantity = quotation.total_quantity
|
|
||||||
context['administration_fee'] = sum(item.car.administration_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['transportation_fee'] = sum(item.car.transportation_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['custom_card_fee'] = sum(item.car.custom_card_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['registration_fee'] = sum(item.car.registration_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['administration_fee_vat'] = sum(item.car.administration_fee_vat for item in quotation.quotation_cars.all())
|
|
||||||
context['transportation_fee_vat'] = sum(item.car.transportation_fee_vat for item in quotation.quotation_cars.all())
|
|
||||||
context['custom_card_fee_vat'] = sum(item.car.custom_card_fee_vat for item in quotation.quotation_cars.all())
|
|
||||||
context['administration_fee_total'] = sum(item.car.administration_fee + context['administration_fee_vat'] for item in quotation.quotation_cars.all())
|
|
||||||
context['transportation_fee_total'] = sum(item.car.transportation_fee + context['transportation_fee_vat'] for item in quotation.quotation_cars.all())
|
|
||||||
context['custom_card_fee_total'] = sum(item.car.custom_card_fee + context['custom_card_fee_vat'] for item in quotation.quotation_cars.all())
|
|
||||||
context['registration_fee_total'] = sum(item.car.registration_fee * total_quantity for item in quotation.quotation_cars.all())
|
|
||||||
|
|
||||||
return context
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
quotation = self.object
|
|
||||||
|
|
||||||
# Totals Calculation
|
|
||||||
context['vat_rate'] = settings.VAT_RATE
|
|
||||||
context['total_sales_before_vat'] = sum(item.car.selling_price * item.quantity for item in quotation.quotation_cars.all())
|
|
||||||
context['vat_amount'] = sum(item.car.vat_amount * item.quantity for item in quotation.quotation_cars.all())
|
|
||||||
context['total_sales_after_vat'] = context['total_sales_before_vat'] + context['vat_amount']
|
|
||||||
|
|
||||||
# Additional Costs
|
|
||||||
total_quantity = quotation.total_quantity
|
|
||||||
context['administration_fee'] = sum(item.car.administration_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['transportation_fee'] = sum(item.car.transportation_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['custom_card_fee'] = sum(item.car.custom_card_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['registration_fee'] = sum(item.car.registration_fee for item in quotation.quotation_cars.all())
|
|
||||||
context['administration_fee_vat'] = sum(item.car.administration_fee_vat for item in quotation.quotation_cars.all())
|
|
||||||
context['transportation_fee_vat'] = sum(item.car.transportation_fee_vat for item in quotation.quotation_cars.all())
|
|
||||||
context['custom_card_fee_vat'] = sum(item.car.custom_card_fee_vat for item in quotation.quotation_cars.all())
|
|
||||||
context['administration_fee_total'] = sum(item.car.administration_fee + context['administration_fee_vat'] for item in quotation.quotation_cars.all())
|
|
||||||
context['transportation_fee_total'] = sum(item.car.transportation_fee + context['transportation_fee_vat'] for item in quotation.quotation_cars.all())
|
|
||||||
context['custom_card_fee_total'] = sum(item.car.custom_card_fee + context['custom_card_fee_vat'] for item in quotation.quotation_cars.all())
|
|
||||||
context['registration_fee_total'] = sum(item.car.registration_fee * total_quantity for item in quotation.quotation_cars.all())
|
|
||||||
|
|
||||||
return context
|
return context
|
||||||
|
|
||||||
@ -756,6 +725,8 @@ def confirm_quotation(request, pk):
|
|||||||
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
|
||||||
try:
|
try:
|
||||||
quotation.confirm()
|
quotation.confirm()
|
||||||
|
quotation_cars = quotation.quotation_cars.annotate(total_price=F('car__total') * F('quantity'))
|
||||||
|
total_amount = quotation_cars.aggregate(total=Sum('total_price'))['total']
|
||||||
models.SalesOrder.objects.create(
|
models.SalesOrder.objects.create(
|
||||||
quotation=quotation,
|
quotation=quotation,
|
||||||
total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"],
|
total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"],
|
||||||
@ -800,8 +771,8 @@ class UserListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
|
|||||||
class UserDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView):
|
class UserDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView):
|
||||||
model = models.Dealer
|
model = models.Dealer
|
||||||
template_name = "users/user_detail.html"
|
template_name = "users/user_detail.html"
|
||||||
context_object_name = "user"
|
context_object_name = "user_"
|
||||||
permission_required = ('inventory.view_dealer',)
|
permission_required = ('inventory.view_dealer',)
|
||||||
|
|
||||||
class UserCreateView(LoginRequiredMixin,PermissionRequiredMixin,SuccessMessageMixin,AddDealerInstanceMixin, CreateView):
|
class UserCreateView(LoginRequiredMixin,PermissionRequiredMixin,SuccessMessageMixin,AddDealerInstanceMixin, CreateView):
|
||||||
model = models.Dealer
|
model = models.Dealer
|
||||||
@ -890,23 +861,22 @@ class OrganizationDetailView(DetailView):
|
|||||||
context_object_name = 'organization'
|
context_object_name = 'organization'
|
||||||
|
|
||||||
|
|
||||||
class OrganizationCreateView(LoginRequiredMixin, CreateView):
|
class OrganizationCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView):
|
||||||
model = models.Organization
|
model = models.Organization
|
||||||
form_class = forms.OrganizationForm
|
form_class = forms.OrganizationForm
|
||||||
template_name = 'organizations/organization_form.html'
|
template_name = 'organizations/organization_form.html'
|
||||||
success_url = reverse_lazy('organization_list')
|
success_url = reverse_lazy('organization_list')
|
||||||
|
success_message = "Organization created successfully."
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.instance.dealer = self.request.user.dealer
|
form.instance.dealer = self.request.user.dealer.get_parent_or_self
|
||||||
form.save()
|
form.save()
|
||||||
success_message = "Organization created successfully."
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
else:
|
else:
|
||||||
return form.errors
|
return form.errors
|
||||||
|
|
||||||
|
|
||||||
class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
|
class OrganizationUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
|
||||||
model = models.Organization
|
model = models.Organization
|
||||||
form_class = forms.OrganizationForm
|
form_class = forms.OrganizationForm
|
||||||
template_name = 'organizations/organization_form.html'
|
template_name = 'organizations/organization_form.html'
|
||||||
@ -914,7 +884,7 @@ class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
success_message = "Organization updated successfully."
|
success_message = "Organization updated successfully."
|
||||||
|
|
||||||
|
|
||||||
class OrganizationDeleteView(LoginRequiredMixin, DeleteView):
|
class OrganizationDeleteView(LoginRequiredMixin,SuccessMessageMixin, DeleteView):
|
||||||
model = models.Organization
|
model = models.Organization
|
||||||
template_name = 'organizations/organization_confirm_delete.html'
|
template_name = 'organizations/organization_confirm_delete.html'
|
||||||
success_url = reverse_lazy('organization_list')
|
success_url = reverse_lazy('organization_list')
|
||||||
@ -926,30 +896,28 @@ class RepresentativeListView(LoginRequiredMixin, ListView):
|
|||||||
template_name = 'representatives/representative_list.html'
|
template_name = 'representatives/representative_list.html'
|
||||||
context_object_name = 'representatives'
|
context_object_name = 'representatives'
|
||||||
|
|
||||||
|
|
||||||
class RepresentativeDetailView(DetailView):
|
class RepresentativeDetailView(DetailView):
|
||||||
model = models.Representative
|
model = models.Representative
|
||||||
template_name = 'representatives/representative_detail.html'
|
template_name = 'representatives/representative_detail.html'
|
||||||
context_object_name = 'representative'
|
context_object_name = 'representative'
|
||||||
|
|
||||||
|
class RepresentativeCreateView(LoginRequiredMixin, SuccessMessageMixin,CreateView):
|
||||||
class RepresentativeCreateView(LoginRequiredMixin, CreateView):
|
|
||||||
model = models.Representative
|
model = models.Representative
|
||||||
form_class = forms.RepresentativeForm
|
form_class = forms.RepresentativeForm
|
||||||
template_name = 'representatives/representative_form.html'
|
template_name = 'representatives/representative_form.html'
|
||||||
success_url = reverse_lazy('representative_list')
|
success_url = reverse_lazy('representative_list')
|
||||||
|
success_message = "Representative created successfully."
|
||||||
|
|
||||||
def form_valid(self, form):
|
def form_valid(self, form):
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
form.instance.dealer = self.request.user.dealer
|
form.instance.dealer = self.request.user.dealer.get_parent_or_self
|
||||||
form.save()
|
form.save()
|
||||||
success_message = "Representative created successfully."
|
|
||||||
return super().form_valid(form)
|
return super().form_valid(form)
|
||||||
else:
|
else:
|
||||||
return form.errors
|
return form.errors
|
||||||
|
|
||||||
|
|
||||||
class RepresentativeUpdateView(LoginRequiredMixin, UpdateView):
|
class RepresentativeUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
|
||||||
model = models.Representative
|
model = models.Representative
|
||||||
form_class = forms.RepresentativeForm
|
form_class = forms.RepresentativeForm
|
||||||
template_name = 'representatives/representative_form.html'
|
template_name = 'representatives/representative_form.html'
|
||||||
@ -957,8 +925,8 @@ class RepresentativeUpdateView(LoginRequiredMixin, UpdateView):
|
|||||||
success_message = "Representative updated successfully."
|
success_message = "Representative updated successfully."
|
||||||
|
|
||||||
|
|
||||||
class RepresentativeDeleteView(LoginRequiredMixin, DeleteView):
|
class RepresentativeDeleteView(LoginRequiredMixin,SuccessMessageMixin, DeleteView):
|
||||||
model = models.Representative
|
model = models.Representative
|
||||||
template_name = 'representatives/representative_confirm_delete.html'
|
template_name = 'representatives/representative_confirm_delete.html'
|
||||||
success_url = reverse_lazy('representative_list')
|
success_url = reverse_lazy('representative_list')
|
||||||
success_message = "Representative deleted successfully."
|
success_message = "Representative deleted successfully."
|
||||||
@ -19,7 +19,7 @@
|
|||||||
<span class="navbar-toggler-icon"></span>
|
<span class="navbar-toggler-icon"></span>
|
||||||
</button>
|
</button>
|
||||||
<div class="collapse navbar-collapse" id="headerNav">
|
<div class="collapse navbar-collapse" id="headerNav">
|
||||||
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
||||||
{% if user.is_authenticated and user.dealer.pk %}
|
{% if user.is_authenticated and user.dealer.pk %}
|
||||||
<li class="nav-item">
|
<li class="nav-item">
|
||||||
{% block home %}<a class="nav-link" href="{% url 'landing_page' %}">{% trans 'home' %}</a>{% endblock %}
|
{% block home %}<a class="nav-link" href="{% url 'landing_page' %}">{% trans 'home' %}</a>{% endblock %}
|
||||||
|
|||||||
@ -62,7 +62,7 @@
|
|||||||
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
<div class="modal-dialog modal-lg modal-dialog-scrollable">
|
||||||
<div class="modal-content glossy-modal">
|
<div class="modal-content glossy-modal">
|
||||||
<div class="modal-header bg-primary text-light">
|
<div class="modal-header bg-primary text-light">
|
||||||
<h5 class="modal-title" id="specificationsModalLabel">
|
<h5 class="modal-title" id="specificationsModalLabel">
|
||||||
{% trans 'specifications'|upper %}
|
{% trans 'specifications'|upper %}
|
||||||
</h5>
|
</h5>
|
||||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
@ -227,27 +227,23 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Selling Price" %}</th>
|
<th>{% trans "Selling Price" %}</th>
|
||||||
<td>{{ car.finances.selling_price }}</td>
|
<td>{{ car.finances.selling_price }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td><small class="ms-5">{% trans "Administration Fee" %}</small></td>
|
|
||||||
<td><small>{{ car.finances.administration_fee }}</small></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><small class="ms-5">{% trans "Registration Fee" %}</small></td>
|
|
||||||
<td><small>{{ car.finances.registration_fee }}</small></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><small class="ms-5">{% trans "Transportation Fee" %}</small></td>
|
|
||||||
<td><small>{{ car.finances.transportation_fee }}</small></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td><small class="ms-5">{% trans "Custom Card Fee" %}</small></td>
|
|
||||||
<td><small>{{ car.finances.custom_card_fee }}</small></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Discount Amount" %}</th>
|
<th>{% trans "Discount Amount" %}</th>
|
||||||
<td>{{ car.finances.discount_amount }} -</td>
|
<td>{{ car.finances.discount_amount }} -</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Additional Fee" %}</th>
|
||||||
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% if car.finances.additional_services.first.pk %}
|
||||||
|
{% for service in car.finances.additional_services.all %}
|
||||||
|
<tr>
|
||||||
|
<td><small class="ms-5">{{service.name}}</small></td>
|
||||||
|
<td><small>{{ service.price }}</small></td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "VAT Amount" %}</th>
|
<th>{% trans "VAT Amount" %}</th>
|
||||||
<td>{{ car.finances.vat_amount }}</td>
|
<td>{{ car.finances.vat_amount }}</td>
|
||||||
|
|||||||
@ -624,8 +624,8 @@ async function loadSpecifications(trimId){
|
|||||||
/*stockTypeSelect.addEventListener('change', checkFormCompletion);
|
/*stockTypeSelect.addEventListener('change', checkFormCompletion);
|
||||||
mileageInput.addEventListener('input', checkFormCompletion);
|
mileageInput.addEventListener('input', checkFormCompletion);
|
||||||
remarksInput.addEventListener('input', checkFormCompletion);*/
|
remarksInput.addEventListener('input', checkFormCompletion);*/
|
||||||
makeSelect.addEventListener("change", (e) => {loadModels(e.target.value)})
|
makeSelect.addEventListener("change", (e) => {loadModels(e.target.value, modelSelect.value)})
|
||||||
modelSelect.addEventListener("change", (e) => {loadSeries(e.target.value)})
|
modelSelect.addEventListener("change", (e) => {loadSeries(e.target.value, yearSelect.value)})
|
||||||
decodeVinBtn.addEventListener('click', decodeVin);
|
decodeVinBtn.addEventListener('click', decodeVin);
|
||||||
});
|
});
|
||||||
const Toast = Swal.mixin({
|
const Toast = Swal.mixin({
|
||||||
|
|||||||
@ -65,12 +65,12 @@
|
|||||||
{% else %}
|
{% else %}
|
||||||
<td><span class="color-div">{% trans 'No Color' %}</span></td>
|
<td><span class="color-div">{% trans 'No Color' %}</span></td>
|
||||||
<td><span class="color-div">{% trans 'No Color' %}</span></td>
|
<td><span class="color-div">{% trans 'No Color' %}</span></td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if car.location.is_owner_showroom %}
|
{% if car.location.is_owner_showroom %}
|
||||||
<td> {% trans 'Our Showroom' %}</td>
|
<td> {% trans 'Our Showroom' %}</td>
|
||||||
{% else %}
|
{% else %}
|
||||||
<td>{{ car.location.showroom.get_local_name }}</td>
|
<td>{{ car.location.showroom.get_local_name }}</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success">
|
<a href="{% url 'car_detail' car.pk %}" class="btn btn-sm btn-success">
|
||||||
<small>{% trans "view" %}</small>
|
<small>{% trans "view" %}</small>
|
||||||
|
|||||||
@ -2,6 +2,34 @@
|
|||||||
{% load custom_filters %}
|
{% load custom_filters %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<!-- Custom Card Modal -->
|
||||||
|
<div class="modal fade" id="customCardModal" tabindex="-1" aria-labelledby="customCardModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-sm">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header bg-primary">
|
||||||
|
<h5 class="modal-title text-light" id="customCardModalLabel">{% trans 'Custom Card' %}</h5>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
{% trans 'Confirm Approve?' %}
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button"
|
||||||
|
class="btn btn-sm btn-danger"
|
||||||
|
data-bs-dismiss="modal">
|
||||||
|
{% trans 'No' %}
|
||||||
|
</button>
|
||||||
|
<form method="POST" action="{% url 'confirm_quotation' quotation.id %}" class="d-inline">
|
||||||
|
{% csrf_token %}
|
||||||
|
<button type="submit" class="btn btn-success btn-sm">{% trans "Yes" %}</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="container mt-4">
|
<div class="container mt-4">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
@ -45,9 +73,9 @@
|
|||||||
<td>{{ item.car.id_car_model.get_local_name }}</td>
|
<td>{{ item.car.id_car_model.get_local_name }}</td>
|
||||||
<td>{{ item.car.year }}</td>
|
<td>{{ item.car.year }}</td>
|
||||||
<td>{{ item.quantity }}</td>
|
<td>{{ item.quantity }}</td>
|
||||||
<td>{{ item.car.selling_price }}</td>
|
<td>{{ item.car.finances.selling_price }}</td>
|
||||||
<td>{{ item.car.vat_amount }}</td>
|
<td>{{ 0.15 }}</td>
|
||||||
<td>{{ item.car.total }}</td>
|
<td>{{ item.total }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
@ -68,40 +96,34 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>{% trans "Additions" %}</th>
|
<th>{% trans "Additions" %}</th>
|
||||||
<th>{% trans "Cost" %}</th>
|
<th>{% trans "Cost" %}</th>
|
||||||
<th>{% trans "VAT" %}</th>
|
<th>{% trans "VAT %" %}</th>
|
||||||
<th>{% trans "Total Cost with VAT" %}</th>
|
<th>{% trans "Total Cost with VAT" %}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
{% for service in services %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans "Administration Fee" %}</td>
|
<td>{{service.name}}</td>
|
||||||
<td>{{ administration_fee }}</td>
|
<td>{{ service.price }}</td>
|
||||||
<td>{{ administration_fee_vat }}</td>
|
<td>{{ service.vated }}</td>
|
||||||
<td>{{ administration_fee_total }}</td>
|
<td>{{ service.total_price_vat }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{% trans "Transportation Fee" %}</td>
|
<td></td>
|
||||||
<td>{{ transportation_fee }}</td>
|
<td>{{ total_cost }}</td>
|
||||||
<td>{{ transportation_fee_vat }}</td>
|
<td>{{ total_vat }}</td>
|
||||||
<td>{{ transportation_fee_total }}</td>
|
<td>{{ total_cost_vat }}</td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{% trans "Custom Card Fee" %}</td>
|
|
||||||
<td>{{ custom_card_fee }}</td>
|
|
||||||
<td>{{ custom_card_fee_vat }}</td>
|
|
||||||
<td>{{ custom_card_fee_total }}</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>{% trans "Registration Fee (No VAT)" %}</td>
|
|
||||||
<td>{{ registration_fee }}</td>
|
|
||||||
<td>{% trans "N/A" %}</td>
|
|
||||||
<td>{{ registration_fee_total }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer text-end">
|
<div class="card-footer text-end">
|
||||||
<a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Back to Quotations" %}</a>
|
<a href="{% url 'quotation_list' %}" class="btn btn-secondary">{% trans "Back to Quotations" %}</a>
|
||||||
|
{% if perms.inventory.change_carfinance and quotation.status == 'DRAFT' %}
|
||||||
|
<button class="btn btn-success" data-bs-toggle="modal" data-bs-target="#customCardModal">{% trans "Approve Quotation" %}</button>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -20,12 +20,22 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for quotation in quotations %}
|
{% for quotation in quotations %}
|
||||||
<tr>
|
<tr>
|
||||||
|
|
||||||
<td>{{ forloop.counter }}</td>
|
<td>{{ forloop.counter }}</td>
|
||||||
<td>{{ quotation.customer.get_full_name }}</td>
|
<td>{{ quotation.customer.get_full_name }}</td>
|
||||||
<td>{{ quotation.quotation_cars.count }}</td>
|
<td>{{ quotation.quotation_cars.count }}</td>
|
||||||
<td>{{ quotation.quotation_cars.get_financial_details.total_amount }}</td>
|
<td>{{ quotation.quotation_cars.get_financial_details.total_amount }}</td>
|
||||||
|
<td>
|
||||||
|
{% if quotation.status == 'DRAFT' %}
|
||||||
|
<span class="badge rounded-pill bg-light">{{ quotation.status }}</span>
|
||||||
|
{% elif quotation.status == 'PENDING' %}
|
||||||
|
<span class="badge rounded-pill bg-warning">{{ quotation.status }}</span>
|
||||||
|
{% elif quotation.status == 'CONFIRMED' %}
|
||||||
|
<span class="badge rounded-pill bg-success">{{ quotation.status }}</span>
|
||||||
|
{% elif quotation.status == 'CANCELED' %}
|
||||||
|
<span class="badge rounded-pill bg-danger">{{ quotation.status }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
<td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td>
|
<td>{{ quotation.created_at|date:"d/m/Y H:i" }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{% url 'quotation_detail' quotation.id %}" class="btn btn-sm btn-info">
|
<a href="{% url 'quotation_detail' quotation.id %}" class="btn btn-sm btn-info">
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block title %}{{ _("View Customer") }}{% endblock title %}
|
{% block title %}{{ _("View Customer") }}{% endblock title %}
|
||||||
@ -27,7 +28,7 @@
|
|||||||
</button>
|
</button>
|
||||||
<a type="button"
|
<a type="button"
|
||||||
class="btn btn-sm btn-danger"
|
class="btn btn-sm btn-danger"
|
||||||
href="{% url 'user_delete' user.id %}">
|
href="{% url 'user_delete' user_.id %}">
|
||||||
{% trans 'Yes' %}
|
{% trans 'Yes' %}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
@ -42,19 +43,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<p><strong>{{ _("Name") }}:</strong> {{ user.name }}</p>
|
<p><strong>{{ _("Name") }}:</strong> {{ user_.name }}</p>
|
||||||
<p><strong>{{ _("Arabic Name") }}:</strong> {{ user.arabic_name }}</p>
|
<p><strong>{{ _("Arabic Name") }}:</strong> {{ user_.arabic_name }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<p><strong>{{ _("Phone Number") }}:</strong> {{ user.phone_number }}</p>
|
<p><strong>{{ _("Phone Number") }}:</strong> {{ user_.phone_number }}</p>
|
||||||
<p><strong>{{ _("Address") }}:</strong> {{ user.address }}</p>
|
<p><strong>{{ _("Address") }}:</strong> {{ user_.address }}</p>
|
||||||
<p><strong>{{ _("Role") }}:</strong> {{ user.dealer_type }}</p>
|
<p><strong>{{ _("Role") }}:</strong> {{ user_.dealer_type }}</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-footer d-flex ">
|
<div class="card-footer d-flex ">
|
||||||
<a class="btn btn-sm btn-primary me-1" href="{% url 'user_update' user.id %}">
|
<a class="btn btn-sm btn-primary me-1" href="{% url 'user_update' user_.id %}">
|
||||||
<!--<i class="bi bi-pencil-square"></i> -->
|
<!--<i class="bi bi-pencil-square"></i> -->
|
||||||
{{ _("Edit") }}
|
{{ _("Edit") }}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@ -10,8 +10,7 @@
|
|||||||
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
|
<div class="d-flex flex-column flex-sm-grow-1 ms-sm-14 p-4">
|
||||||
<main class="d-grid gap-4 p-1">
|
<main class="d-grid gap-4 p-1">
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-lg-6 col-xl-12">
|
<div class="col-lg-6 col-xl-12">
|
||||||
|
|
||||||
<div class="container-fluid p-2">
|
<div class="container-fluid p-2">
|
||||||
<form method="get">
|
<form method="get">
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user