Merge branch 'main' of http://10.10.1.136:3000/ismail/haikal into frontend
This commit is contained in:
commit
5ff9b4c573
@ -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)
|
||||
|
||||
@ -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"]
|
||||
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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'
|
||||
38
templates/chart_of_accounts/coa_list.html
Normal file
38
templates/chart_of_accounts/coa_list.html
Normal 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 %}
|
||||
116
templates/chart_of_accounts/includes/coa_card.html
Normal file
116
templates/chart_of_accounts/includes/coa_card.html
Normal 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>
|
||||
@ -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>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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) --- #}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -98,10 +98,10 @@
|
||||
<td>{{ transfer.car.vin }}</td>
|
||||
<td>{{ transfer.car }}</td>
|
||||
<td class="text-center">
|
||||
{{ transfer.car.finances.selling_price }} <span class="icon-saudi_riyal"></span>
|
||||
{{ transfer.car.selling_price }} <span class="icon-saudi_riyal"></span>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{{ transfer.car.finances.vat_amount }} <span class="icon-saudi_riyal"></span>
|
||||
{{ transfer.car.vat_amount }} <span class="icon-saudi_riyal"></span># TODO : check later
|
||||
</td>
|
||||
<td class="text-center">
|
||||
{{ transfer.total_price }} <span class="icon-saudi_riyal"></span>
|
||||
|
||||
@ -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>
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
@ -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 %}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user