Merge branch 'main' of http://10.10.1.136:3000/ismail/haikal into frontend

This commit is contained in:
Faheedkhan 2025-08-12 12:05:11 +03:00
commit 5ff9b4c573
19 changed files with 1007 additions and 691 deletions

View File

@ -38,7 +38,7 @@ admin.site.register(models.DealerSettings)
# admin.site.register(models.SaleQuotationCar)
admin.site.register(models.SaleOrder)
admin.site.register(models.CustomGroup)
admin.site.register(models.CarFinance)
# admin.site.register(models.CarFinance)
admin.site.register(models.CarColors)
admin.site.register(models.CarRegistration)
admin.site.register(models.CustomCard)

View File

@ -32,7 +32,7 @@ from .models import (
Car,
VatRate,
CarTransfer,
CarFinance,
# CarFinance,
CustomCard,
CarRegistration,
CarColors,
@ -444,7 +444,7 @@ class CarFinanceForm(forms.ModelForm):
return cleaned_data
class Meta:
model = CarFinance
model = Car
fields = ["cost_price","marked_price"]

View File

@ -603,7 +603,6 @@ class AdditionalServices(models.Model, LocalizedNameMixin):
class Car(Base):
item_model = models.OneToOneField(
ItemModel,
models.DO_NOTHING,
@ -668,6 +667,32 @@ class Car(Base):
default=CarStockTypeChoices.NEW,
verbose_name=_("Stock Type"),
)
#
additional_services = models.ManyToManyField(
AdditionalServices, related_name="additionals", blank=True,null=True
)
cost_price = models.DecimalField(
max_digits=14, decimal_places=2, verbose_name=_("Cost Price"),default=Decimal("0.00")
)
selling_price = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Selling Price"),
default=Decimal("0.00"),
)
marked_price = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Marked Price"),
default=Decimal("0.00"),
)
discount_amount = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Discount Amount"),
default=Decimal("0.00"),
)
#
remarks = models.TextField(blank=True, null=True, verbose_name=_("Remarks"))
mileage = models.IntegerField(blank=True, null=True, verbose_name=_("Mileage"))
receiving_date = models.DateTimeField(verbose_name=_("Receiving Date"))
@ -734,20 +759,16 @@ class Car(Base):
@property
def logo(self):
return getattr(self.id_car_make, "logo", "")
@property
def additional_services(self):
return self.finances.additional_services.all()
@property
def total_additional_services(self):
return sum([service.price_ for service in self.additional_services])
# @property
# def additional_services(self):
# return self.additional_services.all()
@property
def ready(self):
try:
return all(
[
self.colors,
self.finances,
self.finances.marked_price > 0,
self.marked_price > 0,
]
)
except Exception:
@ -838,10 +859,51 @@ class Car(Base):
car=self, exterior=exterior, interior=interior
)
self.save()
@property
def logo(self):
return self.id_car_make.logo.url if self.id_car_make.logo else None
#
@property
def get_additional_services_amount(self):
return sum([Decimal(x.price_) for x in self.additional_services.all()])
def get_additional_services(self):
return {"services": [x for x in self.additional_services.all()],"total":self.get_additional_services_amount}
@property
def vat_amount(self):
vat = VatRate.objects.filter(dealer=self.dealer,is_active=True).first()
return Decimal(self.marked_price) * (vat.rate / 100)
@property
def total_vat(self):
return Decimal(self.marked_price) + Decimal(self.vat_amount)
# def get_discount_amount(self,estimate,user):
# try:
# instance = models.ExtraInfo.objects.get(
# dealer=self.dealer,
# content_object=estimate,
# related_object=user
# )
# if instance:
# return instance.data.get("discount",0)
# return 0
# except Exception:
# print("Error getting discount amount")
# return 0
# @property
# def total_discount(self):
# if self.discount_amount > 0:
# return self.marked_price - self.discount_amount
# return self.marked_price
# @property
# def total_vat(self):
# return round(self.total_discount + self.vat_amount + self.total_additionals, 2)
class CarTransfer(models.Model):
@ -880,7 +942,7 @@ class CarTransfer(models.Model):
@property
def total_price(self):
return self.quantity * self.car.finances.total_vat
return self.quantity * self.car.total_vat # TODO : check later
class Meta:
verbose_name = _("Car Transfer Log")
@ -924,105 +986,105 @@ class CarReservation(models.Model):
# Car Finance 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")
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"),
default=Decimal("0.00"),
)
marked_price = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Marked Price"),
default=Decimal("0.00"),
)
discount_amount = models.DecimalField(
max_digits=14,
decimal_places=2,
verbose_name=_("Discount Amount"),
default=Decimal("0.00"),
)
# is_sold = models.BooleanField(default=False)
# 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")
# 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"),
# default=Decimal("0.00"),
# )
# marked_price = models.DecimalField(
# max_digits=14,
# decimal_places=2,
# verbose_name=_("Marked Price"),
# default=Decimal("0.00"),
# )
# discount_amount = models.DecimalField(
# max_digits=14,
# decimal_places=2,
# verbose_name=_("Discount Amount"),
# default=Decimal("0.00"),
# )
# # is_sold = models.BooleanField(default=False)
@property
def total(self):
return self.marked_price
# @property
# def total(self):
# return self.marked_price
@property
def total_additionals_no_vat(self):
return sum(x.price for x in self.additional_services.all())
# @property
# def total_additionals_no_vat(self):
# return sum(x.price for x in self.additional_services.all())
@property
def total_additionals(self):
return sum(x.price_ for x in self.additional_services.all())
# @property
# def total_additionals(self):
# return sum(x.price_ for x in self.additional_services.all())
@property
def total_discount(self):
if self.discount_amount > 0:
return self.marked_price - self.discount_amount
return self.marked_price
# @property
# def total_discount(self):
# if self.discount_amount > 0:
# return self.marked_price - self.discount_amount
# return self.marked_price
@property
def total_vat(self):
return round(self.total_discount + self.vat_amount + self.total_additionals, 2)
# @property
# def total_vat(self):
# return round(self.total_discount + self.vat_amount + self.total_additionals, 2)
@property
def vat_amount(self):
vat = VatRate.objects.filter(dealer=self.car.dealer, is_active=True).first()
if vat:
return (self.total_discount * Decimal(vat.rate)).quantize(Decimal("0.01"))
return Decimal("0.00")
# @property
# def vat_amount(self):
# vat = VatRate.objects.filter(dealer=self.car.dealer, is_active=True).first()
# if vat:
# return (self.total_discount * Decimal(vat.rate)).quantize(Decimal("0.01"))
# return Decimal("0.00")
@property
def revenue(self):
return self.marked_price - self.cost_price
# @property
# def revenue(self):
# return self.marked_price - self.cost_price
def to_dict(self):
return {
"cost_price": str(self.cost_price),
"selling_price": str(self.selling_price),
"marked_price": str(self.marked_price),
"discount_amount": str(self.discount_amount),
"total": str(self.total),
"total_discount": str(self.total_discount),
"total_vat": str(self.total_vat),
"vat_amount": str(self.vat_amount),
}
# def to_dict(self):
# return {
# "cost_price": str(self.cost_price),
# "selling_price": str(self.selling_price),
# "marked_price": str(self.marked_price),
# "discount_amount": str(self.discount_amount),
# "total": str(self.total),
# "total_discount": str(self.total_discount),
# "total_vat": str(self.total_vat),
# "vat_amount": str(self.vat_amount),
# }
def __str__(self):
return f"Car: {self.car}, Marked Price: {self.marked_price}"
# def __str__(self):
# return f"Car: {self.car}, Marked Price: {self.marked_price}"
# 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)
# # 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)
class Meta:
verbose_name = _("Car Financial Details")
verbose_name_plural = _("Car Financial Details")
indexes = [
models.Index(fields=["car"], name="car_finance_car_idx"),
models.Index(fields=["cost_price"], name="car_finance_cost_price_idx"),
models.Index(
fields=["selling_price"], name="car_finance_selling_price_idx"
),
models.Index(fields=["marked_price"], name="car_finance_marked_price_idx"),
models.Index(fields=["discount_amount"], name="car_finance_discount_idx"),
]
# class Meta:
# verbose_name = _("Car Financial Details")
# verbose_name_plural = _("Car Financial Details")
# indexes = [
# models.Index(fields=["car"], name="car_finance_car_idx"),
# models.Index(fields=["cost_price"], name="car_finance_cost_price_idx"),
# models.Index(
# fields=["selling_price"], name="car_finance_selling_price_idx"
# ),
# models.Index(fields=["marked_price"], name="car_finance_marked_price_idx"),
# models.Index(fields=["discount_amount"], name="car_finance_discount_idx"),
# ]
class ExteriorColors(models.Model, LocalizedNameMixin):
@ -2875,7 +2937,7 @@ class SaleOrder(models.Model):
@property
def price(self):
return self.car.finances.marked_price
return self.car.marked_price
@property
def items(self):

View File

