cleanup and refactoring

This commit is contained in:
gitea 2024-12-18 16:08:34 +00:00
parent 1238c5bac5
commit f0f9dfc65e
19 changed files with 365 additions and 186 deletions

View File

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

View File

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

View File

@ -115,7 +115,7 @@ class CarFinanceForm(AddClassMixin, forms.ModelForm):
class Meta:
model = CarFinance
exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate']
exclude = ['car', 'profit_margin', 'vat_amount', 'total', 'vat_rate','additional_services']
class CarLocationForm(forms.ModelForm):

View File

@ -1,14 +0,0 @@
# Generated by Django 4.2.17 on 2024-12-17 14:12
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('inventory', '0023_cartrim_id_car_model'),
('inventory', '0028_alter_dealer_options'),
]
operations = [
]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,6 +22,7 @@ from django.core.exceptions import ValidationError
from phonenumber_field.modelfields import PhoneNumberField
from django.contrib.contenttypes.models import ContentType
from django.utils.timezone import now
from .utilities.financials import get_financial_value, get_total, get_total_financials
from .mixins import LocalizedNameMixin
@ -140,6 +141,18 @@ class DEALER_TYPES(models.TextChoices):
Sales = "sales", _("Sales")
class AdditionalServices(models.Model):
name = models.CharField(max_length=255, verbose_name=_("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
class Car(models.Model):
vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
dealer = models.ForeignKey(
@ -217,90 +230,6 @@ class Car(models.Model):
active_reservations = self.reservations.filter(reserved_until__gt=now())
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):
# vin = models.CharField(max_length=17, unique=True, verbose_name=_("VIN"))
# make = models.CharField(max_length=255, verbose_name=_("Make"))
@ -328,7 +257,6 @@ class Car(models.Model):
# receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
class CarReservation(models.Model):
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"))
@ -344,9 +272,9 @@ class CarReservation(models.Model):
verbose_name = _("Car Reservation")
verbose_name_plural = _("Car Reservations")
# Car Finance Model
class CarFinance(models.Model):
additional_services = models.ManyToManyField(AdditionalServices, related_name="cars")
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"))
selling_price = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Selling Price"))
@ -357,7 +285,7 @@ class CarFinance(models.Model):
vat_amount = models.DecimalField(max_digits=14,
decimal_places=2,
verbose_name=_("Vat Amount"),
editable=False)
editable=False,default=Decimal('0.00'))
discount_amount = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Discount Amount"),
default=Decimal('0.00'))
registration_fee = models.DecimalField(max_digits=14, decimal_places=2, verbose_name=_("Registration Fee"),
@ -368,16 +296,20 @@ class CarFinance(models.Model):
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):
return f"Car: {self.car}, Selling Price: {self.selling_price}"
def save(self, *args, **kwargs):
# vat_rate = settings.VAT_RATE
def save(self, *args, **kwargs):
self.full_clean()
try:
price_after_discount = self.selling_price - self.discount_amount
self.profit_margin = price_after_discount - self.cost_price
self.vat_amount = settings.VAT_RATE
except InvalidOperation as e:
raise ValidationError(_("Invalid decimal operation: %s") % str(e))
super().save(*args, **kwargs)
@ -386,7 +318,6 @@ class CarFinance(models.Model):
verbose_name = _("Car Financial Details")
verbose_name_plural = _("Car Financial Details")
class ExteriorColors(models.Model, LocalizedNameMixin):
name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
@ -795,10 +726,9 @@ class SaleQuotationCar(models.Model):
"""
Calculate total price dynamically based on quantity and selling price.
"""
car_finance = self.car.finances
if not car_finance:
if not self.car.finances:
return Decimal("0.00")
return car_finance.selling_price * self.quantity
return self.car.finances.selling_price * self.quantity
def __str__(self):
return f"{self.car} - Quotation #{self.quotation.id}"

View File

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

View File

View File

