diff --git a/inventory/migrations/0012_carfinance_is_sold.py b/inventory/migrations/0012_carfinance_is_sold.py new file mode 100644 index 00000000..24d73328 --- /dev/null +++ b/inventory/migrations/0012_carfinance_is_sold.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.7 on 2025-05-25 14:19 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('inventory', '0011_alter_car_item_model'), + ] + + operations = [ + migrations.AddField( + model_name='carfinance', + name='is_sold', + field=models.BooleanField(default=False), + ), + ] diff --git a/inventory/models.py b/inventory/models.py index 54d27aac..9e8066a4 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -612,6 +612,13 @@ class Car(Base): specs = CarSpecificationValue.objects.filter(id_car_trim=self.id_car_trim) return specs + def get_inventory_account(self): + return self.dealer.entity.get_all_accounts().filter(name=f"Inventory:{self.id_car_make.name}").first() + def get_revenue_account(self): + return self.dealer.entity.get_all_accounts().filter(name=f"Revenue:{self.id_car_make.name}").first() + def get_cogs_account(self): + return self.dealer.entity.get_all_accounts().filter(name=f"Cogs:{self.id_car_make.name}").first() + class CarTransfer(models.Model): car = models.ForeignKey( "Car", @@ -709,6 +716,8 @@ class CarFinance(models.Model): verbose_name=_("Discount Amount"), default=Decimal("0.00"), ) + is_sold = models.BooleanField(default=False) + @property def total(self): diff --git a/inventory/signals.py b/inventory/signals.py index c1d51e90..76a98af7 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -1,4 +1,5 @@ -from inventory.tasks import create_coa_accounts +from decimal import Decimal +from inventory.tasks import create_coa_accounts, create_make_accounts from django.contrib.auth.models import Group from django.db.models.signals import post_save, post_delete from django.dispatch import receiver @@ -10,7 +11,8 @@ from django_ledger.models import ( ItemModel, JournalEntryModel, TransactionModel, - LedgerModel + LedgerModel, + AccountModel ) from . import models from django.utils.timezone import now @@ -208,16 +210,21 @@ def create_item_model(sender, instance, created, **kwargs): uom = entity.get_uom_all().get(name="Unit") if not instance.item_model: - product = entity.create_item_product( + inventory = entity.create_item_product( name=instance.vin, item_type=ItemModel.ITEM_TYPE_MATERIAL, uom_model=uom, coa_model=coa, ) - instance.item_model = product - product.additional_info = {} - product.additional_info.update({'car_info': instance.to_dict()}) - product.save() + # inventory = entity.create_item_inventory( + # name=instance.vin, + # uom_model=uom, + # item_type=ItemModel.ITEM_TYPE_LUMP_SUM + # ) + instance.item_model = inventory + inventory.additional_info = {} + inventory.additional_info.update({'car_info': instance.to_dict()}) + inventory.save() else: instance.item_model.additional_info.update({'car_info': instance.to_dict()}) instance.item_model.save() @@ -237,7 +244,42 @@ def update_item_model_cost(sender, instance, created, **kwargs): :param kwargs: Additional keyword arguments passed during the signal invocation. :return: None """ + if created and not instance.is_sold: + 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") + + cash_account = entity.get_all_accounts().filter(name="Cash",role=roles.ASSET_CA_CASH).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="", + ) + + 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.selling_price if not isinstance(instance.car.item_model.additional_info, dict): @@ -695,7 +737,6 @@ def save_journal(car_finance,ledger,vendor): account=vendor_account, amount=car_finance.cost_price, tx_type='credit', - ) @receiver(post_save, sender=models.CarFinance) diff --git a/inventory/tasks.py b/inventory/tasks.py index c608a551..ee9ee7b2 100644 --- a/inventory/tasks.py +++ b/inventory/tasks.py @@ -560,28 +560,37 @@ def create_accounts_for_make(dealer,makes): entity = dealer.entity coa = entity.get_default_coa() + name = ["Inventory", "Revenue", "Cogs"] + role = [roles.ASSET_CA_INVENTORY,roles.ASSET_CA_RECEIVABLES, roles.COGS] + balance_type = ["debit","credit","debit"] + + for name,role,balance_type in zip(name,role,balance_type): + create_make_accounts(entity,coa,makes,name,role,balance_type) + +def create_make_accounts(entity,coa,makes,name,role,balance_type): for make in makes: - last_account = entity.get_all_accounts().filter(role=roles.ASSET_CA_RECEIVABLES).order_by('-created').first() + last_account = entity.get_all_accounts().filter(role=role).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 entity.get_all_accounts().filter( - name=make.name, - role=roles.ASSET_CA_RECEIVABLES, + acc = entity.get_all_accounts().filter( + name=f"{name}:{make.name}", + role=role, coa_model=coa, - balance_type="credit", + balance_type=balance_type, active=True - ).exists(): - entity.create_account( - name=make.name, + ).first() + if not acc: + acc = entity.create_account( + name=f"{name}:{make.name}", code=code, - role=roles.ASSET_CA_RECEIVABLES, + role=role, coa_model=coa, - balance_type="credit", + balance_type=balance_type, active=True ) + return acc diff --git a/inventory/utils.py b/inventory/utils.py index a7aa76e6..17eb2aa2 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -1146,44 +1146,49 @@ def handle_account_process(invoice,amount,finance_data): entity = invoice.ledger.entity coa = entity.get_default_coa() - make_account = entity.get_all_accounts().filter(name=car.id_car_make.name,role=roles.COGS).first() - if not make_account: - last_account = entity.get_all_accounts().filter(role=roles.COGS).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}" + cash_account = entity.get_all_accounts().filter(name="Cash", role=roles.ASSET_CA_CASH).first() + inventory_account = car.get_inventory_account() + revenue_account = car.get_revenue_account() + cogs_account = car.get_cogs_account() - make_account = entity.create_account( - name=car.id_car_make.name, - code=code, - role=roles.COGS, - coa_model=coa, - balance_type="debit", - active=True - ) + # make_account = entity.get_all_accounts().filter(name=car.id_car_make.name,role=roles.COGS).first() + # if not make_account: + # last_account = entity.get_all_accounts().filter(role=roles.COGS).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}" - # get or create additional services account - additional_services_account = entity.get_default_coa_accounts().filter(name="Additional Services",role=roles.COGS).first() - if not additional_services_account: - last_account = entity.get_all_accounts().filter(role=roles.COGS).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}" + # make_account = entity.create_account( + # name=car.id_car_make.name, + # code=code, + # role=roles.COGS, + # coa_model=coa, + # balance_type="debit", + # active=True + # ) - additional_services_account = entity.create_account( - name="Additional Services", - code=code, - role=roles.COGS, - coa_model=coa, - balance_type="debit", - active=True - ) + # # get or create additional services account + # additional_services_account = entity.get_default_coa_accounts().filter(name="Additional Services",role=roles.COGS).first() + # if not additional_services_account: + # last_account = entity.get_all_accounts().filter(role=roles.COGS).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}" - inventory_account = entity.get_default_coa_accounts().filter(role=roles.ASSET_CA_INVENTORY).first() + # additional_services_account = entity.create_account( + # name="Additional Services", + # code=code, + # role=roles.COGS, + # coa_model=coa, + # balance_type="debit", + # active=True + # ) - vat_payable_account = entity.get_default_coa_accounts().get(name="VAT Payable", active=True) + # 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) journal = JournalEntryModel.objects.create( @@ -1191,16 +1196,53 @@ def handle_account_process(invoice,amount,finance_data): description=f"Payment for Invoice {invoice.invoice_number}", ledger=invoice.ledger, locked=False, - origin="Payment", + origin=f"Sale of {car.name}{car.vin}: Invoice {invoice.invoice_number}", ) TransactionModel.objects.create( journal_entry=journal, - account=make_account, # Debit car make Account + account=cash_account, amount=Decimal(finance_data.get("grand_total")), tx_type="debit", - description="Payment Received", + description="", ) + + TransactionModel.objects.create( + journal_entry=journal, + account=revenue_account, + amount=Decimal(finance_data.get("grand_total")), + tx_type="credit", + description="", + ) + + journal_cogs = JournalEntryModel.objects.create( + posted=False, + description=f"COGS of {car.name}{car.vin}: Invoice {invoice.invoice_number}", + ledger=invoice.ledger, + locked=False, + origin="Payment", + ) + TransactionModel.objects.create( + journal_entry=journal_cogs, + account=cogs_account, + amount=Decimal(car.finances.cost_price), + tx_type="debit", + description="", + ) + + TransactionModel.objects.create( + journal_entry=journal_cogs, + account=inventory_account, + amount=Decimal(car.finances.cost_price), + tx_type="credit", + description="", + ) + + car.item_model.for_inventory = False + car.finances.is_sold = True + car.finances.save() + car.item_model.save() + # TransactionModel.objects.create( # journal_entry=journal, # account=additional_services_account, # Debit Additional Services @@ -1209,13 +1251,13 @@ def handle_account_process(invoice,amount,finance_data): # description="Additional Services", # ) - TransactionModel.objects.create( - journal_entry=journal, - account=inventory_account, # Credit Inventory account - amount=Decimal(finance_data.get("grand_total")), - tx_type="credit", - description="Account Adjustment", - ) + # TransactionModel.objects.create( + # journal_entry=journal, + # account=inventory_account, # Credit Inventory account + # amount=Decimal(finance_data.get("grand_total")), + # tx_type="credit", + # description="Account Adjustment", + # ) # TransactionModel.objects.create( # journal_entry=journal, diff --git a/inventory/views.py b/inventory/views.py index 0b9cd146..f49ab999 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -3587,7 +3587,7 @@ def create_estimate(request, pk=None): customer_id = data.get("customer") # terms = data.get("terms") # customer = entity.get_customers().filter(pk=customer_id).first() - customer = models.Customer.objects.filter(pk=customer_id).first() + customer = models.Customer.objects.filter(pk=int(customer_id)).first() items = data.get("item", []) quantities = data.get("quantity", []) @@ -3662,27 +3662,39 @@ def create_estimate(request, pk=None): ] items_txs = [] for item in items_list: - car_instance = ItemModel.objects.filter( - additional_info__car_info__hash=item.get("item_id") - ).all() + # car_instance = ItemModel.objects.filter( + # additional_info__car_info__hash=item.get("item_id") + # ).all() + # for i in car_instance[: int(quantities[0])]: + # items_txs.append( + # { + # "item_number": i.item_number, + # "quantity": 1, + # "unit_cost": i.additional_info.get("car_finance").get( + # "selling_price" + # ), + # "unit_revenue": i.additional_info.get("car_finance").get( + # "selling_price" + # ), + # "total_amount": ( + # i.additional_info.get("car_finance").get("total_vat") + # ), + # } + # ) + car_instance = models.Car.objects.filter(hash=item.get("item_id"),finances__is_sold=False).all() for i in car_instance[: int(quantities[0])]: items_txs.append( { - "item_number": i.item_number, + "item_number": i.item_model.item_number, "quantity": 1, - "unit_cost": i.additional_info.get("car_finance").get( - "selling_price" - ), - "unit_revenue": i.additional_info.get("car_finance").get( - "selling_price" - ), - "total_amount": ( - i.additional_info.get("car_finance").get("total_vat") - ), + "unit_cost": round(float(i.finances.selling_price)), + "unit_revenue": round(float(i.finances.selling_price)), + "total_amount": round(float(i.finances.total_vat)), } ) + estimate_itemtxs = { item.get("item_number"): { "unit_cost": item.get("unit_cost"), @@ -3704,12 +3716,20 @@ def create_estimate(request, pk=None): } } - estimate.migrate_itemtxs( - itemtxs=estimate_itemtxs, - commit=True, - operation=EstimateModel.ITEMIZE_APPEND, - ) - + try: + estimate.migrate_itemtxs( + itemtxs=estimate_itemtxs, + commit=True, + operation=EstimateModel.ITEMIZE_APPEND, + ) + except Exception as e: + estimate.delete() + return JsonResponse( + { + "status": "error", + "message": e, + } + ) if isinstance(items, list): for item in estimate_itemtxs.keys(): item_instance = ItemModel.objects.filter(item_number=item).first() @@ -4677,7 +4697,7 @@ class LeadListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): dealer = get_user_type(self.request) qs = models.Lead.objects.filter(dealer=dealer) if query: - qs = apply_search_filters(qs, query) + qs = apply_search_filters(qs, query) if self.request.is_dealer: return qs staffmember = getattr(self.request.user, "staffmember", None) diff --git a/templates/inventory/car_form.html b/templates/inventory/car_form.html index 8752b7a6..fe27d901 100644 --- a/templates/inventory/car_form.html +++ b/templates/inventory/car_form.html @@ -20,7 +20,7 @@