@ -270,6 +270,9 @@ def create_item_model(sender, instance, created, **kwargs):
)
instance.item_model = inventory
inventory.save()
else:
instance.item_model.default_amount = instance.marked_price
# inventory = entity.create_item_inventory(
# name=instance.vin,
# uom_model=uom,
@ -284,108 +287,108 @@ def create_item_model(sender, instance, created, **kwargs):
# # update price - CarFinance
@receiver(post_save, sender=models.CarFinance)
def update_item_model_cost(sender, instance, created, **kwargs):
"""
Signal handler for updating an inventory item's cost and additional information
when a CarFinance instance is saved. This function updates the corresponding
inventory item of the car dealer's entity associated with the car's VIN by
modifying its default amount and updating additional data fields.
# @receiver(post_save, sender=models.CarFinance)
# def update_item_model_cost(sender, instance, created, **kwargs):
# """
# Signal handler for updating an inventory item's cost and additional information
# when a CarFinance instance is saved. This function updates the corresponding
# inventory item of the car dealer's entity associated with the car's VIN by
# modifying its default amount and updating additional data fields.
:param sender: The model class that triggered the signal.
:param instance: The instance of the CarFinance that was saved.
:param created: A boolean indicating whether the model instance was newly created.
:param kwargs: Additional keyword arguments passed during the signal invocation.
:return: None
"""
# if created and not instance.is_sold:
# if created:
# entity = instance.car.dealer.entity
# coa = entity.get_default_coa()
# inventory_account = (
# entity.get_all_accounts()
# .filter(name=f"Inventory:{instance.car.id_car_make.name}")
# .first()
# )
# if not inventory_account:
# inventory_account = create_make_accounts(
# entity,
# coa,
# [instance.car.id_car_make],
# "Inventory",
# roles.ASSET_CA_INVENTORY,
# "debit",
# )
# :param sender: The model class that triggered the signal.
# :param instance: The instance of the CarFinance that was saved.
# :param created: A boolean indicating whether the model instance was newly created.
# :param kwargs: Additional keyword arguments passed during the signal invocation.
# :return: None
# """
# # if created and not instance.is_sold:
# # if created:
# # entity = instance.car.dealer.entity
# # coa = entity.get_default_coa()
# # inventory_account = (
# # entity.get_all_accounts()
# # .filter(name=f"Inventory:{instance.car.id_car_make.name}")
# # .first()
# # )
# # if not inventory_account:
# # inventory_account = create_make_accounts(
# # entity,
# # coa,
# # [instance.car.id_car_make],
# # "Inventory",
# # roles.ASSET_CA_INVENTORY,
# # "debit",
# # )
# cogs = (
# entity.get_all_accounts()
# .filter(name=f"Cogs:{instance.car.id_car_make.name}")
# .first()
# )
# if not cogs:
# cogs = create_make_accounts(
# entity, coa, [instance.car.id_car_make], "Cogs", roles.COGS, "debit"
# )
# revenue = (
# entity.get_all_accounts()
# .filter(name=f"Revenue:{instance.car.id_car_make.name}")
# .first()
# )
# if not revenue:
# revenue = create_make_accounts(
# entity,
# coa,
# [instance.car.id_car_make],
# "Revenue",
# roles.ASSET_CA_RECEIVABLES,
# "credit",
# )
# # cogs = (
# # entity.get_all_accounts()
# # .filter(name=f"Cogs:{instance.car.id_car_make.name}")
# # .first()
# # )
# # if not cogs:
# # cogs = create_make_accounts(
# # entity, coa, [instance.car.id_car_make], "Cogs", roles.COGS, "debit"
# # )
# # revenue = (
# # entity.get_all_accounts()
# # .filter(name=f"Revenue:{instance.car.id_car_make.name}")
# # .first()
# # )
# # if not revenue:
# # revenue = create_make_accounts(
# # entity,
# # coa,
# # [instance.car.id_car_make],
# # "Revenue",
# # roles.ASSET_CA_RECEIVABLES,
# # "credit",
# # )
# cash_account = (
# # entity.get_all_accounts()
# # .filter(name="Cash", role=roles.ASSET_CA_CASH)
# # .first()
# entity.get_all_accounts()
# .filter(role=roles.ASSET_CA_CASH, role_default=True)
# .first()
# )
# # cash_account = (
# # # entity.get_all_accounts()
# # # .filter(name="Cash", role=roles.ASSET_CA_CASH)
# # # .first()
# # entity.get_all_accounts()
# # .filter(role=roles.ASSET_CA_CASH, role_default=True)
# # .first()
# # )
# ledger = LedgerModel.objects.create(
# entity=entity, name=f"Inventory Purchase - {instance.car}"
# )
# je = JournalEntryModel.objects.create(
# ledger=ledger,
# description=f"Acquired {instance.car} for inventory",
# )
# TransactionModel.objects.create(
# journal_entry=je,
# account=inventory_account,
# amount=Decimal(instance.cost_price),
# tx_type="debit",
# description="",
# )
# # ledger = LedgerModel.objects.create(
# # entity=entity, name=f"Inventory Purchase - {instance.car}"
# # )
# # je = JournalEntryModel.objects.create(
# # ledger=ledger,
# # description=f"Acquired {instance.car} for inventory",
# # )
# # TransactionModel.objects.create(
# # journal_entry=je,
# # account=inventory_account,
# # amount=Decimal(instance.cost_price),
# # tx_type="debit",
# # description="",
# # )
# TransactionModel.objects.create(
# journal_entry=je,
# account=cash_account,
# amount=Decimal(instance.cost_price),
# tx_type="credit",
# description="",
# )
# # TransactionModel.objects.create(
# # journal_entry=je,
# # account=cash_account,
# # amount=Decimal(instance.cost_price),
# # tx_type="credit",
# # description="",
# # )
instance.car.item_model.default_amount = instance.marked_price
# if not isinstance(instance.car.item_model.additional_info, dict):
# instance.car.item_model.additional_info = {}
# instance.car.item_model.additional_info.update({"car_finance": instance.to_dict()})
# instance.car.item_model.additional_info.update(
# {
# "additional_services": [
# service.to_dict() for service in instance.additional_services.all()
# ]
# }
# )
instance.car.item_model.save()
print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
# instance.car.item_model.default_amount = instance.marked_price
# # if not isinstance(instance.car.item_model.additional_info, dict):
# # instance.car.item_model.additional_info = {}
# # instance.car.item_model.additional_info.update({"car_finance": instance.to_dict()})
# # instance.car.item_model.additional_info.update(
# # {
# # "additional_services": [
# # service.to_dict() for service in instance.additional_services.all()
# # ]
# # }
# # )
# instance.car.item_model.save()
# print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
# @receiver(pre_save, sender=models.SaleQuotation)
@ -803,144 +806,144 @@ def create_dealer_settings(sender, instance, created, **kwargs):
# )
def save_journal(car_finance, ledger, vendor):
"""
Saves a journal entry pertaining to a car finance transaction for a specific ledger and vendor.
# def save_journal(car_finance, ledger, vendor):
# """
# Saves a journal entry pertaining to a car finance transaction for a specific ledger and vendor.
This function ensures that relevant accounts are updated to record financial transactions. It handles
debiting of the inventory account and crediting of the vendor account to maintain accurate bookkeeping.
Additionally, it creates vendor accounts dynamically if required and ties the created journal entry to
the ledger passed as a parameter. All transactions adhere to the ledger's entity-specific Chart of
Accounts (COA) configuration.
# This function ensures that relevant accounts are updated to record financial transactions. It handles
# debiting of the inventory account and crediting of the vendor account to maintain accurate bookkeeping.
# Additionally, it creates vendor accounts dynamically if required and ties the created journal entry to
# the ledger passed as a parameter. All transactions adhere to the ledger's entity-specific Chart of
# Accounts (COA) configuration.
:param car_finance: Instance of the car finance object containing details about the financed car
and its associated costs.
:type car_finance: `CarFinance` object
:param ledger: Ledger instance to which the journal entry is tied. This ledger must provide
entity-specific details, including its COA and related accounts.
:type ledger: `Ledger` object
:param vendor: Vendor instance representing the supplier or vendor related to the car finance
transaction. This vendor is used to derive or create the vendor account in COA.
:type vendor: `Vendor` object
# :param car_finance: Instance of the car finance object containing details about the financed car
# and its associated costs.
# :type car_finance: `CarFinance` object
# :param ledger: Ledger instance to which the journal entry is tied. This ledger must provide
# entity-specific details, including its COA and related accounts.
# :type ledger: `Ledger` object
# :param vendor: Vendor instance representing the supplier or vendor related to the car finance
# transaction. This vendor is used to derive or create the vendor account in COA.
# :type vendor: `Vendor` object
:return: None
"""
entity = ledger.entity
coa = entity.get_default_coa()
journal = JournalEntryModel.objects.create(
posted=False,
description=f"Finances of Car:{car_finance.car.vin} for Vendor:{car_finance.car.vendor.name}",
ledger=ledger,
locked=False,
origin="Payment",
)
ledger.additional_info["je_number"] = journal.je_number
ledger.save()
# :return: None
# """
# entity = ledger.entity
# coa = entity.get_default_coa()
# journal = JournalEntryModel.objects.create(
# posted=False,
# description=f"Finances of Car:{car_finance.car.vin} for Vendor:{car_finance.car.vendor.name}",
# ledger=ledger,
# locked=False,
# origin="Payment",
# )
# ledger.additional_info["je_number"] = journal.je_number
# ledger.save()
inventory_account = (
entity.get_default_coa_accounts().filter(role=roles.ASSET_CA_INVENTORY).first()
)
vendor_account = entity.get_default_coa_accounts().filter(name=vendor.name).first()
# inventory_account = (
# entity.get_default_coa_accounts().filter(role=roles.ASSET_CA_INVENTORY).first()
# )
# vendor_account = entity.get_default_coa_accounts().filter(name=vendor.name).first()
if not vendor_account:
last_account = (
entity.get_all_accounts()
.filter(role=roles.LIABILITY_CL_ACC_PAYABLE)
.order_by("-created")
.first()
)
if len(last_account.code) == 4:
code = f"{int(last_account.code)}{1:03d}"
elif len(last_account.code) > 4:
code = f"{int(last_account.code) + 1}"
# if not vendor_account:
# last_account = (
# entity.get_all_accounts()
# .filter(role=roles.LIABILITY_CL_ACC_PAYABLE)
# .order_by("-created")
# .first()
# )
# if len(last_account.code) == 4:
# code = f"{int(last_account.code)}{1:03d}"
# elif len(last_account.code) > 4:
# code = f"{int(last_account.code) + 1}"
vendor_account = entity.create_account(
name=vendor.name,
code=code,
role=roles.LIABILITY_CL_ACC_PAYABLE,
coa_model=coa,
balance_type="credit",
active=True,
)
additional_services_account = (
entity.get_default_coa_accounts()
.filter(name="Additional Services", role=roles.COGS)
.first()
)
# vendor_account = entity.create_account(
# name=vendor.name,
# code=code,
# role=roles.LIABILITY_CL_ACC_PAYABLE,
# coa_model=coa,
# balance_type="credit",
# active=True,
# )
# additional_services_account = (
# entity.get_default_coa_accounts()
# .filter(name="Additional Services", role=roles.COGS)
# .first()
# )
# Debit Inventory Account
TransactionModel.objects.create(
journal_entry=journal,
account=inventory_account,
amount=car_finance.cost_price,
tx_type="debit",
)
# # Debit Inventory Account
# TransactionModel.objects.create(
# journal_entry=journal,
# account=inventory_account,
# amount=car_finance.cost_price,
# tx_type="debit",
# )
# Credit Vendor Account
TransactionModel.objects.create(
journal_entry=journal,
account=vendor_account,
amount=car_finance.cost_price,
tx_type="credit",
)
# # Credit Vendor Account
# TransactionModel.objects.create(
# journal_entry=journal,
# account=vendor_account,
# amount=car_finance.cost_price,
# tx_type="credit",
# )
@receiver(post_save, sender=models.CarFinance)
def update_finance_cost(sender, instance, created, **kwargs):
"""
Signal to handle `post_save` functionality for the `CarFinance` model. This function
creates or updates financial records related to a car's finance details in the ledger
associated with the car's vendor and dealer. For newly created instances, a ledger is
created or retrieved, and the `save_journal` function is executed to log the financial
transactions.
# @receiver(post_save, sender=models.CarFinance)
# def update_finance_cost(sender, instance, created, **kwargs):
# """
# Signal to handle `post_save` functionality for the `CarFinance` model. This function
# creates or updates financial records related to a car's finance details in the ledger
# associated with the car's vendor and dealer. For newly created instances, a ledger is
# created or retrieved, and the `save_journal` function is executed to log the financial
# transactions.
This function also has commented-out logic to handle updates for already created
instances, including journal updates or adjustments to existing financial transactions.
# This function also has commented-out logic to handle updates for already created
# instances, including journal updates or adjustments to existing financial transactions.
:param sender: Model class that triggered the signal
:type sender: Model
:param instance: Instance of the `CarFinance` model passed to the signal
:type instance: CarFinance
:param created: Boolean value indicating if the instance was created (`True`) or updated (`False`)
:type created: bool
:param kwargs: Arbitrary keyword arguments passed to the signal
:type kwargs: dict
:return: None
"""
if created:
entity = instance.car.dealer.entity
vendor = instance.car.vendor
vin = instance.car.vin if instance.car.vin else ""
make = instance.car.id_car_make.name if instance.car.id_car_make else ""
model = instance.car.id_car_model.name if instance.car.id_car_model else ""
year = instance.car.year
vendor_name = vendor.name if vendor else ""
# :param sender: Model class that triggered the signal
# :type sender: Model
# :param instance: Instance of the `CarFinance` model passed to the signal
# :type instance: CarFinance
# :param created: Boolean value indicating if the instance was created (`True`) or updated (`False`)
# :type created: bool
# :param kwargs: Arbitrary keyword arguments passed to the signal
# :type kwargs: dict
# :return: None
# """
# if created:
# entity = instance.car.dealer.entity
# vendor = instance.car.vendor
# vin = instance.car.vin if instance.car.vin else ""
# make = instance.car.id_car_make.name if instance.car.id_car_make else ""
# model = instance.car.id_car_model.name if instance.car.id_car_model else ""
# year = instance.car.year
# vendor_name = vendor.name if vendor else ""
name = f"{vin}-{make}-{model}-{year}-{vendor_name}"
ledger, _ = LedgerModel.objects.get_or_create(name=name, entity=entity)
save_journal(instance, ledger, vendor)
# name = f"{vin}-{make}-{model}-{year}-{vendor_name}"
# ledger, _ = LedgerModel.objects.get_or_create(name=name, entity=entity)
# save_journal(instance, ledger, vendor)
# if not created:
# if ledger.additional_info.get("je_number"):
# journal = JournalEntryModel.objects.filter(je_number=ledger.additional_info.get("je_number")).first()
# journal.description = f"Finances of Car:{instance.car.vin} for Vendor:{instance.car.vendor.vendor_name}"
# journal.save()
# debit = journal.get_transaction_queryset().filter(tx_type='debit').first()
# credit = journal.get_transaction_queryset().filter(tx_type='credit').first()
# if debit and credit:
# if journal.is_locked():
# journal.mark_as_unlocked()
# journal.save()
# debit.amount = instance.cost_price
# credit.amount = instance.cost_price
# debit.save()
# credit.save()
# else:
# save_journal(instance,ledger,vendor,journal=journal)
# else:
# save_journal(instance,ledger,vendor)
# else:
# save_journal(instance,ledger,vendor)
# # if not created:
# # if ledger.additional_info.get("je_number"):
# # journal = JournalEntryModel.objects.filter(je_number=ledger.additional_info.get("je_number")).first()
# # journal.description = f"Finances of Car:{instance.car.vin} for Vendor:{instance.car.vendor.vendor_name}"
# # journal.save()
# # debit = journal.get_transaction_queryset().filter(tx_type='debit').first()
# # credit = journal.get_transaction_queryset().filter(tx_type='credit').first()
# # if debit and credit:
# # if journal.is_locked():
# # journal.mark_as_unlocked()
# # journal.save()
# # debit.amount = instance.cost_price
# # credit.amount = instance.cost_price
# # debit.save()
# # credit.save()
# # else:
# # save_journal(instance,ledger,vendor,journal=journal)
# # else:
# # save_journal(instance,ledger,vendor)
# # else:
# # save_journal(instance,ledger,vendor)
@receiver(post_save, sender=PurchaseOrderModel)

