Compare commits

..

No commits in common. "e5d09b6e0d92be6809ed59ce9ade18d7983a3077" and "62813388d76447b3be1aeb3790f0f699562af13f" have entirely different histories.

13 changed files with 626 additions and 689 deletions

View File

@ -1,4 +1,4 @@
# Generated by Django 4.2.20 on 2025-03-20 17:15 # Generated by Django 5.1.6 on 2025-03-06 01:43
from django.db import migrations, models from django.db import migrations, models

View File

@ -1,7 +1,7 @@
# Generated by Django 4.2.20 on 2025-03-20 17:15 # Generated by Django 5.1.6 on 2025-03-06 01:43
from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -9,8 +9,8 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('inventory', '0001_initial'),
('haikalbot', '0001_initial'), ('haikalbot', '0001_initial'),
('inventory', '0001_initial'),
] ]
operations = [ operations = [

View File

@ -5,8 +5,8 @@ class InventoryConfig(AppConfig):
name = 'inventory' name = 'inventory'
def ready(self): def ready(self):
import inventory.signals import inventory.signals
from decimal import Decimal # from decimal import Decimal
from inventory.models import VatRate # from inventory.models import VatRate
VatRate.objects.get_or_create(rate=Decimal('0.15'), is_active=True) # VatRate.objects.get_or_create(rate=Decimal('0.15'), is_active=True)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,18 @@
# Generated by Django 5.1.7 on 2025-03-16 19:09
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('inventory', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='saleorder',
name='payment_method',
field=models.CharField(choices=[('cash', 'Cash'), ('finance', 'Finance'), ('lease', 'Lease'), ('credit_card', 'Credit Card'), ('bank_transfer', 'Bank Transfer'), ('SADAD', 'SADAD')], max_length=20),
),
]

View File

@ -358,6 +358,8 @@ class Car(models.Model):
vendor = models.ForeignKey( vendor = models.ForeignKey(
VendorModel, VendorModel,
models.DO_NOTHING, models.DO_NOTHING,
null=True,
blank=True,
related_name="cars", related_name="cars",
verbose_name=_("Vendor"), verbose_name=_("Vendor"),
) )
@ -592,10 +594,6 @@ class CarFinance(models.Model):
def total(self): def total(self):
return self.selling_price return self.selling_price
@property
def total_additionals_no_vat(self):
return sum(x.price for x in self.additional_services.all())
@property @property
def total_additionals(self): def total_additionals(self):
return sum(x.price_ for x in self.additional_services.all()) return sum(x.price_ for x in self.additional_services.all())

View File

@ -19,9 +19,7 @@ from django_ledger.models import (
CustomerModel, CustomerModel,
JournalEntryModel, JournalEntryModel,
TransactionModel, TransactionModel,
LedgerModel, LedgerModel
BillModel,
ItemTransactionModel
) )
from . import models from . import models
from django.utils.timezone import now from django.utils.timezone import now
@ -625,7 +623,7 @@ def create_ledger_entity(sender, instance, created, **kwargs):
entity.create_account(coa_model=coa, code="6302", role=roles.EXPENSE_OTHER, name=_("Taxes"), balance_type="debit", active=True) entity.create_account(coa_model=coa, code="6302", role=roles.EXPENSE_OTHER, name=_("Taxes"), balance_type="debit", active=True)
entity.create_account(coa_model=coa, code="6303", role=roles.EXPENSE_OTHER, name=_("Foreign Currency Translation"), balance_type="debit", active=True) entity.create_account(coa_model=coa, code="6303", role=roles.EXPENSE_OTHER, name=_("Foreign Currency Translation"), balance_type="debit", active=True)
entity.create_account(coa_model=coa, code="6304", role=roles.EXPENSE_OTHER, name=_("Interest Expenses"), balance_type="debit", active=True) entity.create_account(coa_model=coa, code="6304", role=roles.EXPENSE_OTHER, name=_("Interest Expenses"), balance_type="debit", active=True)
@receiver(post_save, sender=models.Dealer) @receiver(post_save, sender=models.Dealer)
def create_dealer_groups(sender, instance, created, **kwargs): def create_dealer_groups(sender, instance, created, **kwargs):
@ -644,21 +642,23 @@ def create_dealer_groups(sender, instance, created, **kwargs):
def create_ledger_vendor(sender, instance, created, **kwargs): def create_ledger_vendor(sender, instance, created, **kwargs):
if created: if created:
entity = EntityModel.objects.filter(name=instance.dealer.name).first() entity = EntityModel.objects.filter(name=instance.dealer.name).first()
additionals = to_dict(instance)
entity.create_vendor( entity.create_vendor(
vendor_model_kwargs={ vendor_model_kwargs={
"vendor_name": instance.name, "vendor_name": instance.name,
"vendor_number": instance.crn, "vendor_number": instance.crn,
"address_1": instance.address, "address_1": instance.address,
"phone": instance.phone_number, "phone": instance.phone_number,
"email": instance.email,
"tax_id_number": instance.vrn, "tax_id_number": instance.vrn,
"active": True, "active": True,
"hidden": False, "hidden": False,
"additional_info": additionals, "additional_info": {
"arabic_name": instance.arabic_name,
"contact_person": instance.contact_person,
},
} }
) )
coa = entity.get_default_coa() coa = entity.get_default_coa()
last_account = entity.get_all_accounts().filter(role=roles.LIABILITY_CL_ACC_PAYABLE).order_by('-created').first() last_account = entity.get_all_accounts().filter(role=roles.LIABILITY_CL_ACC_PAYABLE).order_by('-created').first()
# code = f"{int(last_account.code)}{1:03d}" # code = f"{int(last_account.code)}{1:03d}"
@ -674,39 +674,27 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
coa_model=coa, coa_model=coa,
balance_type="credit", balance_type="credit",
active=True active=True
) )
print(f"VendorModel created for Vendor: {instance.name}") print(f"VendorModel created for Vendor: {instance.name}")
else:
additionals = to_dict(instance)
entity.get_vendors().filter(email=instance.email).first().update(
vendor_name= instance.name,
vendor_number= instance.crn,
address_1= instance.address,
phone= instance.phone_number,
email= instance.email,
tax_id_number= instance.vrn,
additional_info= additionals,
)
@receiver(post_save, sender=models.CustomerModel) @receiver(post_save, sender=models.CustomerModel)
def create_customer_user(sender, instance, created, **kwargs): def create_customer_user(sender, instance, created, **kwargs):
if created: if created:
try: first_name = instance.additional_info.get("customer_info").get("first_name")
first_name = instance.additional_info.get("customer_info").get("first_name") last_name = instance.additional_info.get("customer_info").get("last_name")
last_name = instance.additional_info.get("customer_info").get("last_name") user = User.objects.create(
user = User.objects.create( username=instance.email,
username=instance.email, email=instance.email,
email=instance.email, first_name=first_name if first_name else '',
first_name=first_name if first_name else '', last_name=last_name if last_name else '',
last_name=last_name if last_name else '', )
) instance.additional_info.update({"user_info": to_dict(user)})
instance.additional_info.update({"user_info": to_dict(user)}) user.set_unusable_password()
user.set_unusable_password() user.save()
user.save() instance.user = user
instance.user = user instance.save()
instance.save()
except Exception as e:
print(e)
# Create Item # Create Item
@receiver(post_save, sender=models.Car) @receiver(post_save, sender=models.Car)
@ -910,6 +898,11 @@ def create_dealer_settings(sender, instance, created, **kwargs):
bill_prepaid_account=instance.entity.get_all_accounts().filter(role=roles.ASSET_CA_PREPAID).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() bill_unearned_account=instance.entity.get_all_accounts().filter(role=roles.LIABILITY_CL_ACC_PAYABLE).first()
) )
@receiver(post_save, sender=models.Dealer)
def check_if_vat_exists(sender, instance, created, **kwargs):
if created:
models.VatRate.objects.get_create(is_active=True)
# @receiver(post_save, sender=EstimateModel) # @receiver(post_save, sender=EstimateModel)
# def update_estimate_status(sender, instance,created, **kwargs): # def update_estimate_status(sender, instance,created, **kwargs):
@ -941,106 +934,98 @@ def create_dealer_settings(sender, instance, created, **kwargs):
@receiver(post_save, sender=models.Dealer) @receiver(post_save, sender=models.Dealer)
def create_make_ledger_accounts(sender, instance, created, **kwargs): def create_make_ledger_accounts(sender, instance, created, **kwargs):
if created: if created:
entity = instance.entity entity_name = instance.user.dealer.name
coa = entity.get_default_coa() entity = EntityModel.objects.get(name=entity_name)
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}"
for make in models.CarMake.objects.all():
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) # @receiver(post_save, sender=VendorModel)
# def create_vendor_accounts(sender, instance, created, **kwargs): # def create_vendor_accounts(sender, instance, created, **kwargs):
# if created: # if created:
# entity = instance.entity_model # 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() # last_account = entity.get_all_accounts().filter(role=roles.LIABILITY_CL_ACC_PAYABLE).order_by('-created').first()
# if len(last_account.code) == 4: # code = str(int(last_account.code) + 1)
# code = f"{int(last_account.path)}{1:03d}" # account = entity.create_account(
# elif len(last_account.code) > 4:
# code = f"{int(last_account.path)+1}"
# entity.create_account(
# name=instance.vendor_name, # name=instance.vendor_name,
# code=code, # code=code,
# role=roles.LIABILITY_CL_ACC_PAYABLE, # role=roles.LIABILITY_CL_ACC_PAYABLE,
# coa_model=coa, # coa_model=entity.get_default_coa(),
# balance_type="credit", # balance_type="credit",
# active=True # active=True
# ) # )
def save_journal(car_finance,ledger,vendor):
entity = ledger.entity
journal = JournalEntryModel.objects.create(
posted=False,
description=f"Finances of Car:{car_finance.car.vin} for Vendor:{car_finance.car.vendor.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().get(name=vendor.vendor_name)
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) @receiver(post_save, sender=models.CarFinance)
def update_finance_cost(sender, instance, created, **kwargs): def update_finance_cost(sender, instance, created, **kwargs):
if created: entity = instance.car.dealer.entity
entity = instance.car.dealer.entity ledger,created = LedgerModel.objects.get_or_create(name=instance.car.vin, entity=entity)
vendor = instance.car.vendor vendor = instance.car.vendor
name = f"{instance.car.vin}-{instance.car.id_car_make.name}-{instance.car.id_car_model.name}-{instance.car.year}-{vendor.vendor_name}"
ledger,_ = LedgerModel.objects.get_or_create(name=name, entity=entity)
save_journal(instance,ledger,vendor)
# if not created: if created:
# if ledger.additional_info.get("je_number"): journal = JournalEntryModel.objects.create(
# journal = JournalEntryModel.objects.filter(je_number=ledger.additional_info.get("je_number")).first() posted=False,
# journal.description = f"Finances of Car:{instance.car.vin} for Vendor:{instance.car.vendor.vendor_name}" description=f"Finances of Car:{instance.car.vin} for Vendor:{instance.car.vendor.vendor_name}",
# journal.save() ledger=ledger,
# debit = journal.get_transaction_queryset().filter(tx_type='debit').first() locked=False,
# credit = journal.get_transaction_queryset().filter(tx_type='credit').first() origin="Payment",
# if debit and credit: )
# if journal.is_locked(): ledger.additional_info["je_number"] = journal.je_number
# journal.mark_as_unlocked() ledger.save()
# journal.save()
# debit.amount = instance.cost_price inventory_account = entity.get_default_coa_accounts().filter(role=roles.ASSET_CA_INVENTORY).first()
# credit.amount = instance.cost_price vendor_account = entity.get_default_coa_accounts().get(name=vendor.vendor_name)
# debit.save()
# credit.save() # Debit Inventory Account
# else: TransactionModel.objects.create(
# save_journal(instance,ledger,vendor,journal=journal) journal_entry=journal,
# else: account=inventory_account,
# save_journal(instance,ledger,vendor) amount=instance.total + instance.total_additionals,
# else: tx_type='debit'
# save_journal(instance,ledger,vendor) )
# Credit Vendor Account
TransactionModel.objects.create(
journal_entry=journal,
account=vendor_account,
amount=instance.cost_price,
tx_type='credit'
)
else:
if not ledger.additional_info.get("je_number"):
journal = JournalEntryModel.objects.create(
posted=False,
description=f"Finances of Car:{instance.car.vin} for Vendor:{instance.car.vendor.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().get(name=vendor.vendor_name, active=True)
# Debit Inventory Account
TransactionModel.objects.create(
journal_entry=journal,
account=inventory_account,
amount=instance.cost_price,
tx_type='debit'
)
# Credit Vendor Account
TransactionModel.objects.create(
journal_entry=journal,
account=vendor_account,
amount=instance.cost_price,
tx_type='credit'
)
else:
journal = JournalEntryModel.objects.filter(je_number=ledger.additional_info.get("je_number")).first()
debit = journal.get_transaction_queryset().filter(tx_type='debit').first()
credit = journal.get_transaction_queryset().filter(tx_type='credit').first()
debit.amount = instance.cost_price
credit.amount = instance.cost_price
debit.save()
credit.save()

View File

@ -134,7 +134,7 @@ urlpatterns = [
views.lead_transfer, views.lead_transfer,
name="lead_transfer", name="lead_transfer",
), ),
path( path(
"crm/opportunities/<int:pk>/add_note/", "crm/opportunities/<int:pk>/add_note/",
views.add_note_to_opportunity, views.add_note_to_opportunity,
@ -346,7 +346,7 @@ path(
# views.payment_create, # views.payment_create,
# name="payment_create", # name="payment_create",
# ), # ),
# Users URLs # Users URLs
path("user/create/", views.UserCreateView.as_view(), name="user_create"), path("user/create/", views.UserCreateView.as_view(), name="user_create"),
path("user/<int:pk>/update/", views.UserUpdateView.as_view(), name="user_update"), path("user/<int:pk>/update/", views.UserUpdateView.as_view(), name="user_update"),
@ -415,7 +415,7 @@ path(
# Ledger # Ledger
path( path(
"ledgers/", views.LedgerModelListView.as_view(), name="ledger_list" "ledgers/", views.LedgerModelListView.as_view(), name="ledger_list"
), ),
path( path(
"ledgers/<slug:entity_slug>/detail/<uuid:pk>/", views.LedgerModelDetailView.as_view(), name="ledger_detail" "ledgers/<slug:entity_slug>/detail/<uuid:pk>/", views.LedgerModelDetailView.as_view(), name="ledger_detail"
), ),
@ -436,10 +436,10 @@ path(
# ), # ),
path( path(
"journalentries/<uuid:pk>/list/", views.JournalEntryListView.as_view(), name="journalentry_list" "journalentries/<uuid:pk>/list/", views.JournalEntryListView.as_view(), name="journalentry_list"
), ),
path( path(
"journalentries/<uuid:pk>/create/", views.JournalEntryCreateView.as_view(), name="journalentry_create" "journalentries/<uuid:pk>/create/", views.JournalEntryCreateView.as_view(), name="journalentry_create"
), ),
path( path(
"journalentries/<uuid:pk>/delete/", views.JournalEntryDeleteView, name="journalentry_delete" "journalentries/<uuid:pk>/delete/", views.JournalEntryDeleteView, name="journalentry_delete"
), ),
@ -640,11 +640,11 @@ path(
views.bill_mark_as_paid, views.bill_mark_as_paid,
name="bill_mark_as_paid", name="bill_mark_as_paid",
), ),
# orders # orders
path("orders/", views.OrderListView.as_view(), name="order_list_view"), path("orders/", views.OrderListView.as_view(), name="order_list_view"),
# BALANCE SHEET Reports... # BALANCE SHEET Reports...
# Entities... # Entities...
path('entity/<slug:entity_slug>/balance-sheet/', path('entity/<slug:entity_slug>/balance-sheet/',

View File

@ -97,7 +97,7 @@ def send_email(from_, to_, subject, message):
send_mail(subject, message, from_email, recipient_list) send_mail(subject, message, from_email, recipient_list)
def get_user_type(request): def get_user_type(request):
if request.is_dealer: if request.is_dealer:
return request.user.dealer return request.user.dealer
elif request.is_staff: elif request.is_staff:
@ -286,7 +286,7 @@ def get_financial_values(model):
def set_invoice_payment(dealer, entity, invoice, amount, payment_method): def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
calculator = CarFinanceCalculator(invoice) calculator = CarFinanceCalculator(invoice)
finance_data = calculator.get_finance_data() finance_data = calculator.get_finance_data()
# journal = JournalEntryModel.objects.create( # journal = JournalEntryModel.objects.create(
# posted=False, # posted=False,
@ -295,11 +295,11 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
# locked=False, # locked=False,
# origin="Payment", # origin="Payment",
# ) # )
# credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue") # credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue")
# debit_account = entity.get_default_coa_accounts().get(name="Cash", active=True) # debit_account = entity.get_default_coa_accounts().get(name="Cash", active=True)
# vat_payable_account = entity.get_default_coa_accounts().get(name="VAT Payable", active=True) # vat_payable_account = entity.get_default_coa_accounts().get(name="VAT Payable", active=True)
# TransactionModel.objects.create( # TransactionModel.objects.create(
# journal_entry=journal, # journal_entry=journal,
# account=debit_account, # Debit Account # account=debit_account, # Debit Account
@ -316,7 +316,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method):
# description="Payment Received", # description="Payment Received",
# ) # )
# TransactionModel.objects.create( # TransactionModel.objects.create(
# journal_entry=journal, # journal_entry=journal,
# account=vat_payable_account, # Credit VAT Payable # account=vat_payable_account, # Credit VAT Payable
@ -457,7 +457,7 @@ class CarTransfer:
self._add_car_item_to_invoice() self._add_car_item_to_invoice()
def _add_car_item_to_invoice(self): def _add_car_item_to_invoice(self):
self.item = self.from_dealer.entity.get_items_products().filter(name=self.car.vin).first() self.item = self.from_dealer.entity.get_items_products().filter(name=self.car.vin).first()
if not self.item: if not self.item:
return return
@ -475,7 +475,7 @@ class CarTransfer:
commit=True, commit=True,
operation=InvoiceModel.ITEMIZE_APPEND, operation=InvoiceModel.ITEMIZE_APPEND,
) )
if self.invoice.can_review(): if self.invoice.can_review():
self.invoice.mark_as_review() self.invoice.mark_as_review()
self.invoice.mark_as_approved(self.from_dealer.entity.slug, self.from_dealer.entity.admin) self.invoice.mark_as_approved(self.from_dealer.entity.slug, self.from_dealer.entity.admin)
@ -759,26 +759,26 @@ class CarFinanceCalculator:
"make": car_info.get('make'), "make": car_info.get('make'),
"model": car_info.get('model'), "model": car_info.get('model'),
"year": car_info.get('year'), "year": car_info.get('year'),
"trim": car_info.get('trim'), "trim": car_info.get('trim'),
"mileage": car_info.get('mileage'), "mileage": car_info.get('mileage'),
"cost_price": car_finance.get('cost_price'), "cost_price": car_finance.get('cost_price'),
"selling_price": car_finance.get('selling_price'), "selling_price": car_finance.get('selling_price'),
"discount": car_finance.get('discount_amount'), "discount": car_finance.get('discount_amount'),
"quantity": quantity, "quantity": quantity,
"unit_price": unit_price, "unit_price": unit_price,
"total": unit_price * Decimal(quantity), "total": unit_price * Decimal(quantity),
"total_vat": car_finance.get('total_vat'), "total_vat": car_finance.get('total_vat'),
"additional_services": self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY), "additional_services": self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY),
} }
def _get_additional_services(self): def _get_additional_services(self):
return [ return [
{"name": service.get('name'), "price": service.get('price'), "taxable": service.get('taxable'),"price_": service.get('price_')} {"name": service.get('name'), "price": service.get('price'), "taxable": service.get('taxable'),"price_": service.get('price_')}
for item in self.item_transactions for item in self.item_transactions
for service in self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY) or [] for service in self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY) or []
] ]
def calculate_totals(self): def calculate_totals(self):
total_price = sum( total_price = sum(
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'selling_price')) * Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'selling_price')) *
@ -786,14 +786,14 @@ class CarFinanceCalculator:
for item in self.item_transactions for item in self.item_transactions
) )
total_additionals = sum(Decimal(x.get('price_')) for x in self._get_additional_services()) total_additionals = sum(Decimal(x.get('price_')) for x in self._get_additional_services())
total_discount = sum( total_discount = sum(
Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'discount_amount')) Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'discount_amount'))
for item in self.item_transactions for item in self.item_transactions
) )
total_price_discounted = total_price - total_discount total_price_discounted = total_price - total_discount
total_vat_amount = total_price_discounted * self.vat_rate total_vat_amount = total_price_discounted * self.vat_rate
return { return {
"total_price": round(total_price_discounted, 2), # total_price_discounted, "total_price": round(total_price_discounted, 2), # total_price_discounted,
"total_vat_amount": round(total_vat_amount, 2), # total_vat_amount, "total_vat_amount": round(total_vat_amount, 2), # total_vat_amount,
@ -856,7 +856,7 @@ def handle_account_process(invoice,amount,finance_data):
car = models.Car.objects.get(vin=invoice.get_itemtxs_data()[0].first().item_model.name) car = models.Car.objects.get(vin=invoice.get_itemtxs_data()[0].first().item_model.name)
entity = invoice.ledger.entity entity = invoice.ledger.entity
coa = entity.get_default_coa() coa = entity.get_default_coa()
make_account = entity.get_all_accounts().filter(name=car.id_car_make.name,role=roles.COGS).first() make_account = entity.get_all_accounts().filter(name=car.id_car_make.name,role=roles.COGS).first()
if not make_account: if not make_account:
last_account = entity.get_all_accounts().filter(role=roles.COGS).order_by('-created').first() last_account = entity.get_all_accounts().filter(role=roles.COGS).order_by('-created').first()
@ -864,8 +864,8 @@ def handle_account_process(invoice,amount,finance_data):
code = f"{int(last_account.code)}{1:03d}" code = f"{int(last_account.code)}{1:03d}"
elif len(last_account.code) > 4: elif len(last_account.code) > 4:
code = f"{int(last_account.code)+1}" code = f"{int(last_account.code)+1}"
make_account = entity.create_account( make_account = entity.create_account(
name=car.id_car_make.name, name=car.id_car_make.name,
code=code, code=code,
role=roles.COGS, role=roles.COGS,
@ -873,7 +873,7 @@ def handle_account_process(invoice,amount,finance_data):
balance_type="debit", balance_type="debit",
active=True active=True
) )
# get or create additional services account # get or create additional services account
additional_services_account = entity.get_default_coa_accounts().filter(name="Additional Services",role=roles.COGS).first() additional_services_account = entity.get_default_coa_accounts().filter(name="Additional Services",role=roles.COGS).first()
if not additional_services_account: if not additional_services_account:
@ -882,8 +882,8 @@ def handle_account_process(invoice,amount,finance_data):
code = f"{int(last_account.code)}{1:03d}" code = f"{int(last_account.code)}{1:03d}"
elif len(last_account.code) > 4: elif len(last_account.code) > 4:
code = f"{int(last_account.code)+1}" code = f"{int(last_account.code)+1}"
additional_services_account = entity.create_account( additional_services_account = entity.create_account(
name="Additional Services", name="Additional Services",
code=code, code=code,
role=roles.COGS, role=roles.COGS,
@ -891,12 +891,12 @@ def handle_account_process(invoice,amount,finance_data):
balance_type="debit", balance_type="debit",
active=True active=True
) )
inventory_account = entity.get_default_coa_accounts().filter(role=roles.ASSET_CA_INVENTORY).first() inventory_account = entity.get_default_coa_accounts().filter(role=roles.ASSET_CA_INVENTORY).first()
vat_payable_account = entity.get_default_coa_accounts().get(name="VAT Payable", active=True) vat_payable_account = entity.get_default_coa_accounts().get(name="VAT Payable", active=True)
journal = JournalEntryModel.objects.create( journal = JournalEntryModel.objects.create(
posted=False, posted=False,
description=f"Payment for Invoice {invoice.invoice_number}", description=f"Payment for Invoice {invoice.invoice_number}",
@ -904,7 +904,7 @@ def handle_account_process(invoice,amount,finance_data):
locked=False, locked=False,
origin="Payment", origin="Payment",
) )
TransactionModel.objects.create( TransactionModel.objects.create(
journal_entry=journal, journal_entry=journal,
account=make_account, # Debit car make Account account=make_account, # Debit car make Account
@ -926,38 +926,12 @@ def handle_account_process(invoice,amount,finance_data):
amount=Decimal(car.finances.total), amount=Decimal(car.finances.total),
tx_type="credit", tx_type="credit",
description="Account Adjustment", description="Account Adjustment",
) )
TransactionModel.objects.create( TransactionModel.objects.create(
journal_entry=journal, journal_entry=journal,
account=vat_payable_account, # Credit VAT Payable account=vat_payable_account, # Credit VAT Payable
amount=finance_data.get("total_vat_amount"), amount=finance_data.get("total_vat_amount"),
tx_type="credit", tx_type="credit",
description="VAT Payable on Invoice", description="VAT Payable on Invoice",
)
def create_make_accounts(dealer):
entity = dealer.entity
coa = entity.get_default_coa()
# Create a unique account name for the dealer and car make combination
makes = models.DealersMake.objects.filter(dealer=dealer).all()
for make in makes:
account_name = f"{make.car_make.name} Inventory Account"
account = entity.get_all_accounts().filter(coa_model=coa,name=account_name).first()
if not account:
last_account = entity.get_all_accounts().filter(role=roles.ASSET_CA_INVENTORY).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}"
account = entity.create_account(
name=account_name,
code=code,
role=roles.ASSET_CA_INVENTORY,
coa_model=coa,
balance_type="credit",
active=True
) )

View File

@ -127,7 +127,6 @@ from .services import (
) )
from .utils import ( from .utils import (
CarFinanceCalculator, CarFinanceCalculator,
create_make_accounts,
get_car_finance_data, get_car_finance_data,
get_financial_values, get_financial_values,
get_item_transactions, get_item_transactions,
@ -137,7 +136,6 @@ from .utils import (
set_bill_payment, set_bill_payment,
set_invoice_payment, set_invoice_payment,
CarTransfer, CarTransfer,
to_dict,
) )
##################################################################### #####################################################################
@ -1506,31 +1504,13 @@ class VendorUpdateView(
SuccessMessageMixin, SuccessMessageMixin,
UpdateView, UpdateView,
): ):
model = VendorModel model = models.Vendor
form_class = forms.VendorForm form_class = forms.VendorForm
template_name = "vendors/vendor_form.html" template_name = "vendors/vendor_form.html"
success_url = reverse_lazy("vendor_list") success_url = reverse_lazy("vendor_list")
success_message = _("Vendor updated successfully.") success_message = _("Vendor updated successfully.")
def get_initial(self):
initial = super().get_initial()
initial = self.object.additional_info
return initial
def form_valid(self, form):
instance = form.save(commit=False)
instance.vendor_name = self.request.POST["name"]
instance.vendor_number = self.request.POST["crn"]
instance.address_1 = self.request.POST["address"]
instance.phone = self.request.POST["phone_number"]
instance.email = self.request.POST["email"]
instance.tax_id_number = self.request.POST["vrn"]
additionals = form.cleaned_data
additionals['phone_number'] = str(additionals['phone_number'])
instance.additional_info = additionals
instance.save()
return super().form_valid(form)
@login_required @login_required
def delete_vendor(request, pk): def delete_vendor(request, pk):
vendor = get_object_or_404(models.Vendor, pk=pk) vendor = get_object_or_404(models.Vendor, pk=pk)
@ -4072,7 +4052,6 @@ def assign_car_makes(request):
form = forms.DealersMakeForm(request.POST, dealer=dealer) form = forms.DealersMakeForm(request.POST, dealer=dealer)
if form.is_valid(): if form.is_valid():
form.save() form.save()
create_make_accounts(dealer)
return redirect("dealer_detail", pk=dealer.pk) return redirect("dealer_detail", pk=dealer.pk)
else: else:
# Pre-fill the form with existing selections # Pre-fill the form with existing selections
@ -4091,9 +4070,8 @@ class LedgerModelListView(LoginRequiredMixin, ListView,ArchiveIndexView):
show_all = False show_all = False
show_current = False show_current = False
show_visible = False show_visible = False
allow_empty = True
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
@ -4113,51 +4091,51 @@ class LedgerModelDetailView(LoginRequiredMixin, DetailView):
model = LedgerModel model = LedgerModel
context_object_name = "ledger" context_object_name = "ledger"
template_name = "ledger/ledger/ledger_detail.html" template_name = "ledger/ledger/ledger_detail.html"
# class LedgerModelCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView): # class LedgerModelCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView):
# model = LedgerModel # model = LedgerModel
# template_name = "ledger/ledger/ledger_form.html" # template_name = "ledger/ledger/ledger_form.html"
# form_class = forms.LedgerModelCreateForm # form_class = forms.LedgerModelCreateForm
# success_message = "Ledger created" # success_message = "Ledger created"
# def get_form(self, form_class=None): # def get_form(self, form_class=None):
# dealer = get_user_type(self.request) # dealer = get_user_type(self.request)
# form = forms.LedgerModelCreateForm(entity_slug=dealer.entity.slug,user_model=dealer.entity.admin,**self.get_form_kwargs()) # form = forms.LedgerModelCreateForm(entity_slug=dealer.entity.slug,user_model=dealer.entity.admin,**self.get_form_kwargs())
# return form # return form
# def get_success_url(self): # def get_success_url(self):
# return reverse('ledger_list') # return reverse('ledger_list')
# def form_valid(self, form): # def form_valid(self, form):
# instance = form.save(commit=False) # instance = form.save(commit=False)
# dealer = get_user_type(self.request) # dealer = get_user_type(self.request)
# instance.entity = dealer.entity # instance.entity = dealer.entity
# instance.save() # instance.save()
# return super().form_valid(form) # return super().form_valid(form)
class JournalEntryListView(LoginRequiredMixin, ListView): class JournalEntryListView(LoginRequiredMixin, ListView):
model = JournalEntryModel model = JournalEntryModel
context_object_name = "journal_entries" context_object_name = "journal_entries"
template_name = "ledger/journal_entry/journal_entry_list.html" template_name = "ledger/journal_entry/journal_entry_list.html"
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset() qs = super().get_queryset()
ledger = LedgerModel.objects.filter(pk=self.kwargs['pk']).first() ledger = LedgerModel.objects.filter(pk=self.kwargs['pk']).first()
qs = qs.filter(ledger=ledger) qs = qs.filter(ledger=ledger)
return qs return qs
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context['ledger'] = LedgerModel.objects.filter(pk=self.kwargs['pk']).first() context['ledger'] = LedgerModel.objects.filter(pk=self.kwargs['pk']).first()
return context return context
class JournalEntryCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView): class JournalEntryCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView):
model = JournalEntryModel model = JournalEntryModel
template_name = "ledger/journal_entry/journal_entry_form.html" template_name = "ledger/journal_entry/journal_entry_form.html"
form_class = forms.JournalEntryModelCreateForm form_class = forms.JournalEntryModelCreateForm
ledger_model = None ledger_model = None
success_message = "Journal Entry created" success_message = "Journal Entry created"
def get_form(self, form_class=None): def get_form(self, form_class=None):
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
ledger = LedgerModel.objects.filter(pk=self.kwargs['pk']).first() ledger = LedgerModel.objects.filter(pk=self.kwargs['pk']).first()
@ -4172,11 +4150,11 @@ class JournalEntryCreateView(LoginRequiredMixin,SuccessMessageMixin, CreateView)
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
context["ledger"] = LedgerModel.objects.filter(pk=self.kwargs['pk']).first() context["ledger"] = LedgerModel.objects.filter(pk=self.kwargs['pk']).first()
return context return context
def get_success_url(self): def get_success_url(self):
ledger = LedgerModel.objects.filter(pk=self.kwargs['pk']).first() ledger = LedgerModel.objects.filter(pk=self.kwargs['pk']).first()
return reverse("journalentry_list", kwargs={"pk": ledger.pk}) return reverse("journalentry_list", kwargs={"pk": ledger.pk})
def JournalEntryDeleteView(request,pk): def JournalEntryDeleteView(request,pk):
journal_entry = get_object_or_404(JournalEntryModel, pk=pk) journal_entry = get_object_or_404(JournalEntryModel, pk=pk)
@ -4204,7 +4182,7 @@ def JournalEntryTransactionsView(request, pk):
class JournalEntryModelTXSDetailView(JournalEntryModelTXSDetailViewBase): class JournalEntryModelTXSDetailView(JournalEntryModelTXSDetailViewBase):
template_name = 'ledger/journal_entry/journal_entry_txs.html' template_name = 'ledger/journal_entry/journal_entry_txs.html'
def ledger_lock_all_journals(request,entity_slug,pk): def ledger_lock_all_journals(request,entity_slug,pk):
ledger = LedgerModel.objects.filter(pk=pk).first() ledger = LedgerModel.objects.filter(pk=pk).first()
@ -4221,7 +4199,7 @@ def ledger_unlock_all_journals(request,entity_slug,pk):
if not ledger.is_locked(): if not ledger.is_locked():
messages.error(request, "Ledger is already Unlocked.") messages.error(request, "Ledger is already Unlocked.")
return redirect("journalentry_list", pk=ledger.pk) return redirect("journalentry_list", pk=ledger.pk)
ledger.unlock() ledger.unlock()
ledger.save() ledger.save()
qs = ledger.journal_entries.locked() qs = ledger.journal_entries.locked()
@ -4246,10 +4224,10 @@ def ledger_unpost_all_journals(request,entity_slug,pk):
messages.error(request, "Ledger is already Unposted.") messages.error(request, "Ledger is already Unposted.")
return redirect("journalentry_list", pk=ledger.pk) return redirect("journalentry_list", pk=ledger.pk)
qs = ledger.journal_entries.posted() qs = ledger.journal_entries.posted()
for je in qs: for je in qs:
je.mark_as_unposted() je.mark_as_unposted()
je.save() je.save()
ledger.unpost() ledger.unpost()
ledger.save() ledger.save()
return redirect("journalentry_list", pk=ledger.pk) return redirect("journalentry_list", pk=ledger.pk)

