diff --git a/inventory/models.py b/inventory/models.py index 45d873bd..860bb2f9 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -592,6 +592,10 @@ class CarFinance(models.Model): def total(self): return self.selling_price + @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()) diff --git a/inventory/signals.py b/inventory/signals.py index ac99e298..075e4181 100644 --- a/inventory/signals.py +++ b/inventory/signals.py @@ -19,7 +19,9 @@ from django_ledger.models import ( CustomerModel, JournalEntryModel, TransactionModel, - LedgerModel + LedgerModel, + BillModel, + ItemTransactionModel ) from . import models from django.utils.timezone import now @@ -642,20 +644,18 @@ def create_dealer_groups(sender, instance, created, **kwargs): def create_ledger_vendor(sender, instance, created, **kwargs): if created: entity = EntityModel.objects.filter(name=instance.dealer.name).first() - + additionals = to_dict(instance) entity.create_vendor( vendor_model_kwargs={ "vendor_name": instance.name, "vendor_number": instance.crn, "address_1": instance.address, "phone": instance.phone_number, + "email": instance.email, "tax_id_number": instance.vrn, "active": True, "hidden": False, - "additional_info": { - "arabic_name": instance.arabic_name, - "contact_person": instance.contact_person, - }, + "additional_info": additionals, } ) @@ -676,25 +676,37 @@ def create_ledger_vendor(sender, instance, created, **kwargs): active=True ) 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) def create_customer_user(sender, instance, created, **kwargs): if created: - first_name = instance.additional_info.get("customer_info").get("first_name") - last_name = instance.additional_info.get("customer_info").get("last_name") - user = User.objects.create( - username=instance.email, - email=instance.email, - first_name=first_name if first_name else '', - last_name=last_name if last_name else '', - ) - instance.additional_info.update({"user_info": to_dict(user)}) - user.set_unusable_password() - user.save() - instance.user = user - instance.save() - + try: + first_name = instance.additional_info.get("customer_info").get("first_name") + last_name = instance.additional_info.get("customer_info").get("last_name") + user = User.objects.create( + username=instance.email, + email=instance.email, + first_name=first_name if first_name else '', + last_name=last_name if last_name else '', + ) + instance.additional_info.update({"user_info": to_dict(user)}) + user.set_unusable_password() + user.save() + instance.user = user + instance.save() + except Exception as e: + print(e) # Create Item @receiver(post_save, sender=models.Car) @@ -929,98 +941,106 @@ def create_dealer_settings(sender, instance, created, **kwargs): @receiver(post_save, sender=models.Dealer) def create_make_ledger_accounts(sender, instance, created, **kwargs): if created: - entity_name = instance.user.dealer.name - entity = EntityModel.objects.get(name=entity_name) + entity = instance.entity + coa = entity.get_default_coa() + 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) # def create_vendor_accounts(sender, instance, created, **kwargs): # 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() -# code = str(int(last_account.code) + 1) -# account = entity.create_account( +# 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=entity.get_default_coa(), +# coa_model=coa, # balance_type="credit", # 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) def update_finance_cost(sender, instance, created, **kwargs): - entity = instance.car.dealer.entity - ledger,created = LedgerModel.objects.get_or_create(name=instance.car.vin, entity=entity) - vendor = instance.car.vendor - if created: - 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() + entity = instance.car.dealer.entity + 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) - 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) - - # Debit Inventory Account - TransactionModel.objects.create( - journal_entry=journal, - account=inventory_account, - amount=instance.total + instance.total_additionals, - tx_type='debit' - ) - - # 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() \ No newline at end of file + # 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) \ No newline at end of file diff --git a/inventory/urls.py b/inventory/urls.py index db4d86fd..b29a9dca 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -134,7 +134,7 @@ urlpatterns = [ views.lead_transfer, name="lead_transfer", ), - + path( "crm/opportunities//add_note/", views.add_note_to_opportunity, @@ -346,7 +346,7 @@ path( # views.payment_create, # name="payment_create", # ), - + # Users URLs path("user/create/", views.UserCreateView.as_view(), name="user_create"), path("user//update/", views.UserUpdateView.as_view(), name="user_update"), @@ -415,7 +415,7 @@ path( # Ledger path( "ledgers/", views.LedgerModelListView.as_view(), name="ledger_list" - ), + ), path( "ledgers//detail//", views.LedgerModelDetailView.as_view(), name="ledger_detail" ), @@ -436,10 +436,10 @@ path( # ), path( "journalentries//list/", views.JournalEntryListView.as_view(), name="journalentry_list" - ), + ), path( "journalentries//create/", views.JournalEntryCreateView.as_view(), name="journalentry_create" - ), + ), path( "journalentries//delete/", views.JournalEntryDeleteView, name="journalentry_delete" ), @@ -640,11 +640,11 @@ path( views.bill_mark_as_paid, name="bill_mark_as_paid", ), - - + + # orders path("orders/", views.OrderListView.as_view(), name="order_list_view"), - + # BALANCE SHEET Reports... # Entities... path('entity//balance-sheet/', diff --git a/inventory/utils.py b/inventory/utils.py index 6609239b..1e42766f 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -97,7 +97,7 @@ def send_email(from_, to_, subject, message): send_mail(subject, message, from_email, recipient_list) -def get_user_type(request): +def get_user_type(request): if request.is_dealer: return request.user.dealer elif request.is_staff: @@ -286,7 +286,7 @@ def get_financial_values(model): def set_invoice_payment(dealer, entity, invoice, amount, payment_method): calculator = CarFinanceCalculator(invoice) - finance_data = calculator.get_finance_data() + finance_data = calculator.get_finance_data() # journal = JournalEntryModel.objects.create( # posted=False, @@ -295,11 +295,11 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method): # locked=False, # origin="Payment", # ) - + # credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue") # 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) - + # TransactionModel.objects.create( # journal_entry=journal, # account=debit_account, # Debit Account @@ -316,7 +316,7 @@ def set_invoice_payment(dealer, entity, invoice, amount, payment_method): # description="Payment Received", # ) - + # TransactionModel.objects.create( # journal_entry=journal, # account=vat_payable_account, # Credit VAT Payable @@ -457,7 +457,7 @@ class CarTransfer: 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() if not self.item: return @@ -475,7 +475,7 @@ class CarTransfer: commit=True, operation=InvoiceModel.ITEMIZE_APPEND, ) - + if self.invoice.can_review(): self.invoice.mark_as_review() 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'), "model": car_info.get('model'), "year": car_info.get('year'), - "trim": car_info.get('trim'), + "trim": car_info.get('trim'), "mileage": car_info.get('mileage'), "cost_price": car_finance.get('cost_price'), "selling_price": car_finance.get('selling_price'), "discount": car_finance.get('discount_amount'), "quantity": quantity, - "unit_price": unit_price, + "unit_price": unit_price, "total": unit_price * Decimal(quantity), "total_vat": car_finance.get('total_vat'), "additional_services": self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY), } - + def _get_additional_services(self): return [ {"name": service.get('name'), "price": service.get('price'), "taxable": service.get('taxable'),"price_": service.get('price_')} for item in self.item_transactions for service in self._get_nested_value(item, self.ADDITIONAL_SERVICES_KEY) or [] - ] - + ] + def calculate_totals(self): total_price = sum( Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'selling_price')) * @@ -786,14 +786,14 @@ class CarFinanceCalculator: for item in self.item_transactions ) total_additionals = sum(Decimal(x.get('price_')) for x in self._get_additional_services()) - + total_discount = sum( Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'discount_amount')) for item in self.item_transactions ) total_price_discounted = total_price - total_discount total_vat_amount = total_price_discounted * self.vat_rate - + return { "total_price": round(total_price_discounted, 2), # total_price_discounted, "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) 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() @@ -864,8 +864,8 @@ def handle_account_process(invoice,amount,finance_data): 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( + + make_account = entity.create_account( name=car.id_car_make.name, code=code, role=roles.COGS, @@ -873,7 +873,7 @@ def handle_account_process(invoice,amount,finance_data): 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: @@ -882,8 +882,8 @@ def handle_account_process(invoice,amount,finance_data): code = f"{int(last_account.code)}{1:03d}" elif len(last_account.code) > 4: code = f"{int(last_account.code)+1}" - - additional_services_account = entity.create_account( + + additional_services_account = entity.create_account( name="Additional Services", code=code, role=roles.COGS, @@ -891,12 +891,12 @@ def handle_account_process(invoice,amount,finance_data): balance_type="debit", 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( posted=False, description=f"Payment for Invoice {invoice.invoice_number}", @@ -904,7 +904,7 @@ def handle_account_process(invoice,amount,finance_data): locked=False, origin="Payment", ) - + TransactionModel.objects.create( journal_entry=journal, account=make_account, # Debit car make Account @@ -926,12 +926,38 @@ def handle_account_process(invoice,amount,finance_data): amount=Decimal(car.finances.total), tx_type="credit", description="Account Adjustment", - ) - + ) + TransactionModel.objects.create( journal_entry=journal, account=vat_payable_account, # Credit VAT Payable amount=finance_data.get("total_vat_amount"), tx_type="credit", 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 ) \ No newline at end of file diff --git a/inventory/views.py b/inventory/views.py index b200852a..af410b8d 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -127,6 +127,7 @@ from .services import ( ) from .utils import ( CarFinanceCalculator, + create_make_accounts, get_car_finance_data, get_financial_values, get_item_transactions, @@ -136,6 +137,7 @@ from .utils import ( set_bill_payment, set_invoice_payment, CarTransfer, + to_dict, ) ##################################################################### @@ -1504,13 +1506,31 @@ class VendorUpdateView( SuccessMessageMixin, UpdateView, ): - model = models.Vendor + model = VendorModel form_class = forms.VendorForm template_name = "vendors/vendor_form.html" success_url = reverse_lazy("vendor_list") 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 def delete_vendor(request, pk): vendor = get_object_or_404(models.Vendor, pk=pk) @@ -4052,6 +4072,7 @@ def assign_car_makes(request): form = forms.DealersMakeForm(request.POST, dealer=dealer) if form.is_valid(): form.save() + create_make_accounts(dealer) return redirect("dealer_detail", pk=dealer.pk) else: # Pre-fill the form with existing selections diff --git a/templates/vendors/vendors_list.html b/templates/vendors/vendors_list.html index d39f408f..9919173f 100644 --- a/templates/vendors/vendors_list.html +++ b/templates/vendors/vendors_list.html @@ -118,7 +118,7 @@

{{ vendor.vendor_name }}

{{ vendor.id}}
- + {{ vendor.email }} @@ -130,11 +130,12 @@
-