View File

@ -348,13 +348,8 @@ urlpatterns = [
name="car_delete",
),
path(
"<slug:dealer_slug>/cars/<slug:slug>/finance/create/",
views.CarFinanceCreateView.as_view(),
name="car_finance_create",
),
path(
"<slug:dealer_slug>/cars/finance/<int:pk>/update/",
views.CarFinanceUpdateView.as_view(),
"<slug:dealer_slug>/cars/<slug:slug>/finance/update/",
views.CarFinanceUpdateView,
name="car_finance_update",
),
path(
@ -1094,6 +1089,10 @@ urlpatterns = [
views.MonthlyIncomeStatementView.as_view(),
name="entity-ic-date",
),
# Chart of Accounts...
path('<slug:dealer_slug>/chart-of-accounts/<slug:entity_slug>/list/',
views.ChartOfAccountModelListView.as_view(),
name='coa-list'),
# CASH FLOW STATEMENTS...
# Entities...
path(

View File

@ -779,7 +779,6 @@ class CarTransfer:
)
self.bill.additional_info = {}
self.bill.additional_info.update({"car_info": self.car.to_dict()})
self.bill.additional_info.update({"car_finance": self.car.finances.to_dict()})
self.bill.mark_as_review()
self.bill.mark_as_approved(
@ -791,14 +790,14 @@ class CarTransfer:
self.car.dealer = self.to_dealer
self.car.vendor = self.vendor
self.car.receiving_date = datetime.datetime.now()
self.car.finances.additional_services.clear()
self.car.additional_services.clear()
if hasattr(self.car, "custom_cards"):
self.car.custom_cards.delete()
self.car.finances.cost_price = self.transfer.total_price
self.car.finances.marked_price = 0
self.car.finances.discount_amount = 0
self.car.finances.save()
self.car.cost_price = self.transfer.total_price
self.car.marked_price = 0
self.car.discount_amount = 0
self.car.save()
self.car.location.owner = self.to_dealer
self.car.location.showroom = self.to_dealer
self.car.location.description = ""
@ -920,7 +919,7 @@ class CarTransfer:
# )
# bill.additional_info.update({"car_info": car.to_dict()})
# bill.additional_info.update({"car_finance": car.finances.to_dict()})
# bill.additional_info.update({"car_finance": car.to_dict()})
# bill.mark_as_review()
# bill.mark_as_approved(to_dealer.entity.slug, to_dealer.entity.admin)
@ -929,14 +928,14 @@ class CarTransfer:
# car.dealer = to_dealer
# car.vendor = vendor
# car.receiving_date = datetime.datetime.now()
# car.finances.additional_services.clear()
# car.additional_services.clear()
# if hasattr(car, "custom_cards"):
# car.custom_cards.delete()
# car.finances.cost_price = transfer.total_price
# car.finances.selling_price = 0
# car.finances.discount_amount = 0
# car.finances.save()
# car.cost_price = transfer.total_price
# car.selling_price = 0
# car.discount_amount = 0
# car.save()
# car.location.owner = to_dealer
# car.location.showroom = to_dealer
# car.location.description = ""
@ -980,6 +979,153 @@ def to_dict(obj):
return obj_dict
class CarFinanceCalculator1:
"""
Class responsible for calculating car financing details.
This class provides methods and attributes required for calculating various
aspects related to car financing, such as VAT calculation, pricing, discounts,
and additional services. It processes data about cars, computes totals (e.g.,
price, VAT, discounts), and aggregates the financial data for reporting or
further processing.
:ivar model: The data model passed to the calculator for retrieving transaction data.
:type model: Any
:ivar vat_rate: The current active VAT rate retrieved from the database.
:type vat_rate: Decimal
:ivar item_transactions: A collection of item transactions retrieved from the model.
:type item_transactions: list
:ivar additional_services: A list of additional services with details (e.g., name, price, taxable status).
:type additional_services: list
"""
VAT_OBJ_NAME = "vat_rate"
CAR_FINANCE_KEY = "car_finance"
CAR_INFO_KEY = "car_info"
ADDITIONAL_SERVICES_KEY = "additional_services"
def __init__(self, model):
if isinstance(model, InvoiceModel):
self.dealer = models.Dealer.objects.get(entity=model.ce_model.entity)
self.extra_info = models.ExtraInfo.objects.get(
dealer=self.dealer,
content_type=ContentType.objects.get_for_model(model.ce_model),
object_id=model.ce_model.pk,
)
elif isinstance(model, EstimateModel):
self.dealer = models.Dealer.objects.get(entity=model.entity)
self.extra_info = models.ExtraInfo.objects.get(
dealer=self.dealer,
content_type=ContentType.objects.get_for_model(model),
object_id=model.pk,
)
self.model = model
self.vat_rate = self._get_vat_rate()
self.item_transactions = self._get_item_transactions()
# self.additional_services = self._get_additional_services()
def _get_vat_rate(self):
vat = models.VatRate.objects.filter(dealer=self.dealer,is_active=True).first()
if not vat:
raise ObjectDoesNotExist("No active VAT rate found")
return vat.rate
def _get_additional_services(self):
return [x for item in self.item_transactions
for x in item.item_model.car.additional_services
]
def _get_item_transactions(self):
return self.model.get_itemtxs_data()[0].all()
def get_items(self):
return self._get_item_transactions()
@staticmethod
def _get_quantity(item):
return item.ce_quantity or item.quantity
# def _get_nested_value(self, item, *keys):
# current = item.item_model.additional_info
# for key in keys:
# current = current.get(key, {})
# return current
def _get_car_data(self, item):
quantity = self._get_quantity(item)
car = item.item_model.car
unit_price = Decimal(car.marked_price)
discount = self.extra_info.data.get("discount",0)
sell_price = unit_price - Decimal(discount)
return {
"item_number": item.item_model.item_number,
"vin": car.vin, #car_info.get("vin"),
"make": car.id_car_make ,#car_info.get("make"),
"model": car.id_car_model ,#car_info.get("model"),
"year": car.year ,# car_info.get("year"),
"logo": car.logo, # getattr(car.id_car_make, "logo", ""),
"trim": car.id_car_trim ,# car_info.get("trim"),
"mileage": car.mileage ,# car_info.get("mileage"),
"cost_price": car.cost_price,
"selling_price": car.selling_price,
"marked_price": car.marked_price,
"discount": car.discount_amount,
"quantity": quantity,
"unit_price": unit_price,
"sell_price": sell_price,
"total": unit_price,
"total_vat": sell_price * self.vat_rate,
"total_discount": discount,
"final_price": sell_price + (sell_price * self.vat_rate),
"total_additionals": car.total_additional_services,
"grand_total": sell_price + (sell_price * self.vat_rate) + car.total_additional_services,
"additional_services": car.additional_services,# self._get_nested_value(
#item, self.ADDITIONAL_SERVICES_KEY
#),
}
def calculate_totals(self):
total_price = sum(
Decimal(item.item_model.car.marked_price)
for item in self.item_transactions
)
total_additionals = sum(
Decimal(item.price_) for item in self._get_additional_services())
total_discount = self.extra_info.data.get("discount",0)
total_price_discounted = total_price
if total_discount:
total_price_discounted = total_price - Decimal(total_discount)
print(total_price_discounted)
total_vat_amount = total_price_discounted * self.vat_rate
return {
"total_price_discounted":total_price_discounted,
"total_price_before_discount":total_price,
"total_price": total_price_discounted,
"total_vat_amount": total_vat_amount,
"total_discount": Decimal(total_discount),
"total_additionals": total_additionals,
"grand_total":total_price_discounted + total_vat_amount + total_additionals,
}
def get_finance_data(self):
totals = self.calculate_totals()
return {
"cars": [self._get_car_data(item) for item in self.item_transactions],
"quantity": sum(
self._get_quantity(item) for item in self.item_transactions
),
"total_price": round(totals["total_price"], 2),
"total_price_discounted": round(totals["total_price_discounted"], 2),
"total_price_before_discount": round(totals["total_price_before_discount"], 2),
"total_vat": round(totals["total_vat_amount"] + totals["total_price"], 2),
"total_vat_amount": round(totals["total_vat_amount"], 2),
"total_discount": round(totals["total_discount"], 2),
"total_additionals": round(totals["total_additionals"], 2),
"grand_total": round(totals["grand_total"], 2),
"additionals": self._get_additional_services(),
"vat": round(self.vat_rate, 2),
}
class CarFinanceCalculator:
"""
Class responsible for calculating car financing details.
@ -1054,7 +1200,7 @@ class CarFinanceCalculator:
def _get_car_data(self, item):
quantity = self._get_quantity(item)
car = item.item_model.car
unit_price = Decimal(car.finances.marked_price)
unit_price = Decimal(car.marked_price)
discount = self.extra_info.data.get("discount",0)
sell_price = unit_price - Decimal(discount)
return {
@ -1066,10 +1212,10 @@ class CarFinanceCalculator:
"logo": car.logo, # getattr(car.id_car_make, "logo", ""),
"trim": car.id_car_trim ,# car_info.get("trim"),
"mileage": car.mileage ,# car_info.get("mileage"),
"cost_price": car.finances.cost_price,
"selling_price": car.finances.selling_price,
"marked_price": car.finances.marked_price,
"discount": car.finances.discount_amount,
"cost_price": car.cost_price,
"selling_price": car.selling_price,
"marked_price": car.marked_price,
"discount": car.discount_amount,
"quantity": quantity,
"unit_price": unit_price,
"sell_price": sell_price,
@ -1086,7 +1232,7 @@ class CarFinanceCalculator:
def calculate_totals(self):
total_price = sum(
Decimal(item.item_model.car.finances.marked_price)
Decimal(item.item_model.car.marked_price)
for item in self.item_transactions
)
total_additionals = sum(
@ -1112,7 +1258,7 @@ class CarFinanceCalculator:
def get_finance_data(self):
totals = self.calculate_totals()
return {
"cars": [self._get_car_data(item) for item in self.item_transactions],
"car": [self._get_car_data(item) for item in self.item_transactions],
"quantity": sum(
self._get_quantity(item) for item in self.item_transactions
),
@ -1127,6 +1273,53 @@ class CarFinanceCalculator:
"additionals": self._get_additional_services(),
"vat": round(self.vat_rate, 2),
}
def get_finance_data(estimate,dealer):
vat = models.VatRate.objects.filter(dealer=dealer,is_active=True).first()
item = estimate.get_itemtxs_data()[0].first()
car = item.item_model.car
if isinstance(estimate,InvoiceModel) and hasattr(estimate, "ce_model"):
estimate = estimate.ce_model
extra_info = models.ExtraInfo.objects.get(
dealer=dealer,
content_type=ContentType.objects.get_for_model(EstimateModel),
object_id=estimate.pk,
)
discount = extra_info.data.get("discount", 0)
discount = Decimal(discount)
vat_amount = car.marked_price * vat.rate
additional_services = car.get_additional_services()
return {
"car": car,
"discounted_price": (Decimal(car.marked_price) - discount) or 0,
"price_before_discount": car.marked_price,
"vat_amount": vat_amount,
"vat_rate": vat.rate,
"discount_amount": discount,
"additional_services": additional_services,
"grand_total": (car.marked_price - discount) + vat_amount + additional_services.get("total")
}
# totals = self.calculate_totals()
# return {
# "car": [self._get_car_data(item) for item in self.item_transactions],
# "quantity": sum(
# self._get_quantity(item) for item in self.item_transactions
# ),
# "total_price": round(totals["total_price"], 2),
# "total_price_discounted": round(totals["total_price_discounted"], 2),
# "total_price_before_discount": round(totals["total_price_before_discount"], 2),
# "total_vat": round(totals["total_vat_amount"] + totals["total_price"], 2),
# "total_vat_amount": round(totals["total_vat_amount"], 2),
# "total_discount": round(totals["total_discount"], 2),
# "total_additionals": round(totals["total_additionals"], 2),
# "grand_total": round(totals["grand_total"], 2),
# "additionals": self._get_additional_services(),
# "vat": round(self.vat_rate, 2),
# }
# class CarFinanceCalculator:
# """
# Class responsible for calculating car financing details.
@ -1360,9 +1553,9 @@ def _post_sale_and_cogs(invoice, dealer):
2) COGS / Inventory journal
"""
entity = invoice.ledger.entity
calc = CarFinanceCalculator(invoice)
data = calc.get_finance_data()
# calc = CarFinanceCalculator(invoice)
data = get_finance_data(invoice,dealer)
car = data.get("car")
cash_acc = entity.get_all_accounts().filter(role_default=True, role=roles.ASSET_CA_CASH).first()
ar_acc = entity.get_all_accounts().filter(role_default=True, role=roles.ASSET_CA_RECEIVABLES).first()
vat_acc = entity.get_all_accounts().filter(role_default=True, role=roles.LIABILITY_CL_TAXES_PAYABLE).first()
@ -1371,105 +1564,105 @@ def _post_sale_and_cogs(invoice, dealer):
cogs_acc = entity.get_all_accounts().filter(role_default=True, role=roles.COGS).first()
inv_acc = entity.get_all_accounts().filter(role_default=True, role=roles.ASSET_CA_INVENTORY).first()
for car_data in data['cars']:
car = invoice.get_itemtxs_data()[0].filter(
item_model__car__vin=car_data['vin']
).first().item_model.car
qty = Decimal(car_data['quantity'])
# for car_data in data['cars']:
# car = invoice.get_itemtxs_data()[0].filter(
# item_model__car__vin=car_data['vin']
# ).first().item_model.car
# qty = Decimal(car_data['quantity'])
net_car_price = Decimal(car_data['total']) - Decimal(car_data['total_discount'])
net_additionals_price = Decimal(data['total_additionals'])
vat_amount = Decimal(data['total_vat_amount']) * qty
grand_total = net_car_price + net_additionals_price + vat_amount
cost_total = Decimal(car_data['cost_price']) * qty
net_car_price = Decimal(data['discounted_price'])
net_additionals_price = Decimal(data['additional_services']['total'])
vat_amount = Decimal(data['vat_amount'])
grand_total = net_car_price + net_additionals_price + vat_amount
cost_total = Decimal(car.cost_price)
# ------------------------------------------------------------------
# 2A. Journal: Cash / A-R / VAT / Sales
# ------------------------------------------------------------------
# ------------------------------------------------------------------
# 2A. Journal: Cash / A-R / VAT / Sales
# ------------------------------------------------------------------
je_sale = JournalEntryModel.objects.create(
ledger=invoice.ledger,
description=f"Sale {car.vin}",
origin=f"Invoice {invoice.invoice_number}",
locked=False,
posted=False
)
# Dr Cash (what the customer paid)
je_sale = JournalEntryModel.objects.create(
ledger=invoice.ledger,
description=f"Sale {car.vin}",
origin=f"Invoice {invoice.invoice_number}",
locked=False,
posted=False
)
# Dr Cash (what the customer paid)
TransactionModel.objects.create(
journal_entry=je_sale,
account=cash_acc,
amount=grand_total,
tx_type='debit'
)
# # Cr A/R (clear the receivable)
# TransactionModel.objects.create(
# journal_entry=je_sale,
# account=ar_acc,
# amount=grand_total,
# tx_type='credit'
# )
# Cr VAT Payable
TransactionModel.objects.create(
journal_entry=je_sale,
account=vat_acc,
amount=vat_amount,
tx_type='credit'
)
# Cr Sales Car
TransactionModel.objects.create(
journal_entry=je_sale,
account=car_rev,
amount=net_car_price,
tx_type='credit'
)
if net_additionals_price > 0:
# Cr Sales Additional Services
TransactionModel.objects.create(
journal_entry=je_sale,
account=cash_acc,
amount=grand_total,
tx_type='debit'
)
# # Cr A/R (clear the receivable)
# TransactionModel.objects.create(
# journal_entry=je_sale,
# account=ar_acc,
# amount=grand_total,
# tx_type='credit'
# )
# Cr VAT Payable
TransactionModel.objects.create(
journal_entry=je_sale,
account=vat_acc,
amount=vat_amount,
account=add_rev,
amount=net_additionals_price,
tx_type='credit'
)
# Cr Sales Car
TransactionModel.objects.create(
journal_entry=je_sale,
account=car_rev,
amount=net_car_price,
tx_type='credit'
)
# ------------------------------------------------------------------
# 2B. Journal: COGS / Inventory reduction
# ------------------------------------------------------------------
je_cogs = JournalEntryModel.objects.create(
ledger=invoice.ledger,
description=f"COGS {car.vin}",
origin=f"Invoice {invoice.invoice_number}",
locked=False,
posted=False
)
if net_additionals_price > 0:
# Cr Sales Additional Services
TransactionModel.objects.create(
journal_entry=je_sale,
account=add_rev,
amount=net_additionals_price,
tx_type='credit'
)
# Dr COGS
TransactionModel.objects.create(
journal_entry=je_cogs,
account=cogs_acc,
amount=cost_total,
tx_type='debit'
)
# ------------------------------------------------------------------
# 2B. Journal: COGS / Inventory reduction
# ------------------------------------------------------------------
je_cogs = JournalEntryModel.objects.create(
ledger=invoice.ledger,
description=f"COGS {car.vin}",
origin=f"Invoice {invoice.invoice_number}",
locked=False,
posted=False
)
# Dr COGS
TransactionModel.objects.create(
journal_entry=je_cogs,
account=cogs_acc,
amount=cost_total,
tx_type='debit'
)
# Cr Inventory
TransactionModel.objects.create(
journal_entry=je_cogs,
account=inv_acc,
amount=cost_total,
tx_type='credit'
)
# ------------------------------------------------------------------
# 2C. Update car state flags inside the same transaction
# ------------------------------------------------------------------
entity.get_items_inventory().filter(name=car.vin).update(for_inventory=False)
# car.item_model.for_inventory = False
# car.item_model.save(update_fields=['for_inventory'])
car.finances.selling_price = grand_total
car.finances.is_sold = True
car.finances.save()
# Cr Inventory
TransactionModel.objects.create(
journal_entry=je_cogs,
account=inv_acc,
amount=cost_total,
tx_type='credit'
)
# ------------------------------------------------------------------
# 2C. Update car state flags inside the same transaction
# ------------------------------------------------------------------
entity.get_items_inventory().filter(name=car.vin).update(for_inventory=False)
# car.item_model.for_inventory = False
# car.item_model.save(update_fields=['for_inventory'])
car.selling_price = grand_total
# car.is_sold = True
car.save()
# def handle_account_process(invoice, amount, finance_data):
# """
# Processes accounting transactions based on an invoice, financial data,

View File

@ -105,6 +105,9 @@ from django_ledger.forms.bank_account import (
BankAccountCreateForm,
BankAccountUpdateForm,
)
from django_ledger.views.chart_of_accounts import (
ChartOfAccountModelListView as ChartOfAccountModelListViewBase
)
from django_ledger.views.bill import (
BillModelCreateView,
BillModelDetailView,
@ -126,6 +129,7 @@ from django_ledger.forms.invoice import (
from django_ledger.forms.item import (
InventoryItemCreateForm,
)
from django_ledger.forms.purchase_order import (
PurchaseOrderModelCreateForm,
BasePurchaseOrderModelUpdateForm,
@ -195,6 +199,7 @@ from .services import (
from .utils import (
CarFinanceCalculator,
get_car_finance_data,
get_finance_data,
get_item_transactions,
handle_payment,
reserve_car,
@ -515,10 +520,10 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
qs = models.Car.objects.filter(dealer=dealer)
total_cars = qs.count()
stats = models.CarFinance.objects.filter(car__dealer=dealer).aggregate(
total_cost_price=Sum("cost_price"),
total_selling_price=Sum("selling_price"),
)
stats = 0 # models.CarFinance.objects.filter(car__dealer=dealer).aggregate( #TODO:update_finance
# total_cost_price=Sum("cost_price"),
# total_selling_price=Sum("selling_price"),
# )
total_cost_price = stats["total_cost_price"] or 0
total_selling_price = stats["total_selling_price"] or 0
total_profit = total_selling_price - total_cost_price
@ -1701,124 +1706,21 @@ class CarDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
permission_required = ["inventory.view_car"]
class CarFinanceCreateView(LoginRequiredMixin, PermissionRequiredMixin,SuccessMessageMixin, CreateView):
"""
Handles the creation of car finance records within the inventory system.
def CarFinanceUpdateView(request,dealer_slug,slug):
car = get_object_or_404(models.Car, slug=slug)
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
This view provides functionality to create car finance records tied to a
specific car in the inventory. It enforces that the user is logged in and
has the required permissions to add a car finance record. It also customizes
form behavior and context data to associate the finance record with the
corresponding car and populate additional services based on the user's type.
:ivar model: The database model associated with the view.
:type model: models.CarFinance
:ivar form_class: The form class used to create car finance records.
:type form_class: forms.CarFinanceForm
:ivar template_name: The template used to render the car finance creation page.
:type template_name: str
:ivar permission_required: The list of permissions required to access this view.
:type permission_required: list
"""
model = models.CarFinance
form_class = forms.CarFinanceForm
template_name = "inventory/car_finance_form.html"
permission_required = ["inventory.add_carfinance"]
def dispatch(self, request, *args, **kwargs):
self.car = get_object_or_404(models.Car, slug=self.kwargs["slug"])
return super().dispatch(request, *args, **kwargs)
def form_valid(self, form):
form.instance.car = self.car
messages.success(self.request, _("Car finance details saved successfully"))
return super().form_valid(form)
def get_success_url(self):
return reverse(
"car_detail",
kwargs={"dealer_slug": self.request.dealer.slug, "slug": self.car.slug},
)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["car"] = self.car
return context
# def get_form(self, form_class=None):
# form = super().get_form(form_class)
# dealer = get_user_type(self.request)
# form.fields[
# "additional_finances"
# ].queryset = models.AdditionalServices.objects.filter(dealer=dealer)
# return form
class CarFinanceUpdateView(
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
):
"""
Manages the update of car finance details.
This class-based view provides functionality for updating car finance
information in the system. It enforces login and specific permissions for
access, and it leverages mixins to provide success messages upon completing
updates. It is designed to interact with car finance data, update form
handling, and enforce related business logic.
:ivar model: The model associated with the view.
:type model: models.CarFinance
:ivar form_class: The form class used by the view.
:type form_class: forms.CarFinanceForm
:ivar template_name: The template used for rendering the form.
:type template_name: str
:ivar success_message: The success message displayed after an update.
:type success_message: str
:ivar permission_required: List of permissions required to access the view.
:type permission_required: list
"""
model = models.CarFinance
form_class = forms.CarFinanceForm
template_name = "inventory/car_finance_form.html"
success_message = _("Car finance details updated successfully")
permission_required = ["inventory.change_carfinance"]
def get_success_url(self):
return reverse(
"car_detail",
kwargs={
"dealer_slug": self.request.dealer.slug,
"slug": self.object.car.slug,
},
)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs["instance"] = self.get_object()
return kwargs
def get_context_data(self , **kwargs):
context = super().get_context_data(**kwargs)
context["car"] = self.object.car
return context
# def get_initial(self):
# initial = super().get_initial()
# instance = self.get_object()
# dealer = get_user_type(self.request)
# selected_items = instance.additional_services.filter(dealer=dealer)
# initial["additional_finances"] = selected_items
# return initial
# def get_form(self, form_class=None):
# form = super().get_form(form_class)
# dealer = get_user_type(self.request)
# form.fields[
# "additional_finances"
# ].queryset = models.AdditionalServices.objects.filter(dealer=dealer)
# return form
if request.method == "POST":
form = forms.CarFinanceForm(request.POST, instance=car)
if form.is_valid():
form.save()
return redirect("car_detail", dealer_slug=dealer.slug, slug=car.slug)
else:
messages.error(request, "Please correct the errors below.")
else:
form = forms.CarFinanceForm(instance=car)
return render(request, "inventory/car_finance_form.html", {"car": car, "dealer": dealer, "form": form})
class CarUpdateView(
LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView
@ -3306,7 +3208,7 @@ def GroupPermissionView(request, dealer_slug, pk):
# Define ALL permissions you want to manage
MODEL_LIST = [
("inventory", "car"),
("inventory", "carfinance"),
# ("inventory", "carfinance"),
("inventory", "carlocation"),
("inventory", "customcard"),
("inventory", "cartransfer"),
@ -4197,7 +4099,7 @@ class BankAccountListView(LoginRequiredMixin, PermissionRequiredMixin, ListView)
def get_queryset(self):
query = self.request.GET.get("q")
dealer = get_user_type(self.request)
dealer = self.request.dealer
bank_accounts = BankAccountModel.objects.filter(entity_model=dealer.entity)
return apply_search_filters(bank_accounts, query)
@ -4901,8 +4803,7 @@ def create_estimate(request, dealer_slug, slug=None):
hash=item.get("item_id"),
# finances__is_sold=False,
colors__isnull=False,
finances__isnull=False,
finances__marked_price__gt=1,
marked_price__gt=1,
status="available",
).all()
@ -4912,9 +4813,9 @@ def create_estimate(request, dealer_slug, slug=None):
{
"item_number": i.item_model.item_number,
"quantity": 1,
"unit_cost": round(float(i.finances.marked_price)),
"unit_revenue": round(float(i.finances.marked_price)),
"total_amount": round(float(i.finances.total_vat)),
"unit_cost": round(float(i.marked_price)),
"unit_revenue": round(float(i.marked_price)),
"total_amount": round(float(i.total_vat)),# TODO : check later
}
)
@ -4932,10 +4833,10 @@ def create_estimate(request, dealer_slug, slug=None):
# instance = models.Car.objects.get(vin=item.name)
# estimate_itemtxs = {
# item.item_number: {
# "unit_cost": instance.finances.cost_price,
# "unit_revenue": instance.finances.marked_price,
# "unit_cost": instance.cost_price,
# "unit_revenue": instance.marked_price,
# "quantity": Decimal(quantities),
# "total_amount": instance.finances.total_vat * int(quantities),
# "total_amount": instance.total_vat * int(quantities),# TODO : check later
# }
# }
@ -5015,8 +4916,7 @@ def create_estimate(request, dealer_slug, slug=None):
models.Car.objects.filter(
dealer=dealer,
colors__isnull=False,
finances__isnull=False,
finances__marked_price__gt=1,
marked_price__gt=1,
status="available",
)
.annotate(
@ -5091,17 +4991,18 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
estimate = kwargs.get("object")
if estimate.get_itemtxs_data():
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
# calculator = CarFinanceCalculator(estimate)
# finance_data = calculator.get_finance_data()
finance_data = get_finance_data(estimate,dealer)
invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first()
kwargs["data"] = finance_data
kwargs["invoice"] = invoice_obj
try:
car_finances = estimate.get_itemtxs_data()[0].first().item_model.car.finances
selected_items = car_finances.additional_services.filter(dealer=dealer)
car = estimate.get_itemtxs_data()[0].first().item_model.car
selected_items = car.additional_services.filter(dealer=dealer)
form = forms.AdditionalFinancesForm()
form.fields["additional_finances"].queryset = form.fields["additional_finances"].queryset.filter(dealer=dealer)
form.fields["additional_finances"].queryset = form.fields["additional_finances"].queryset.filter(dealer=dealer) # TODO : check later
form.initial["additional_finances"] = selected_items
kwargs["additionals_form"] = form
except Exception as e:
@ -5180,8 +5081,8 @@ def create_sale_order(request, dealer_slug, pk):
# else:
# form.fields["opportunity"].widget = HiddenInput()
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
# calculator = CarFinanceCalculator(estimate)
finance_data = get_finance_data(estimate,dealer)
return render(
request,
"sales/estimates/sale_order_form.html",
@ -5199,15 +5100,16 @@ def update_estimate_discount(request, dealer_slug, pk):
content_type=ContentType.objects.get_for_model(EstimateModel),
object_id=estimate.pk,
)
# calculator = CarFinanceCalculator(estimate)
# finance_data = calculator.get_finance_data()
discount_amount = request.POST.get("discount_amount", 0)
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
if Decimal(discount_amount) >= finance_data.get('cars')[0]['marked_price']:
finance_data = get_finance_data(estimate,dealer)
car = finance_data.get('car')
if Decimal(discount_amount) >= car.marked_price:
messages.error(request, _("Discount amount cannot be greater than marked price"))
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
print(finance_data.get('cars')[0]['marked_price'] * Decimal('0.5'))
if Decimal(discount_amount) > finance_data.get('cars')[0]['marked_price'] * Decimal('0.5'):
if Decimal(discount_amount) > car.marked_price * Decimal('0.5'):
messages.warning(request, _("Discount amount is greater than 50% of the marked price, proceed with caution."))
else:
messages.success(request, _("Discount updated successfully"))
@ -5225,10 +5127,10 @@ def update_estimate_additionals(request, dealer_slug, pk):
if form.is_valid():
estimate = get_object_or_404(EstimateModel, pk=pk)
car = estimate.get_itemtxs_data()[0].first().item_model.car
car.finances.additional_services.set(
car.additional_services.set(
form.cleaned_data["additional_finances"]
)
car.finances.save()
car.save()
messages.success(request, "Additional Finances updated successfully")
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
@ -5247,10 +5149,12 @@ class SaleOrderDetail(LoginRequiredMixin, PermissionRequiredMixin, DetailView):
def get_context_data(self, **kwargs):
saleorder = kwargs.get("object")
dealer = self.request.dealer
estimate = saleorder.estimate
if estimate.get_itemtxs_data():
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
# calculator = CarFinanceCalculator(estimate)
# finance_data = calculator.get_finance_data()
finance_data = get_finance_data(estimate,dealer)
kwargs["data"] = finance_data
return super().get_context_data(**kwargs)
@ -5348,9 +5252,10 @@ class EstimatePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie
def get_context_data(self, **kwargs):
estimate = kwargs.get("object")
if estimate.get_itemtxs_data():
# data = get_financial_values(estimate)
calculator = CarFinanceCalculator(estimate)
kwargs["data"] = calculator.get_finance_data()
# calculator = CarFinanceCalculator(estimate)
kwargs["data"] = get_finance_data(estimate,self.request.dealer)
return super().get_context_data(**kwargs)
@ -5530,8 +5435,9 @@ class InvoiceDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView)
invoice = kwargs.get("object")
if invoice.get_itemtxs_data():
calculator = CarFinanceCalculator(invoice)
finance_data = calculator.get_finance_data()
# calculator = CarFinanceCalculator(invoice)
# finance_data = calculator.get_finance_data()
finance_data = get_finance_data(invoice,self.request.dealer)
kwargs["data"] = finance_data
kwargs["payments"] = JournalEntryModel.objects.filter(
ledger=invoice.ledger
@ -5757,16 +5663,17 @@ def invoice_create(request, dealer_slug, pk):
ledger.save()
invoice.save()
calculator = CarFinanceCalculator(estimate)
finance_data = calculator.get_finance_data()
# calculator = CarFinanceCalculator(estimate)
# finance_data = calculator.get_finance_data()
finance_data = get_finance_data(estimate,dealer)
car = finance_data.get("car")
invoice_itemtxs = {
i.get("item_number"): {
"unit_cost": i.get("grand_total"),
car.item_model.item_number: {
"unit_cost": finance_data.get("grand_total"),
"quantity": 1,
"total_amount": i.get("grand_total"),
"total_amount": finance_data.get("grand_total"),
}
for i in finance_data.get("cars")
}
invoice_itemtxs = invoice.migrate_itemtxs(
@ -5834,8 +5741,8 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView
dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"])
invoice = kwargs.get("object")
if invoice.get_itemtxs_data():
calculator = CarFinanceCalculator(invoice)
finance_data = calculator.get_finance_data()
# calculator = CarFinanceCalculator(invoice)
finance_data = get_finance_data(invoice,dealer)
kwargs["data"] = finance_data
kwargs["dealer"] = dealer
return super().get_context_data(**kwargs)
@ -7180,11 +7087,11 @@ class OpportunityCreateView(
if self.request.is_dealer:
form.fields["lead"].queryset = models.Lead.objects.filter(
dealer=dealer
)
)
elif self.request.is_staff:
form.fields["lead"].queryset = models.Lead.objects.filter(
dealer=dealer, staff=self.request.staff
)
)
return form
def get_success_url(self):
@ -9741,7 +9648,7 @@ def pricing_page(request, dealer_slug):
else:
messages.info(request,_("You already have an plan!!"))
return redirect('home',dealer_slug=dealer_slug)
@login_required
@ -10929,13 +10836,13 @@ def upload_cars(request, dealer_slug, pk=None):
vendor=vendor,
receiving_date=receiving_date,
)
if po_item:
models.CarFinance.objects.create(
car=car,
cost_price=po_item.item.unit_cost,
marked_price=0,
selling_price=0,
)
# if po_item: #TODO:update
# models.CarFinance.objects.create(
# car=car,
# cost_price=po_item.item.unit_cost,
# marked_price=0,
# selling_price=0,
# )
car.add_colors(exterior=exterior, interior=interior)
cars_created += 1
logger.debug(
@ -11007,11 +10914,8 @@ def bulk_update_car_price(request):
else:
for car_pk in cars:
car = models.Car.objects.get(pk=car_pk)
if not hasattr(car, "finances"):
models.CarFinance.objects.create(car=car, cost_price=Decimal(price))
else:
car.finances.cost_price = Decimal(price)
car.finances.save()
car.cost_price = Decimal(price)
car.save()
messages.success(request, "Price updated successfully")
response = HttpResponse()
@ -11221,11 +11125,11 @@ def car_sale_report_csv_export(request,dealer_slug):
car.stock_type,
car.created_at.strftime("%Y-%m-%d %H:%M:%S") if car.created_at else '',
car.sold_date.strftime("%Y-%m-%d %H:%M:%S") if car.sold_date else '',
car.finances.cost_price,
car.finances.marked_price,
car.finances.discount_amount,
car.finances.selling_price,
car.finances.vat_amount,
car.cost_price,
car.marked_price,
car.discount_amount,
car.selling_price,
car.vat_amount, # TODO : check later
car.item_model.invoicemodel_set.first().invoice_number
])
@ -11474,4 +11378,8 @@ def ticket_update(request, ticket_id):
return render(request, 'support/ticket_update.html', {
'ticket': ticket,
'form': form
})
})
class ChartOfAccountModelListView(ChartOfAccountModelListViewBase):
template_name = 'chart_of_accounts/coa_list.html'

View File

@ -0,0 +1,38 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% load icon from django_ledger %}
{% block content %}
<div class="card mb-4">
<div class="card-body">
<div class="row">
<div class="col-12">
<div class="d-flex justify-content-between align-items-center mb-4">
<h1 class="display-4 mb-0">Chart of Accounts</h1>
<a href="{{ entity_model.get_coa_create_url }}" class="btn btn-success btn-lg">
{% icon 'carbon:add-alt' 24 %} Add New
</a>
</div>
{% if not inactive %}
<a class="btn btn-warning mb-4" href="{{ entity_model.get_coa_list_inactive_url }}">
{% trans 'Show Inactive' %}
</a>
{% else %}
<a class="btn btn-warning mb-4" href="{{ entity_model.get_coa_list_url }}">
{% trans 'Show Active' %}
</a>
{% endif %}
</div>
</div>
<div class="row row-cols-1 row-cols-md-2 g-4">
{% for coa_model in coa_list %}
<div class="col">
{% include 'chart_of_accounts/includes/coa_card.html' with coa_model=coa_model %}
</div>
{% endfor %}
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,116 @@
{% load django_ledger %}
{% load i18n %}
{% now "Y" as current_year %}
<div class="card shadow-sm border-0 mb-4">
<div class="card-header {% if coa_model.is_default %}bg-success text-white{% elif not coa_model.is_active %}bg-danger text-white{% endif %} py-3">
<div class="d-flex align-items-center">
<div class="me-3">
<i class="fas fa-building fa-2x"></i>
</div>
<div class="fw-bold fs-5">
{{ coa_model.name }}
{% if coa_model.is_default %}
<span class="badge bg-light text-dark ms-2">{% trans 'DEFAULT' %}</span>
{% endif %}
</div>
</div>
</div>
<div class="card-body">
<div class="row g-3">
<div class="col-md-6">
<div class="d-flex align-items-center mb-2">
<span class="fw-bold me-2">{% trans 'Entity Default' %}:</span>
{% if coa_model.is_default %}
<span class="badge bg-success"><i class="fas fa-check-circle"></i></span>
{% else %}
<span class="badge bg-danger"><i class="fas fa-times-circle"></i></span>
{% endif %}
</div>
<div class="d-flex align-items-center mb-2">
<span class="fw-bold me-2">{% trans 'Is Active' %}:</span>
{% if coa_model.is_active %}
<span class="badge bg-success"><i class="fas fa-check-circle"></i></span>
{% else %}
<span class="badge bg-danger"><i class="fas fa-times-circle"></i></span>
{% endif %}
</div>
<div class="mb-2">
<span class="fw-bold">CoA ID:</span>
<span class="text-muted ms-2">{{ coa_model.slug }}</span>
</div>
</div>
<div class="col-md-6">
<div class="mb-2">
<span class="fw-bold"><i class="fas fa-list-alt me-1"></i> {% trans 'Total Accounts' %}:</span>
<span class="ms-2">{{ coa_model.accountmodel_total__count }}</span>
</div>
<div class="mb-2">
<span class="fw-bold text-info"><i class="fas fa-check-circle me-1"></i> {% trans 'Active Accounts' %}:</span>
<span class="ms-2">{{ coa_model.accountmodel_active__count }}</span>
</div>
<div class="mb-2">
<span class="fw-bold text-danger"><i class="fas fa-lock me-1"></i> {% trans 'Locked Accounts' %}:</span>
<span class="ms-2">{{ coa_model.accountmodel_locked__count }}</span>
</div>
</div>
</div>
<hr class="my-3">
<div class="row">
<div class="col-md-6">
<small class="text-muted">
<i class="far fa-calendar-plus me-1"></i>
<span class="fw-bold">{% trans 'Created' %}:</span>
{{ coa_model.created|date }}
</small>
</div>
<div class="col-md-6">
<small class="text-muted">
<i class="far fa-clock me-1"></i>
<span class="fw-bold">{% trans 'Updated' %}:</span>
{{ coa_model.created|timesince }} {% trans 'ago' %}
</small>
</div>
</div>
</div>
<div class="card-footer bg-transparent border-top-0">
<div class="d-flex flex-wrap gap-2">
<a href="{{ coa_model.get_update_url }}" class="btn btn-sm btn-outline-warning fw-bold">
<i class="fas fa-edit me-1"></i> {% trans 'Update' %}
</a>
<a href="{{ coa_model.get_account_list_url }}" class="btn btn-sm btn-outline-success fw-bold">
<i class="fas fa-book me-1"></i> {% trans 'Accounts' %}
</a>
<a href="{{ coa_model.get_create_coa_account_url }}" class="btn btn-sm btn-outline-info fw-bold">
<i class="fas fa-plus-circle me-1"></i> {% trans 'Add Account' %}
</a>
{% if coa_model.can_mark_as_default %}
<a href="{{ coa_model.mark_as_default_url }}" class="btn btn-sm btn-outline-danger fw-bold">
<i class="fas fa-star me-1"></i> {% trans 'Mark as Default' %}
</a>
{% endif %}
{% if coa_model.can_deactivate %}
<a href="{{ coa_model.mark_as_inactive_url }}" class="btn btn-sm btn-outline-warning fw-bold">
<i class="fas fa-toggle-off me-1"></i> {% trans 'Mark as Inactive' %}
</a>
{% elif coa_model.can_activate %}
<a href="{{ coa_model.mark_as_active_url }}" class="btn btn-sm btn-outline-success fw-bold">
<i class="fas fa-toggle-on me-1"></i> {% trans 'Mark as Active' %}
</a>
{% endif %}
</div>
</div>
</div>

View File

@ -70,9 +70,9 @@
</h3>
{% endif %}
<div class="d-flex align-items-center mb-4">
{% if opportunity.car.finances %}
{% if opportunity.car.marked_price %}
<h5 class="mb-0 me-4">
{{ opportunity.car.finances.total }} <span class="fw-light"><span class="icon-saudi_riyal"></span></span>
{{ opportunity.car.total }} <span class="fw-light"><span class="icon-saudi_riyal"></span></span># TODO : check later
</h5>
{% endif %}
</div>

View File

@ -75,7 +75,7 @@
height: 12px;
width: 12px"></span>{{ opportunity.get_stage_display }}
</p>
<p class="ms-auto fs-9 text-body-emphasis fw-semibold mb-0 deals-revenue">{{ opportunity.car.finances.total }}</p>
<p class="ms-auto fs-9 text-body-emphasis fw-semibold mb-0 deals-revenue">{{ opportunity.car.total }}</p># TODO : check later
</div>
<div class="deals-company-agent d-flex flex-between-center">
<div class="d-flex align-items-center">
@ -107,7 +107,7 @@
<td class="d-none d-sm-block pe-sm-2">:</td>
<td class="py-1">
<p class="ps-6 ps-sm-0 fw-semibold fs-9 mb-0 mb-0 pb-3 pb-sm-0 text-body-emphasis">
{{ opportunity.car.finances.total }}
{{ opportunity.car.total }}# TODO : check later
</p>
</td>
</tr>

View File

@ -217,7 +217,7 @@
<li class="collapsed-nav-item-title d-none">{% trans 'Financials' %}</li>
{% if perms.django_ledger.view_accountmodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'account_list' request.dealer.slug %}">
<a class="nav-link" href="{% url 'coa-list' request.dealer.slug request.entity.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-book-open"></span></span><span class="nav-link-text">{% trans 'Chart of Accounts'|capfirst %}</span>
</div>
@ -295,7 +295,7 @@
</div>
{% endif %}
<!---->
{% if perms.django_ledger.can_view_reports %}
<div class="nav-item-wrapper">
<a class="nav-link dropdown-indicator label-1" href="#nv-reports" role="button" data-bs-toggle="collapse" aria-expanded="false" aria-controls="nv-reports">
@ -304,40 +304,40 @@
<span class="nav-link-icon"><i class="fa-solid fa-book-open"></i></span><span class="nav-link-text">{% trans 'Reports' %}</span>
</div>
</a>
<div class="parent-wrapper label-1">
<ul class="nav collapse parent" data-bs-parent="#navbarVerticalCollapse" id="nv-reports">
<li class="collapsed-nav-item-title d-none">{% trans 'Financials' %}</li>
{% if perms.django_ledger.view_accountmodel %}
<li class="nav-item">
<a class="nav-link" href="{% url 'entity-cf' request.dealer.slug request.dealer.entity.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-solid fa-sack-dollar"></span></span><span class="nav-link-text">{% trans 'Cash Flow'|capfirst %}</span>
</div>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'entity-ic' request.dealer.slug request.dealer.entity.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fa-solid fa-sheet-plastic"></span></span><span class="nav-link-text">{% trans 'Income Statement'|capfirst %}</span>
</div>
</a>
</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'entity-bs' request.dealer.slug request.dealer.entity.slug %}">
<div class="d-flex align-items-center">
<span class="nav-link-icon"><span class="fas fa-solid fa-scale-balanced"></span></span><span class="nav-link-text">{% trans 'Balance Sheet'|capfirst %}</span>
</div>
</a>
</a>
</li>
<li class="nav-item">
<!--car purchase report-->
<a class="nav-link" href="{% url 'po-report' request.dealer.slug %}">
@ -346,7 +346,7 @@
</div>
</a>
</li>
<li class="nav-item">
<!--car sale report-->
<a class="nav-link" href="{% url 'car-sale-report' request.dealer.slug %}">
@ -356,14 +356,14 @@
</a>
</li>
{% endif %}
</ul>
</div>
</div>
{% endif %}
<!---->
</li>
</ul>
{# --- Support & Contact Section (New) --- #}

View File

@ -20,11 +20,11 @@
<div class="alert alert-outline-warning d-flex align-items-center"
role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>
{% if not car.finances and not car.colors %}
{% if not car.marked_price and not car.colors %}
<p class="mb-0 flex-1">
{{ _("This car information is not complete , please add colors and finances both before making it ready for sale .") }}
</p>
{% elif car.finances and not car.colors %}
{% elif car.marked_price and not car.colors %}
<p class="mb-0 flex-1">
{{ _("This car information is not complete , please add colors before making it ready for sale .") }}
</p>
@ -205,7 +205,7 @@
<tr>
<th>{% trans 'Location'|capfirst %}</th>
<td>
{% if car.finances and not car.get_transfer %}
{% if car.marked_price and not car.get_transfer %}
{% if car.location %}
{% if car.location.is_owner_showroom %}
{% trans 'Our Showroom' %}
@ -257,16 +257,16 @@
<div class="card-body">
<div class="table-responsive scrollbar mb-3">
<table class="table table-sm fs-9 mb-0 overflow-hidden">
{% if car.finances %}
{% if car.marked_price %}
<tr>
{% if perms.inventory.view_carfinance %}
<th>{% trans "Cost Price"|capfirst %}</th>
<td>{{ car.finances.cost_price|floatformat:2 }}</td>
<td>{{ car.cost_price|floatformat:2 }}</td>
{% endif %}
</tr>
<tr>
<th>{% trans "Marked Price"|capfirst %}</th>
<td>{{ car.finances.marked_price|floatformat:2 }}</td>
<td>{{ car.marked_price|floatformat:2 }}</td>
</tr>
{% comment %} <tr>
<th>{% trans "Selling Price"|capfirst %}</th>
@ -300,7 +300,7 @@
<tr>
<td colspan="2">
{% if not car.get_transfer %}
<a href="{% url 'car_finance_update' request.dealer.slug car.finances.pk %}"
<a href="{% url 'car_finance_update' request.dealer.slug car.slug %}"
class="btn btn-phoenix-warning btn-sm mb-3">{% trans "Edit" %}</a>
{% else %}
<span class="badge bg-danger">{% trans "Cannot Edit, Car in Transfer." %}</span>
@ -311,7 +311,7 @@
{% else %}
<p>{% trans "No finance details available." %}</p>
{% if perms.inventory.add_carfinance %}
<a href="{% url 'car_finance_create' request.dealer.slug car.slug %}"
<a href="{% url 'car_finance_update' request.dealer.slug car.slug %}"
class="btn btn-phoenix-success btn-sm mb-3">{% trans "Add" %}</a>
{% endif %}
{% endif %}

View File

@ -98,10 +98,10 @@
<td>{{ transfer.car.vin }}</td>
<td>{{ transfer.car }}</td>
<td class="text-center">
{{ transfer.car.finances.selling_price }}&nbsp;<span class="icon-saudi_riyal"></span>
{{ transfer.car.selling_price }}&nbsp;<span class="icon-saudi_riyal"></span>
</td>
<td class="text-center">
{{ transfer.car.finances.vat_amount }}&nbsp;<span class="icon-saudi_riyal"></span>
{{ transfer.car.vat_amount }}&nbsp;<span class="icon-saudi_riyal"></span># TODO : check later
</td>
<td class="text-center">
{{ transfer.total_price }}&nbsp;<span class="icon-saudi_riyal"></span>

View File

@ -296,7 +296,7 @@
<tr>
<td>{{ transfer.car }}</td>
<td class="text-center">{{ transfer.quantity }}</td>
<td class="text-center">{{ transfer.car.finances.selling_price }}</td>
<td class="text-center">{{ transfer.car.selling_price }}</td>
<td class="text-center">{{ transfer.total_price }}</td>
</tr>
</tbody>
@ -345,12 +345,12 @@
});
document.getElementById('confirmAccept').addEventListener('click', function () {
// Handle the accept action here
// Handle the accept action here
$('#acceptModal').modal('hide');
});
document.getElementById('confirmReject').addEventListener('click', function () {
// Handle the reject action here
// Handle the reject action here
$('#rejectModal').modal('hide');
});
</script>

View File

@ -178,11 +178,11 @@
<td class="fs-9">{{ car.stock_type|capfirst }}</td>
<td class="fs-9">{{ car.created_at|date }}</td>
<td class="fs-9">{{ car.invoice.date_paid|date|default_if_none:"-" }}</td>
<td class="fs-9">{{ car.finances.cost_price }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.finances.marked_price }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.finances.total_discount }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.finances.selling_price }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.finances.vat_amount }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.cost_price }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.marked_price }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.total_discount }} <span class="icon-saudi_riyal"></span></td># TODO : check later
<td class="fs-9">{{ car.selling_price }} <span class="icon-saudi_riyal"></span></td>
<td class="fs-9">{{ car.vat_amount }} <span class="icon-saudi_riyal"></span></td># TODO : check later
<td class="fs-9">{{ car.invoice.invoice_number }}</td>
</tr>
{% endfor %}

