from django.core.exceptions import ObjectDoesNotExist import json import random import datetime from django.shortcuts import redirect from django.contrib import messages from django.utils import timezone from django_ledger.models.entity import UnitOfMeasureModel from django_ledger.models.journal_entry import JournalEntryModel from django_ledger.models.ledger import LedgerModel from django_ledger.models.transactions import TransactionModel import requests from inventory import models from django.conf import settings from django.core.mail import send_mail from django.utils.translation import gettext_lazy as _ from inventory.utilities.financials import get_financial_value from django_ledger.models.items import ItemModel from django_ledger.models import ( InvoiceModel, EstimateModel, BillModel, VendorModel, CustomerModel, ) from decimal import Decimal def get_jwt_token(): url = "https://carapi.app/api/auth/login" headers = { "accept": "text/plain", "Content-Type": "application/json", } data = { "api_token": "f5204a00-6f31-4de2-96d8-ed998e0d230c", "api_secret": "8c11320781a5b8f4f327b6937e6f8241", } try: response = requests.post(url, headers=headers, json=data) response.raise_for_status() return response.text except requests.exceptions.RequestException as e: print(f"Error obtaining JWT token: {e}") return None def localize_some_words(): success = _("success") error = _("error") forget = _("Forgot Password?") return None def get_calculations(quotation): context = {} qc_len = quotation.quotation_cars.count() cars = [x.car for x in quotation.quotation_cars.all()] finances = models.CarFinance.objects.filter(car__in=cars) services = ItemModel.objects.filter(additional_finances__in=finances).all() data = [ { "name": x.name, "price": x.default_amount, "total_price": x.default_amount * qc_len, "vated": float(x.default_amount) * 0.15 * float(qc_len), "total_price_vat": float(x.default_amount) + (float(x.default_amount) * 0.15 * float(qc_len)), } for x in services ] context["services"] = data context["total_cost"] = 0 context["total_vat"] = 0 context["total_cost_vat"] = 0 for k in context["services"]: context["total_cost"] += k["total_price"] context["total_vat"] += k["vated"] context["total_cost_vat"] = float(context["total_cost"]) + float( context["total_vat"] ) return context def send_email(from_, to_, subject, message): subject = subject message = message from_email = from_ recipient_list = [to_] send_mail(subject, message, from_email, recipient_list) def get_user_type(request): dealer = "" if hasattr(request.user, "dealer"): dealer = request.user.dealer elif hasattr(request.user, "staff"): dealer = request.user.staff.dealer return dealer def get_dealer_from_instance(instance): if instance.dealer.staff: return instance.dealer else: return instance.dealer def reserve_car(car, request): try: reserved_until = timezone.now() + timezone.timedelta(hours=24) models.CarReservation.objects.create( car=car, reserved_by=request.user, reserved_until=reserved_until ) car.status = models.CarStatusChoices.RESERVED car.save() messages.success(request, _("Car reserved successfully.")) except Exception as e: messages.error(request, f"Error reserving car: {e}") return redirect("car_detail", pk=car.pk) def calculate_vat_amount(amount): vat = models.VatRate.objects.filter(is_active=True).first() if vat: return ((amount * Decimal(vat.rate)).quantize(Decimal("0.01")), vat.rate) return amount def get_car_finance_data(model): vat = models.VatRate.objects.filter(is_active=True).first() data = model.get_itemtxs_data()[0].all() total = sum( [ Decimal(item.item_model.additional_info["car_finance"]["selling_price"]) * Decimal(item.ce_quantity or item.quantity) for item in data ] ) additional_services = [] for i in data: if i.item_model.additional_info["additional_services"]: additional_services.extend( [ {"name": x.name, "price": x.price} for x in i.item_model.additional_info["additional_services"] ] ) return { "cars": [ { "vin": x.item_model.additional_info["car_info"]["vin"], "make": x.item_model.additional_info["car_info"]["make"], "model": x.item_model.additional_info["car_info"]["model"], "year": x.item_model.additional_info["car_info"]["year"], "trim": x.item_model.additional_info["car_info"]["mileage"], "cost_price": x.item_model.additional_info["car_finance"]["cost_price"], "selling_price": x.item_model.additional_info["car_finance"][ "selling_price" ], "discount": x.item_model.additional_info["car_finance"][ "discount_amount" ], "quantity": x.ce_quantity or x.quantity, "unit_price": Decimal(x.item_model.additional_info["car_finance"]["total"]), "total": Decimal(x.item_model.additional_info["car_finance"]["total"]) * Decimal(x.quantity or x.ce_quantity), "total_vat": x.item_model.additional_info["car_finance"]["total_vat"], "additional_services": x.item_model.additional_info[ "additional_services" ], } for x in data ], "quantity": sum((x.quantity or x.ce_quantity) for x in data), "total_price": total, "total_vat": (total * vat.rate) + total, "total_discount": sum( Decimal(x.item_model.additional_info["car_finance"]["discount_amount"]) for x in data ), "grand_total": Decimal(total * vat.rate) + total - Decimal( sum( Decimal(x.item_model.additional_info["car_finance"]["discount_amount"]) for x in data ) ), "additionals": additional_services, "vat": vat.rate, } def get_financial_values(model): vat = models.VatRate.objects.filter(is_active=True).first() if not model.get_itemtxs_data()[0].exists(): return { "vat_amount": 0, "total": 0, "grand_total": 0, "discount_amount": 0, "vat": 0, "car_and_item_info": [], "additional_services": [], } data = model.get_itemtxs_data()[0].all() for item in data: if not item.item_model.additional_info.get("car_finance"): return { "vat_amount": 0, "total": 0, "grand_total": 0, "discount_amount": 0, "vat": 0, "car_and_item_info": [], "additional_services": [], } if isinstance(model, InvoiceModel): if model.ce_model: data = model.ce_model.get_itemtxs_data()[0].all() else: data = model.get_itemtxs_data()[0].all() total = sum( [ Decimal(item.item_model.additional_info["car_finance"]["selling_price"]) * Decimal(item.ce_quantity or item.quantity) for item in data ] ) discount_amount = sum( Decimal(i.item_model.additional_info["car_finance"]["discount_amount"]) for i in data ) additional_services = [] for i in data: if i.item_model.additional_info["additional_services"]: additional_services.extend( [ {"name": x.name, "price": x.price} for x in i.item_model.additional_info["additional_services"] ] ) grand_total = Decimal(total) - Decimal(discount_amount) vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2) car_and_item_info = [ { "info": x.item_model.additional_info["car_info"], "finances": x.item_model.additional_info["car_finance"], "quantity": x.ce_quantity or x.quantity, "total": Decimal( x.item_model.additional_info["car_finance"]["selling_price"] ) * Decimal(x.ce_quantity or x.quantity), } for x in data ] return { "total": total, "discount_amount": discount_amount, "car_and_item_info": car_and_item_info, "additional_services": additional_services, "grand_total": grand_total + vat_amount, "vat_amount": vat_amount, "vat": vat.rate, } def set_invoice_payment(dealer, entity, invoice, amount, payment_method): vat_amount = 0 total_amount = 0 if invoice.terms == "on_receipt": for x in invoice.get_itemtxs_data()[0].all(): # vat_amount += models.Car.objects.get( # vin=x.item_model.name # ).finances.vat_amount * Decimal(x.quantity) total_amount += Decimal(x.unit_cost) * Decimal(x.quantity) # grand_total = total_amount - Decimal(vat_amount) total_amount ledger = LedgerModel.objects.filter( name__icontains=str(invoice.pk), entity=entity ).first() journal = JournalEntryModel.objects.create( posted=False, description=f"Payment for Invoice {invoice.invoice_number}", ledger=ledger, locked=False, origin="Payment", ) credit_account = entity.get_default_coa_accounts().get(name="Sales Revenue") debit_account = None if payment_method == "cash": debit_account = entity.get_default_coa_accounts().get(name="Cash", active=True) elif payment_method == "credit": debit_account = entity.get_default_coa_accounts().get( name="Accounts Receivable", active=True ) else: debit_account = entity.get_default_coa_accounts().get( name="Cash in Bank", 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 Cash amount=amount, # Payment amount tx_type="debit", description="Payment Received", ) # if total_amount + invoice. TransactionModel.objects.create( journal_entry=journal, account=credit_account, # Credit Accounts Receivable amount=total_amount, # Payment amount tx_type="credit", description="Payment Received", ) if vat_amount > 0: TransactionModel.objects.create( journal_entry=journal, account=vat_payable_account, # Credit VAT Payable amount=vat_amount, tx_type="credit", description="VAT Payable on Invoice", ) invoice.make_payment(amount) invoice.save() def set_bill_payment(dealer, entity, bill, amount, payment_method): total_amount = 0 for x in bill.get_itemtxs_data()[0].all(): total_amount += Decimal(x.unit_cost) * Decimal(x.quantity) journal = JournalEntryModel.objects.create( posted=False, description=f"Payment for bill {bill.bill_number}", ledger=bill.ledger, locked=False, origin="Payment", ) cash_account = entity.get_default_coa_accounts().get(name="Cash", active=True) account_payable = entity.get_default_coa_accounts().get( name="Accounts Payable", active=True ) TransactionModel.objects.create( journal_entry=journal, account=cash_account, # Debit Cash amount=amount, # Payment amount tx_type="debit", description="Payment Received", ) TransactionModel.objects.create( journal_entry=journal, account=account_payable, # Credit Accounts Receivable amount=amount, # Payment amount tx_type="credit", description="Payment Received", ) bill.make_payment(amount) bill.save() def transfer_to_dealer(request, cars, to_dealer, remarks=None): dealer = get_user_type(request) if not cars: raise ValueError("No cars selected for transfer.") from_dealer = cars[0].dealer # Assume all cars are from the same dealer # Validate that all cars are from the same dealer for car in cars: if car.dealer != from_dealer: raise ValueError("All cars must be from the same dealer.") if from_dealer == to_dealer: raise ValueError("Cannot transfer cars to the same dealer.") # Log the transfer transfer_log = models.CarTransferLog.objects.create( from_dealer=from_dealer, to_dealer=to_dealer, remarks=remarks, ) transfer_log.cars.set(cars) # Associate the cars with the transfer log # Update the dealer for all cars for car in cars: car.dealer = to_dealer car.save() def transfer_car(car, transfer): from_dealer = transfer.from_dealer to_dealer = transfer.to_dealer # add transfer.to_dealer as customer in transfer.from_dealer entity customer = ( from_dealer.entity.get_customers().filter(email=to_dealer.user.email).first() ) if not customer: customer = from_dealer.entity.create_customer( customer_model_kwargs={ "customer_name": to_dealer.name, "email": to_dealer.user.email, "address_1": to_dealer.address, } ) customer.additional_info.update({"type": "organization"}) customer.save() invoice = from_dealer.entity.create_invoice( customer_model=customer, terms=InvoiceModel.TERMS_NET_30, cash_account=from_dealer.entity.get_default_coa_accounts().get( name="Cash", active=True ), prepaid_account=from_dealer.entity.get_default_coa_accounts().get( name="Accounts Receivable", active=True ), coa_model=from_dealer.entity.get_default_coa(), ) ledger = from_dealer.entity.create_ledger(name=str(invoice.pk)) invoice.ledgar = ledger ledger.invoicemodel = invoice ledger.save() invoice.save() item = from_dealer.entity.get_items_products().filter(name=car.vin).first() if not item: return invoice_itemtxs = { item.item_number: { "unit_cost": transfer.total_price, "quantity": transfer.quantity, "total_amount": transfer.total_price, } } invoice_itemtxs = invoice.migrate_itemtxs( itemtxs=invoice_itemtxs, commit=True, operation=InvoiceModel.ITEMIZE_APPEND, ) invoice.save() invoice.mark_as_review() invoice.mark_as_approved(from_dealer.entity.slug, from_dealer.entity.admin) # invoice.mark_as_paid(from_dealer.entity.slug, from_dealer.entity.admin) invoice.save() # create car item product in to_dealer entity uom = to_dealer.entity.get_uom_all().filter(name=item.uom.name).first() # create item product in the reciever ledger product = to_dealer.entity.create_item_product( name=item.name, uom_model=uom, item_type=item.item_type, coa_model=to_dealer.entity.get_default_coa(), ) product.additional_info.update({"car_info": car.to_dict()}) product.save() # add the sender as vendor and create a bill for it vendor = None vendor = to_dealer.entity.get_vendors().filter(vendor_name=from_dealer.name).first() if not vendor: vendor = VendorModel.objects.create( entity_model=to_dealer.entity, vendor_name=from_dealer.name, additional_info={"info": to_dict(from_dealer)}, ) # transfer the car to to_dealer and create items record bill = to_dealer.entity.create_bill( vendor_model=vendor, terms=BillModel.TERMS_NET_30, cash_account=to_dealer.entity.get_default_coa_accounts().get( name="Cash", active=True ), prepaid_account=to_dealer.entity.get_default_coa_accounts().get( name="Prepaid Expenses", active=True ), coa_model=to_dealer.entity.get_default_coa(), ) bill.additional_info = {} bill_itemtxs = { product.item_number: { "unit_cost": transfer.total_price, "quantity": transfer.quantity, "total_amount": transfer.total_price, } } bill_itemtxs = bill.migrate_itemtxs( itemtxs=bill_itemtxs, commit=True, operation=BillModel.ITEMIZE_APPEND ) bill.additional_info.update({"car_info": car.to_dict()}) bill.additional_info.update({"car_finance": car.finances.to_dict()}) bill.mark_as_review() bill.mark_as_approved(to_dealer.entity.slug, to_dealer.entity.admin) bill.save() car.dealer = to_dealer car.vendor = vendor car.receiving_date = datetime.datetime.now() car.finances.additional_services.clear() if hasattr(car, "custom_cards"): car.custom_cards.delete() car.finances.cost_price = transfer.total_price car.finances.selling_price = 0 car.finances.discount_amount = 0 car.finances.save() car.location.owner = to_dealer car.location.showroom = to_dealer car.location.description = "" car.location.save() car.status = models.CarStatusChoices.AVAILABLE transfer.status = models.CarTransferStatusChoices.success transfer.active = False transfer.save() car.save() return True # pay the pill # set_bill_payment(to_dealer,to_dealer.entity,bill,transfer.total_price,"credit") def to_dict(obj): obj_dict = vars(obj).copy() if "_state" in vars(obj): del obj_dict["_state"] for key, value in obj_dict.items(): if isinstance(value, datetime.datetime): obj_dict[key] = value.strftime("%Y-%m-%d %H:%M:%S") elif hasattr(value, "pk") or hasattr(value, "id"): try: obj_dict[key] = value.name except AttributeError: obj_dict[key] = str(value) else: obj_dict[key] = str(value) return obj_dict class CarFinanceCalculator: VAT_OBJ_NAME = 'vat_rate' CAR_FINANCE_KEY = 'car_finance' CAR_INFO_KEY = 'car_info' ADDITIONAL_SERVICES_KEY = 'additional_services' def __init__(self, model): self.model = model self.vat_rate = self._get_vat_rate() self.item_transactions = self._get_item_transactions() self.additional_services = self._get_additional_services() def _get_vat_rate(self): vat = models.VatRate.objects.filter(is_active=True).first() if not vat: raise ObjectDoesNotExist("No active VAT rate found") return vat.rate def _get_item_transactions(self): return self.model.get_itemtxs_data()[0].all() @staticmethod def _get_quantity(item): return item.ce_quantity or item.quantity def _get_nested_value(self, item, *keys): current = item.item_model.additional_info for key in keys: current = current.get(key, {}) return current def _get_car_data(self, item): quantity = self._get_quantity(item) car_finance = self._get_nested_value(item, self.CAR_FINANCE_KEY) car_info = self._get_nested_value(item, self.CAR_INFO_KEY) unit_price = Decimal(car_finance.get('selling_price', 0)) return { "item_number": item.item_model.item_number, "vin": car_info.get('vin'), "make": car_info.get('make'), "model": car_info.get('model'), "year": car_info.get('year'), "trim": car_info.get('mileage'), # Verify if this should actually be 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, "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.name, "price": service.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')) * Decimal(self._get_quantity(item)) for item in self.item_transactions ) total_vat_amount = total_price * self.vat_rate total_discount = sum( Decimal(self._get_nested_value(item, self.CAR_FINANCE_KEY, 'discount_amount')) for item in self.item_transactions ) return { "total_price": total_price, "total_vat_amount": total_vat_amount, "total_discount": total_discount, "grand_total": (total_price + total_vat_amount) - total_discount, } def get_finance_data(self): totals = self.calculate_totals() return { "cars": [self._get_car_data(item) for item in self.item_transactions], "quantity": sum(self._get_quantity(item) for item in self.item_transactions), "total_price": totals['total_price'], "total_vat": totals['total_vat_amount'] + totals['total_price'], "total_discount": totals['total_discount'], "grand_total": totals['grand_total'], "additionals": self.additional_services, "vat": self.vat_rate, }