haikal/inventory/signals.py

757 lines
33 KiB
Python

from inventory.tasks import create_coa_accounts
from django.contrib.auth.models import Group
from django.db.models.signals import post_save, post_delete
from django.dispatch import receiver
from django.utils.translation import gettext_lazy as _
from django.contrib.auth import get_user_model
from django_ledger.io import roles
from django_ledger.models import (
EntityModel,
ItemModel,
JournalEntryModel,
TransactionModel,
LedgerModel
)
from . import models
from django.utils.timezone import now
from django.db import transaction
User = get_user_model()
# @receiver(post_save, sender=models.SaleQuotation)
# def link_quotation_to_entity(sender, instance, created, **kwargs):
# if created:
# # Get the corresponding Django Ledger entity for the dealer
# entity = EntityModel.objects.get(name=instance.dealer.get_root_dealer.name)
# instance.entity = entity
# instance.save()
# @receiver(pre_delete, sender=models.Dealer)
# def remove_user_account(sender, instance, **kwargs):
# user = instance.user
# if user:
# user.delete()
#
# @receiver(post_save, sender=User)
# def create_dealer(instance, created, *args, **kwargs):
# if created:
# if not instance.dealer:
#
# models.Dealer.objects.create(user=instance,name=instance.username,email=instance.email)
#
# @receiver(post_save, sender=models.Dealer)
# def create_user_account(sender, instance, created, **kwargs):
# if created:
# if instance.dealer_type != "Owner":
# user = User.objects.create_user(
# username=instance.name,
# email=instance.email,
# )
# user.set_password("Tenhal@123")
# user.save()
# instance.user = user
# instance.save()
# check with marwan
@receiver(post_save, sender=models.Car)
def create_car_location(sender, instance, created, **kwargs):
"""
Signal handler to create a CarLocation entry when a new Car instance is created.
The function ensures that the associated Car has a dealer before creating its
CarLocation. If the dealer is missing, a ValueError is raised, and an error
message is logged.
:param sender: The model class that sends the signal, typically `models.Car`.
:type sender: Type[models.Model]
:param instance: The actual instance of the Car model that triggered the signal.
:param created: A boolean value indicating whether a new record was created.
:type created: bool
:param kwargs: Additional keyword arguments provided with the signal.
:type kwargs: dict
:return: None
"""
try:
if created:
if instance.dealer is None:
raise ValueError(
f"Cannot create CarLocation for car {instance.vin}: dealer is missing."
)
models.CarLocation.objects.create(
car=instance,
owner=instance.dealer,
showroom=instance.dealer,
description=f"Initial location set for car {instance.vin}.",
)
except Exception as e:
print(f"Failed to create CarLocation for car {instance.vin}: {e}")
# Create Entity
@receiver(post_save, sender=models.Dealer)
def create_ledger_entity(sender, instance, created, **kwargs):
"""
Signal handler for creating ledger entities and initializing accounts for a new Dealer instance upon creation.
This signal is triggered when a new Dealer instance is saved to the database. It performs the following actions:
1. Creates a ledger entity for the Dealer with necessary configurations.
2. Generates a chart of accounts (COA) for the entity and assigns it as the default.
3. Creates predefined unit of measures (UOMs) related to the entity.
4. Initializes and assigns default accounts under various roles (e.g., assets, liabilities) for the entity with their
respective configurations (e.g., account code, balance type).
This function ensures all necessary financial records and accounts are set up when a new Dealer is added, preparing the
system for future financial transactions and accounting operations.
:param sender: The model class that sent the signal (in this case, Dealer).
:param instance: The instance of the model being saved.
:param created: A boolean indicating whether a new record was created.
:param kwargs: Additional keyword arguments passed by the signal.
:return: None
"""
if created:
entity_name = instance.user.dealer.name
entity = EntityModel.create_entity(
name=entity_name,
admin=instance.user,
use_accrual_method=True,
fy_start_month=1,
)
if entity:
instance.entity = entity
instance.save()
coa = entity.create_chart_of_accounts(
assign_as_default=True, commit=True, coa_name=_(f"{entity_name}-COA")
)
if coa:
# Create unit of measures
entity.create_uom(name="Unit", unit_abbr="unit")
for u in models.UnitOfMeasure.choices:
entity.create_uom(name=u[1], unit_abbr=u[0])
# Create COA accounts, background task
create_coa_accounts(instance.pk)
# create_settings(instance.pk)
# create_accounts_for_make(instance.pk)
@receiver(post_save, sender=models.Dealer)
def create_dealer_groups(sender, instance, created, **kwargs):
"""
Signal handler to create and assign default groups to a Dealer instance when it
is created. The groups are based on predefined names and assigned specific
permissions. Uses transaction hooks to ensure groups are created only after
successful database commit.
:param sender: The model class that triggered the signal.
:type sender: Type[models.Model]
:param instance: The instance of the model class that caused the signal to fire.
:param created: Boolean indicating whether an instance was newly created.
:param kwargs: Additional keyword arguments passed by the signal.
:type kwargs: dict
"""
group_names = ["Inventory", "Accountant", "Sales"]
def create_groups():
for group_name in group_names:
group, created = Group.objects.get_or_create(name=f"{instance.pk}_{group_name}")
group_manager,created = models.CustomGroup.objects.get_or_create(name=group_name, dealer=instance, group=group)
group_manager.set_default_permissions()
instance.user.groups.add(group)
transaction.on_commit(create_groups)
# Create Vendor
@receiver(post_save, sender=models.Vendor)
def create_ledger_vendor(sender, instance, created, **kwargs):
"""
Signal function that listens for the `post_save` event on the Vendor model.
This function is triggered after a Vendor instance is saved. If the instance
is newly created (`created` is True), it will create necessary related entities
such as a Vendor model in the corresponding Entity and an Account for that Vendor.
:param sender: The model class that triggered the signal event, which is `models.Vendor`.
:param instance: The specific instance of the `models.Vendor` that was saved.
:param created: A boolean indicating whether the instance is newly created (`True`)
or updated (`False`).
:param kwargs: Additional keyword arguments passed by the signal dispatcher.
:return: None
"""
if created:
instance.create_vendor_model()
instance.create_vendor_account(roles.LIABILITY_CL_ACC_PAYABLE)
else:
instance.update_vendor_model()
# Create Item
@receiver(post_save, sender=models.Car)
def create_item_model(sender, instance, created, **kwargs):
"""
Signal handler that triggers upon saving a `Car` model instance. This function is responsible
for creating or updating an associated product in the related entity's inventory system. The
new product is created only if it does not already exist, and additional information about the
car is added to the product's metadata.
:param sender: Signal sender, typically the model class triggering the save event.
:type sender: type
:param instance: Instance of the `Car` model that was saved.
:type instance: models.Car
:param created: Flag indicating whether the model instance was newly created.
:type created: bool
:param kwargs: Additional keyword arguments passed by the signal mechanism.
:type kwargs: dict
:return: None
"""
entity = instance.dealer.entity
if created:
coa = entity.get_default_coa()
uom = entity.get_uom_all().get(name="Unit")
if not entity.get_items_all().filter(name=instance.vin).exists():
product = entity.create_item_product(
name=instance.vin,
item_type=ItemModel.ITEM_TYPE_MATERIAL,
uom_model=uom,
coa_model=coa,
)
product.additional_info = {}
product.save()
product = entity.get_items_all().filter(name=instance.vin).first()
product.additional_info.update({'car_info': instance.to_dict()})
product.save()
# # 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.
: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
"""
entity = instance.car.dealer.entity
product = entity.get_items_all().filter(name=instance.car.vin).first()
product.default_amount = instance.selling_price
if not isinstance(product.additional_info, dict):
product.additional_info = {}
product.additional_info.update({"car_finance":instance.to_dict()})
product.additional_info.update({"additional_services": [service.to_dict() for service in instance.additional_services.all()]})
product.save()
print(f"Inventory item updated with CarFinance data for Car: {instance.car}")
# @receiver(pre_save, sender=models.SaleQuotation)
# def update_quotation_status(sender, instance, **kwargs):
# if instance.valid_until and timezone.now() > instance.valid_until:
# instance.status = 'expired'
# # instance.total_price = instance.calculate_total_price()
#
#
# @receiver(post_save, sender=models.Payment)
# def update_status_on_payment(sender, instance, created, **kwargs):
# if created:
# quotation = instance.sale_quotation
# total_payments = sum(payment.amount for payment in quotation.payments.all())
# if total_payments >= quotation.amount:
# quotation.status = 'completed'
# # SalesInvoice.objects.create(sales_order=order)
# elif total_payments > 0:
# quotation.status = 'partially_paid'
# else:
# quotation.status = 'pending'
# quotation.save()
@receiver(post_save, sender=models.CarColors)
def update_car_when_color_changed(sender, instance, **kwargs):
"""
Signal receiver to handle updates to a car instance when its related
CarColors instance is modified. Triggered by the `post_save` signal
for the `CarColors` model. Ensures that the associated `Car` instance
is saved, propagating changes effectively.
:param sender: The model class (`CarColors`) that was saved.
:type sender: Type[models.CarColors]
:param instance: The specific instance of `CarColors` that was saved.
:type instance: models.CarColors
:param kwargs: Additional keyword arguments passed by the signal.
:type kwargs: dict
:return: None
"""
car = instance.car
car.save()
@receiver(post_save, sender=models.Opportunity)
def notify_staff_on_deal_stage_change(sender, instance, **kwargs):
"""
Notify staff members when the stage of an Opportunity is updated. This function listens to the `post_save`
signal for the Opportunity model and triggers a notification if the stage attribute of the Opportunity
instance has been changed.
:param sender: The model class that sends the signal.
:type sender: type[models.Opportunity]
:param instance: The actual instance being saved in the Django ORM.
:type instance: models.Opportunity
:param kwargs: Additional keyword arguments passed by the signal.
:type kwargs: dict
:return: None
"""
if instance.pk:
previous = models.Opportunity.objects.get(pk=instance.pk)
if previous.stage != instance.stage:
message = f"Opportunity '{instance.pk}' status changed from {previous.stage} to {instance.stage}."
models.Notification.objects.create(
staff=instance.created_by, message=message
)
# @receiver(post_save, sender=models.Opportunity)
# def log_opportunity_creation(sender, instance, created, **kwargs):
# if created:
# models.OpportunityLog.objects.create(
# opportunity=instance,
# action="create",
# user=instance.created_by,
# details=f"Opportunity '{instance.deal_name}' was created.",
# )
# @receiver(pre_save, sender=models.Opportunity)
# def log_opportunity_update(sender, instance, **kwargs):
# if instance.pk:
# previous = models.Opportunity.objects.get(pk=instance.pk)
# if previous.stage != instance.deal_status:
# models.OpportunityLog.objects.create(
# opportunity=instance,
# action="status_change",
# user=instance.created_by,
# old_status=previous.deal_status,
# new_status=instance.deal_status,
# details=f"Status changed from {previous.deal_status} to {instance.deal_status}.",
# )
# else:
# models.OpportunityLog.objects.create(
# opportunity=instance,
# action="update",
# user=instance.created_by,
# details=f"Opportunity '{instance.deal_name}' was updated.",
# )
@receiver(post_save, sender=models.AdditionalServices)
def create_item_service(sender, instance, created, **kwargs):
"""
Signal handler for creating a service item in the ItemModel when a new
AdditionalServices instance is created. This function listens to the
post_save signal of the AdditionalServices model and sets up the related
ItemModel instance to represent the service.
:param sender: The model class that sent the signal.
:param instance: The instance of the model being saved.
:param created: Boolean indicating whether a new instance was created.
:param kwargs: Additional keyword arguments provided by the signal.
:return: None
"""
if created:
entity = instance.dealer.entity
uom = entity.get_uom_all().get(unit_abbr=instance.uom)
cogs = entity.get_all_accounts().get(role=roles.COGS)
service_model = ItemModel.objects.create(
name=instance.name,
uom=uom,
default_amount=instance.price,
entity=entity,
is_product_or_service=True,
sold_as_unit=True,
is_active=True,
item_role="service",
for_inventory=False,
cogs_account=cogs,
)
instance.item = service_model
instance.save()
@receiver(post_save, sender=models.Lead)
def track_lead_status_change(sender, instance, **kwargs):
"""
Tracks changes in the status of a Lead instance and logs the transition into the
LeadStatusHistory model. This function is triggered after the `post_save` signal
is emitted for a Lead instance.
The function compares the `status` of the updated Lead instance with its previous
value. If the `status` has changed, it creates a new entry in the LeadStatusHistory
model, recording the old status, the new status, and the staff responsible for the change.
:param sender: The model class that sent the signal.
:type sender: Type[models.Model]
:param instance: The actual instance being saved. It represents the Lead instance
whose status is being tracked.
:type instance: models.Lead
:param kwargs: Additional keyword arguments passed by the signal. These can include
flags such as 'created' to indicate if the instance was newly created or updated.
:return: None
"""
if instance.pk: # Ensure the instance is being updated, not created
try:
old_lead = models.Lead.objects.get(pk=instance.pk)
if old_lead.status != instance.status: # Check if status has changed
models.LeadStatusHistory.objects.create(
lead=instance,
old_status=old_lead.status,
new_status=instance.status,
changed_by=instance.staff # Assuming the assigned staff made the change
)
except models.Lead.DoesNotExist:
pass # Ignore if the lead doesn't exist (e.g., during initial creation)
@receiver(post_save, sender=models.Lead)
def notify_assigned_staff(sender, instance, created, **kwargs):
"""
Signal handler that sends a notification to the staff member when a new lead is assigned.
This function is triggered when a Lead instance is saved. If the lead has been assigned
to a staff member, it creates a Notification object, notifying the staff member of the
new assignment.
:param sender: The model class that sent the signal.
:param instance: The instance of the model that was saved.
:param created: A boolean indicating whether a new instance was created.
:param kwargs: Additional keyword arguments.
:return: None
"""
if instance.staff: # Check if the lead is assigned
models.Notification.objects.create(
user=instance.staff.staff_member.user,
message=f"You have been assigned a new lead: {instance.full_name}."
)
@receiver(post_save, sender=models.CarReservation)
def update_car_status_on_reservation_create(sender, instance, created, **kwargs):
"""
Signal handler to update the status of a car upon the creation of a car reservation.
This function is triggered when a new instance of a CarReservation is created and saved
to the database. It modifies the status of the associated car to reflect the RESERVED status.
:param sender: The model class that sends the signal (CarReservation).
:param instance: The specific instance of the CarReservation that triggered the signal.
:param created: A boolean indicating whether the CarReservation instance was created.
:param kwargs: Additional keyword arguments passed by the signal.
:return: None
"""
if created:
car = instance.car
car.status = models.CarStatusChoices.RESERVED
car.save()
@receiver(post_delete, sender=models.CarReservation)
def update_car_status_on_reservation_delete(sender, instance, **kwargs):
"""
Signal handler that updates the status of a car to available when a car reservation
is deleted. If there are no active reservations associated with the car, the car's
status is updated to 'AVAILABLE'. This ensures the car's status accurately reflects
its reservability.
:param sender: The model class that triggers the signal (should always be
models.CarReservation in this context).
:type sender: type
:param instance: The instance of the deleted reservation.
:type instance: models.CarReservation
:param kwargs: Additional keyword arguments.
:type kwargs: dict
"""
car = instance.car
# Check if there are no active reservations for the car
if not car.reservations.filter(reserved_until__gt=now()).exists():
car.status = models.CarStatusChoices.AVAILABLE
car.save()
@receiver(post_save, sender=models.CarReservation)
def update_car_status_on_reservation_update(sender, instance, **kwargs):
"""
Handles the post-save signal for CarReservation model, updating the associated
car's status based on the reservation's activity status. If the reservation is
active, the car's status is updated to RESERVED. If the reservation is not
active, the car's status is set to AVAILABLE if there are no other active
reservations for the car.
:param sender: The model class that sent the signal.
:param instance: The CarReservation instance that triggered the signal.
:param kwargs: Additional keyword arguments passed by the signal.
:return: None
"""
car = instance.car
if instance.is_active:
car.status = models.CarStatusChoices.RESERVED
else:
if not car.reservations.filter(reserved_until__gt=now()).exists():
car.status = models.CarStatusChoices.AVAILABLE
car.save()
@receiver(post_save, sender=models.Dealer)
def create_dealer_settings(sender, instance, created, **kwargs):
"""
Triggered when a `Dealer` instance is saved. This function creates corresponding
`DealerSettings` for a newly created `Dealer` instance. The function assigns
default accounts for invoices and bills based on the role of the accounts
retrieved from the associated entity of the `Dealer`.
:param sender: The model class that triggered the signal, specifically `Dealer`.
:type sender: Type
:param instance: The actual instance of the `Dealer` that was saved.
:type instance: models.Dealer
:param created: A boolean indicating whether a new instance was created.
`True` if the instance was newly created; otherwise, `False`.
:type created: bool
:param kwargs: Additional keyword arguments passed by the signal.
:type kwargs: dict
:return: None
"""
if created:
models.DealerSettings.objects.create(
dealer=instance,
invoice_cash_account=instance.entity.get_all_accounts().filter(role=roles.ASSET_CA_CASH).first(),
invoice_prepaid_account=instance.entity.get_all_accounts().filter(role=roles.ASSET_CA_RECEIVABLES).first(),
invoice_unearned_account=instance.entity.get_all_accounts().filter(role=roles.LIABILITY_CL_DEFERRED_REVENUE).first(),
bill_cash_account=instance.entity.get_all_accounts().filter(role=roles.ASSET_CA_CASH).first(),
bill_prepaid_account=instance.entity.get_all_accounts().filter(role=roles.ASSET_CA_PREPAID).first(),
bill_unearned_account=instance.entity.get_all_accounts().filter(role=roles.LIABILITY_CL_ACC_PAYABLE).first()
)
# @receiver(post_save, sender=EstimateModel)
# def update_estimate_status(sender, instance,created, **kwargs):
# items = instance.get_itemtxs_data()[0].all()
# total = sum([Decimal(item.item_model.additional_info['car_finance']["selling_price"]) * Decimal(item.ce_quantity) for item in items])
# @receiver(post_save, sender=models.Schedule)
# def create_activity_on_schedule_creation(sender, instance, created, **kwargs):
# if created:
# models.Activity.objects.create(
# content_object=instance,
# activity_type='Schedule Created',
# created_by=instance.scheduled_by,
# notes=f"New schedule created for {instance.purpose} with {instance.lead.full_name} on {instance.scheduled_at}."
# )
# @receiver(post_save, sender=models.Staff)
# def check_users_quota(sender, instance, **kwargs):
# quota_dict = get_user_quota(instance.dealer.user)
# allowed_users = quota_dict.get("Users")
# if allowed_users is None:
# raise ValidationError(_("The user quota for staff members is not defined. Please contact support."))
# current_staff_count = instance.dealer.staff.count()
# if current_staff_count > allowed_users:
# raise ValidationError(_("You have reached the maximum number of staff users allowed for your plan."))
# @receiver(post_save, sender=models.Dealer)
# def create_vat(sender, instance, created, **kwargs):
# """
# Signal receiver that listens to the `post_save` signal for the `Dealer` model
# and handles the creation of a `VatRate` instance if it does not already exist.
# This function ensures that a default VAT rate is created with a specified rate
# and is marked as active. It is connected to the Django signals framework and
# automatically executes whenever a `Dealer` instance is saved.
# :param sender: The model class that triggered the signal (in this case, `Dealer`).
# :param instance: The instance of the model being saved.
# :param created: Boolean indicating whether a new instance was created.
# :param kwargs: Additional keyword arguments passed by the signal.
# :return: None
# """
# VatRate.objects.get_or_create(rate=Decimal('0.15'), is_active=True)
@receiver(post_save, sender=models.Dealer)
def create_make_ledger_accounts(sender, instance, created, **kwargs):
"""
Signal receiver that creates ledger accounts for car makes associated with a dealer when a new dealer instance
is created. This function listens to the `post_save` signal of the `Dealer` model and automatically generates
new ledger accounts for all car makes, associating them with the given dealer's entity.
:param sender: The model class (`Dealer`) that triggered the signal.
:type sender: Type[models.Dealer]
:param instance: The instance of the `Dealer` model that triggered the signal.
:param created: A boolean indicating whether a new `Dealer` instance was created.
:type created: bool
:param kwargs: Additional keyword arguments passed by the signal.
:return: None
"""
if created:
entity = instance.entity
coa = entity.get_default_coa()
# for make in models.CarMake.objects.all():
# last_account = entity.get_all_accounts().filter(role=roles.ASSET_CA_RECEIVABLES).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}"
# entity.create_account(
# name=make.name,
# code=code,
# role=roles.ASSET_CA_RECEIVABLES,
# coa_model=coa,
# balance_type="credit",
# active=True
# )
# @receiver(post_save, sender=VendorModel)
# def create_vendor_accounts(sender, instance, created, **kwargs):Dealer)
# if created:
# entity = instance.entity_model
# coa = entity.get_default_coa()
# 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.path)}{1:03d}"
# elif len(last_account.code) > 4:
# code = f"{int(last_account.path)+1}"
# entity.create_account(
# name=instance.vendor_name,
# code=code,
# role=roles.LIABILITY_CL_ACC_PAYABLE,
# coa_model=coa,
# balance_type="credit",
# active=True
# )
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.
: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()
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}"
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'
)
# 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.
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 ""
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)