@ -0,0 +1,28 @@
from decimal import Decimal
from django.conf import settings
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_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(instance,"registration_fee") +
get_financial_value(instance,"administration_fee",vat) +
get_financial_value(instance,"transportation_fee",vat) +
get_financial_value(instance,"custom_card_fee",vat))
return subtotal
def get_total(instance):
total = get_total_financials(instance)
total_vat = get_total_financials(instance,vat=True)
return total + total_vat

View File

@ -1,6 +1,9 @@
import requests
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from inventory.utilities.financials import get_financial_value
def get_jwt_token():
url = 'https://carapi.app/api/auth/login'
@ -29,3 +32,48 @@ def localize_some_words():
return None
def get_calculations(instance,quotation):
context = {}
context['vat_rate'] = settings.VAT_RATE
context['total_sales_before_vat'] = sum(item.car.finances.selling_price * item.quantity for item in quotation.quotation_cars.all())
context['vat_amount'] = sum(item.car.finances.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.finances.administration_fee for item in quotation.quotation_cars.all())
context['transportation_fee'] = sum(item.car.finances.transportation_fee for item in quotation.quotation_cars.all())
context['custom_card_fee'] = sum(item.car.finances.custom_card_fee for item in quotation.quotation_cars.all())
context['registration_fee'] = sum(item.car.finances.registration_fee for item in quotation.quotation_cars.all())
context['administration_fee_vat'] = sum(get_financial_value(item.car.finances,"administration_fee",True) for item in quotation.quotation_cars.all())
context['transportation_fee_vat'] = sum(get_financial_value(item.car.finances,"transportation_fee",True) for item in quotation.quotation_cars.all())
context['custom_card_fee_vat'] = sum(get_financial_value(item.car.finances,"custom_card_fee",True) for item in quotation.quotation_cars.all())
context['administration_fee_total'] = sum(item.car.finances.administration_fee + context['administration_fee_vat'] for item in quotation.quotation_cars.all())
context['transportation_fee_total'] = sum(item.car.finances.transportation_fee + context['transportation_fee_vat'] for item in quotation.quotation_cars.all())
context['custom_card_fee_total'] = sum(item.car.finances.custom_card_fee + context['custom_card_fee_vat'] for item in quotation.quotation_cars.all())
context['registration_fee_total'] = sum(item.car.finances.registration_fee * total_quantity for item in quotation.quotation_cars.all())
return context
# def get_calculations(quotation):
# context = {}
# 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

View File