View File

@ -29,6 +29,8 @@ commonmark
contourpy contourpy
crispy-bootstrap5 crispy-bootstrap5
cryptography cryptography
cssselect2
ctranslate2
cycler cycler
Cython Cython
decorator decorator

View File

@ -1,5 +1,4 @@
from inventory.models import * from inventory.models import *
from django_ledger.models import VendorModel
from rich import print from rich import print
import random import random
import datetime import datetime
@ -9,9 +8,6 @@ from inventory.services import decodevin
def run(): def run():
# car = Car.objects.filter(vin='2C3HD46R4WH170267') # car = Car.objects.filter(vin='2C3HD46R4WH170267')
dealer = Dealer.objects.first()
vendors = [VendorModel.objects.create(vendor_name=f'vendor{i}',entity_model=dealer.entity) for i in range(1, 5)]
vin_list = [ vin_list = [
"1B3ES56C13D120225", "1B3ES56C13D120225",
"1GB4KYC86FF131536", "1GB4KYC86FF131536",
@ -28,8 +24,8 @@ def run():
] ]
for vin in vin_list: for vin in vin_list:
try: try:
for _ in range(5): for _ in range(15):
dealer = Dealer.objects.get(user__email="ismail.mosa.ibrahim@gmail.com")
vin = f"{vin[:-4]}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}" vin = f"{vin[:-4]}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}{random.randint(0, 9)}"
result = decodevin(vin) result = decodevin(vin)
make = CarMake.objects.get(name=result["maker"]) make = CarMake.objects.get(name=result["maker"])
@ -39,14 +35,13 @@ def run():
year = result["modelYear"] year = result["modelYear"]
serie = random.choice(model.carserie_set.all()) serie = random.choice(model.carserie_set.all())
trim = random.choice(serie.cartrim_set.all()) trim = random.choice(serie.cartrim_set.all())
vendor = random.choice(vendors)
car = Car.objects.create( car = Car.objects.create(
vin=vin, vin=vin,
id_car_make=make, id_car_make=make,
id_car_model=model, id_car_model=model,
id_car_serie=serie, id_car_serie=serie,
id_car_trim=trim, id_car_trim=trim,
vendor=vendor,
year=(int(year) or 2025), year=(int(year) or 2025),
receiving_date=datetime.datetime.now(), receiving_date=datetime.datetime.now(),
dealer=dealer, dealer=dealer,

View File

@ -118,7 +118,7 @@
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.vendor_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.id}}</span> <p class="mb-0 text-body-highlight fw-semibold fs-9 me-2">{{ vendor.vendor_name }}</p><span class="badge badge-phoenix badge-phoenix-primary">{{ vendor.id}}</span>
</div> </div>
</div> </div>
</div> </div>
</td> </td>
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="">{{ vendor.email }}</a></td> <td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent"><a class="text-body-highlight" href="">{{ vendor.email }}</a></td>
@ -130,12 +130,11 @@
<td class="align-middle white-space-nowrap text-end pe-0 ps-4"> <td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static"> <div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button> <button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"> <div class="dropdown-menu dropdown-menu-end py-2"><a href="" class="dropdown-item text-success-dark">
<a href="{% url 'vendor_update' vendor.pk %}" class="dropdown-item text-success-dark">
{% trans "Edit" %} {% trans "Edit" %}
</a> </a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button> <div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
</div> </div>
</div> </div>
</td> </td>
</tr> </tr>