View File

@ -104,7 +104,7 @@
{% endif %}
</div>
</div>
<div class="d-flex align-items-center gap-2">
<div class="d-flex align-items-center gap-2" hx-boost="false">
{% if estimate.status == 'draft' %}
{% if perms.django_ledger.change_estimatemodel %}
<button id="mark_as_sent_estimate"
@ -238,21 +238,18 @@
<th scope="col" style="min-width: 100px;">{% trans "Total" %}</th>
</tr>
</thead>
<tbody>
{% for item in data.cars %}
<tr>
<td class="align-middle">
<img src="{{item.logo}}" width="40" height="40" class="ps-2"></img>
<img src="{{data.car.logo}}" width="40" height="40" class="ps-2"></img>
</td>
<td class="align-middle">{{ item.make }}</td>
<td class="align-middle">{{ item.model }}</td>
<td class="align-middle">{{ item.year }}</td>
<td class="align-middle">{{ item.vin }}</td>
<td class="align-middle">{{ item.quantity }}</td>
<td class="align-middle ps-5">{{ item.unit_price }}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{ item.total }}</td>
<td class="align-middle">{{ data.car.id_car_make }}</td>
<td class="align-middle">{{ data.car.id_car_model }}</td>
<td class="align-middle">{{ data.car.year }}</td>
<td class="align-middle">{{ data.car.vin }}</td>
<td class="align-middle">1</td>
<td class="align-middle ps-5">{{ data.car.marked_price }}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{ data.car.marked_price }}</td>
</tr>
{% endfor %}
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Discount Amount" %}</td>
@ -265,7 +262,7 @@
<input type="number"
class="form-control"
name="discount_amount"
value="{{ data.total_discount }}"
value="{{ data.discount_amount }}"
step="0.01"
style="width: 1px">
<span class="input-group-text"><span class="icon-saudi_riyal"></span></span>
@ -273,20 +270,20 @@
</div>
</form>
{% else %}
{{ data.total_discount }} <span class="icon-saudi_riyal"></span>
{{ data.discount_amount }} <span class="icon-saudi_riyal"></span>
{% endif %}
</td>
</tr>
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Vat" %} ({{ data.vat }})</td>
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Vat" %} ({{ data.vat_rate }})</td>
<td class="align-middle text-start fw-semibold">
<span id="">+ {{ data.total_vat_amount }}<span class="icon-saudi_riyal"></span></span>
<span id="">+ {{ data.vat_amount }}<span class="icon-saudi_riyal"></span></span>
</td>
</tr>
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Additional Services" %}</td>
<td class="align-middle text-start fw-semibold">
{% for service in data.additionals %}
{% for service in data.additional_services.services %}
<small><span class="fw-semibold">+ {{ service.name }} - {{ service.price_|floatformat }}<span class="icon-saudi_riyal"></span></span></small>
<br>
{% endfor %}