@ -28,6 +28,7 @@ from django.contrib import messages
from django.db.models import Sum, F, Count
from inventory.mixins import AddDealerInstanceMixin
from inventory.utils import get_calculations
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 . import models, forms
@ -187,6 +188,7 @@ class AjaxHandlerView(LoginRequiredMixin, View):
def get_series(self, request):
model_id = request.GET.get('model_id')
year = request.GET.get('year')
# Validate inputs
if not model_id or not year:
@ -206,9 +208,9 @@ class AjaxHandlerView(LoginRequiredMixin, View):
def get_trims(self, request):
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(
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)
def get_specifications(self, request):
@ -663,8 +665,10 @@ class QuotationCreateView(LoginRequiredMixin,PermissionRequiredMixin, CreateView
def form_valid(self, form):
dealer = self.request.user.dealer.get_parent_or_self
form.instance.dealer = dealer
quotation = form.save()
selected_cars = form.cleaned_data.get("cars")
selected_cars = form.cleaned_data.get("cars")
for car in selected_cars:
car_finance = car.finances
if car_finance:
@ -687,7 +691,9 @@ class QuotationListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
def get_queryset(self):
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:
queryset = queryset.filter(status=status)
return queryset
@ -698,56 +704,14 @@ class QuotationDetailView(LoginRequiredMixin,PermissionRequiredMixin, DetailView
template_name = "sales/quotation_detail.html"
context_object_name = "quotation"
permission_required = ('inventory.view_salequotation',)
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
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())
# context_result = get_calculations(quotation)
context_result = get_calculations(self.object,quotation)
context.update(context_result)
return context
@ -757,6 +721,8 @@ def confirm_quotation(request, pk):
quotation = get_object_or_404(models.SaleQuotation, pk=pk)
try:
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(
quotation=quotation,
total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"],
@ -891,23 +857,22 @@ class OrganizationDetailView(DetailView):
context_object_name = 'organization'
class OrganizationCreateView(LoginRequiredMixin, CreateView):
class OrganizationCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView):
model = models.Organization
form_class = forms.OrganizationForm
template_name = 'organizations/organization_form.html'
success_url = reverse_lazy('organization_list')
success_message = "Organization created successfully."
def form_valid(self, form):
if form.is_valid():
form.instance.dealer = self.request.user.dealer
form.save()
success_message = "Organization created successfully."
form.instance.dealer = self.request.user.dealer.get_parent_or_self
form.save()
return super().form_valid(form)
else:
return form.errors
class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
class OrganizationUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
model = models.Organization
form_class = forms.OrganizationForm
template_name = 'organizations/organization_form.html'
@ -915,7 +880,7 @@ class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
success_message = "Organization updated successfully."
class OrganizationDeleteView(LoginRequiredMixin, DeleteView):
class OrganizationDeleteView(LoginRequiredMixin,SuccessMessageMixin, DeleteView):
model = models.Organization
template_name = 'organizations/organization_confirm_delete.html'
success_url = reverse_lazy('organization_list')
@ -927,30 +892,28 @@ class RepresentativeListView(LoginRequiredMixin, ListView):
template_name = 'representatives/representative_list.html'
context_object_name = 'representatives'
class RepresentativeDetailView(DetailView):
model = models.Representative
template_name = 'representatives/representative_detail.html'
context_object_name = 'representative'
class RepresentativeCreateView(LoginRequiredMixin, CreateView):
class RepresentativeCreateView(LoginRequiredMixin, SuccessMessageMixin,CreateView):
model = models.Representative
form_class = forms.RepresentativeForm
template_name = 'representatives/representative_form.html'
success_url = reverse_lazy('representative_list')
success_message = "Representative created successfully."
def form_valid(self, form):
if form.is_valid():
form.instance.dealer = self.request.user.dealer
form.instance.dealer = self.request.user.dealer.get_parent_or_self
form.save()
success_message = "Representative created successfully."
return super().form_valid(form)
else:
return form.errors
class RepresentativeUpdateView(LoginRequiredMixin, UpdateView):
class RepresentativeUpdateView(LoginRequiredMixin,SuccessMessageMixin, UpdateView):
model = models.Representative
form_class = forms.RepresentativeForm
template_name = 'representatives/representative_form.html'
@ -958,10 +921,8 @@ class RepresentativeUpdateView(LoginRequiredMixin, UpdateView):
success_message = "Representative updated successfully."
class RepresentativeDeleteView(LoginRequiredMixin, DeleteView):
class RepresentativeDeleteView(LoginRequiredMixin,SuccessMessageMixin, DeleteView):
model = models.Representative
template_name = 'representatives/representative_confirm_delete.html'
success_url = reverse_lazy('representative_list')
success_message = "Representative deleted successfully."
success_message = "Representative deleted successfully."

View File

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

View File

@ -2,6 +2,34 @@
{% load custom_filters %}
{% load i18n %}
{% 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="card">
<div class="card-header">
@ -45,7 +73,7 @@
<td>{{ item.car.id_car_model.get_local_name }}</td>
<td>{{ item.car.year }}</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>{{ item.car.total }}</td>
</tr>
@ -102,6 +130,9 @@
</div>
<div class="card-footer text-end">
<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>

View File

@ -20,12 +20,22 @@
</thead>
<tbody>
{% for quotation in quotations %}
<tr>
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ quotation.customer.get_full_name }}</td>
<td>{{ quotation.quotation_cars.count }}</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>
<a href="{% url 'quotation_detail' quotation.id %}" class="btn btn-sm btn-info">