View File

@ -111,7 +111,7 @@
<td>{{ car.year }} - {{ car.id_car_make.name }} - {{ car.id_car_model.name }} - {{ car.id_car_trim.name }}</td>
<td>{{ car.colors.first.exterior.name }}</td>
<td>{{ car.colors.first.interior.name }}</td>
<td>{{ car.finances.selling_price }}</td>
<td>{{ car.selling_price }}</td>
<td>{{ car.get_specifications }}</td>
</tr>
{% endfor %}

View File

@ -330,34 +330,34 @@
</tr>
</thead>
<tbody>
{% for item in data.cars %}
<tr>
<td class="align-middle"></td>
<td class="align-middle">{{ item.make }}</td>
<td class="align-middle">{{ item.model }}</td>
<td class="align-middle">{{ item.year }}</td>
<td class="align-middle">{{ item.vin }}</td>
<td class="align-middle">{{ item.quantity|floatformat:-1 }}</td>
<td class="align-middle ps-5">{{ item.total }}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{ item.total }}</td>
<td class="align-middle">
<img src="{{data.car.logo}}" width="40" height="40" class="ps-2"></img>
</td>
<td class="align-middle">{{ data.car.id_car_make }}</td>
<td class="align-middle">{{ data.car.id_car_model }}</td>
<td class="align-middle">{{ data.car.year }}</td>
<td class="align-middle">{{ data.car.vin }}</td>
<td class="align-middle">1</td>
<td class="align-middle ps-5">{{ data.car.marked_price }}</td>
<td class="align-middle text-body-tertiary fw-semibold">{{ data.car.marked_price }}</td>
</tr>
{% endfor %}
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Discount Amount" %}</td>
<td class="align-middle text-start fw-semibold">
<span id="grand-total">- {{ data.total_discount|floatformat }}<span class="icon-saudi_riyal"></span></span>
<span id="grand-total">- {{ data.discount_amount|floatformat }}<span class="icon-saudi_riyal"></span></span>
</td>
</tr>
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "VAT" %} ({{ data.vat }}%)</td>
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "VAT" %} ({{ data.vat_rate }}%)</td>
<td class="align-middle text-start fw-semibold">
<span id="grand-total">+ {{ data.total_vat_amount|floatformat }}<span class="icon-saudi_riyal"></span></span>
<span id="grand-total">+ {{ data.vat_amount|floatformat }}<span class="icon-saudi_riyal"></span></span>
</td>
</tr>
<tr class="bg-body-secondary total-sum">
<td class="align-middle ps-4 fw-semibold text-body-highlight" colspan="7">{% trans "Additional Services" %}</td>
<td class="align-middle text-start fw-bold">
{% for service in data.additionals %}
{% for service in data.additional_services.services %}
<small><span class="fw-bold">+ {{ service.name }} - {{ service.price_|floatformat }}<span class="icon-saudi_riyal"></span></span></small>
<br>
{% endfor %}