from calendar import month_name from random import randint from rich import print from decimal import Decimal from django.core.paginator import Paginator from django.forms import DateField, DateInput, HiddenInput, TextInput from django_ledger.forms.bill import ( ApprovedBillModelUpdateForm, InReviewBillModelUpdateForm, ) from django.utils.decorators import method_decorator from django.views.decorators.csrf import csrf_exempt from django_ledger.models import ( EntityModel, InvoiceModel, BankAccountModel, AccountModel, JournalEntryModel, TransactionModel, EstimateModel, CustomerModel, LedgerModel, ItemModel, BillModel, VendorModel, ) from django_ledger.forms.bank_account import ( BankAccountCreateForm, BankAccountUpdateForm, ) from django_ledger.forms.customer import CustomerModelForm from django_ledger.forms.bill import BillModelCreateForm from django_ledger.forms.invoice import ( DraftInvoiceModelUpdateForm, ApprovedInvoiceModelUpdateForm, PaidInvoiceModelUpdateForm, ) from django_ledger.forms.account import AccountModelCreateForm, AccountModelUpdateForm # from django_ledger.forms.estimate import EstimateModelCreateForm from django_ledger.forms.invoice import InvoiceModelCreateForm from django_ledger.forms.item import ( ServiceCreateForm, ExpenseItemCreateForm, ExpenseItemUpdateForm, ) from django_ledger.forms.journal_entry import JournalEntryModelCreateForm from django_ledger.io import roles from django.contrib.admin.models import LogEntry import logging import json import datetime from django.db.models.functions import Coalesce from django.shortcuts import HttpResponse from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.decorators import login_required from django.http import JsonResponse from django.shortcuts import render, get_object_or_404, redirect from django.utils.translation import gettext_lazy as _ from django.db.models import Q from django.views.generic import ( View, ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView, ) from django.utils import timezone, translation from django.conf import settings from urllib.parse import urlparse, urlunparse from django.urls import reverse, reverse_lazy from django.contrib import messages from django.db.models import Sum, F, Count from django.db import transaction from .forms import VendorForm from .services import ( decodevin, get_make, get_model, ) from . import models, forms from django.contrib.auth.mixins import PermissionRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.contrib.auth.models import Group from .utils import ( CarFinanceCalculator, calculate_vat_amount, get_calculations, get_car_finance_data, get_financial_values, reserve_car, send_email, get_user_type, set_bill_payment, set_invoice_payment, to_dict, CarTransfer, ) from django.contrib.auth.models import User from allauth.account import views as allauth_views from django.db.models import Count, F, Value from django.contrib.auth import authenticate import cv2 import numpy as np from pyzbar.pyzbar import decode from django.core.files.storage import default_storage from plans.models import Plan,PlanPricing from django_ledger.utils import accruable_net_summary logger = logging.getLogger(__name__) logging.basicConfig(level=logging.INFO) def switch_language(request): language = request.GET.get("language", "en") referer = request.META.get("HTTP_REFERER", "/") parsed_url = urlparse(referer) path_parts = parsed_url.path.split("/") if path_parts[1] in dict(settings.LANGUAGES): path_parts.pop(1) new_path = "/".join(path_parts) new_url = urlunparse( ( parsed_url.scheme, parsed_url.netloc, new_path, parsed_url.params, parsed_url.query, parsed_url.fragment, ) ) if language in dict(settings.LANGUAGES): logger.debug(f"Switching language to: {language}") response = redirect(new_url) response.set_cookie(settings.LANGUAGE_COOKIE_NAME, language) translation.activate(language) request.session[settings.LANGUAGE_COOKIE_NAME] = language logger.debug( f"Language switched to: {language}, Session: {request.session[settings.LANGUAGE_COOKIE_NAME]}" ) return response else: logger.warning(f"Invalid language code: {language}") return redirect("/") def dealer_signup(request, *args, **kwargs): if request.method == "POST": data = json.loads(request.body) wf1 = data.get("wizardValidationForm1") wf2 = data.get("wizardValidationForm2") wf3 = data.get("wizardValidationForm3") # username = wf1.get("username") email = wf1.get("email") password = wf1.get("password") password_confirm = wf1.get("confirm_password") name = wf2.get("name") arabic_name = wf2.get("arabic_name") phone = wf2.get("phone_number") crn = wf3.get("crn") vrn = wf3.get("vrn") address = wf3.get("address") if password != password_confirm: return JsonResponse({"error": "Passwords do not match."}, status=400) try: with transaction.atomic(): # user = User.objects.create(username=username, email=email) user = User.objects.create(username=email, email=email) user.set_password(password) user.save() models.Dealer.objects.create( user=user, name=name, arabic_name=arabic_name, crn=crn, vrn=vrn, phone_number=phone, address=address, ) # user = authenticate(request, email=email, password=password) # if user is not None: # return JsonResponse( # {"message": "User created successfully."}, status=200 # ) # else: # return JsonResponse({"error": "User creation failed."}, status=400) # return redirect("account_login") except Exception as e: return JsonResponse({"error": str(e)}, status=400) form1 = forms.WizardForm1() form2 = forms.WizardForm2() form3 = forms.WizardForm3() return render( request, "account/signup-wizard.html", {"form1": form1, "form2": form2, "form3": form3}, ) # class Login(allauth_views.LoginView): # template_name = "account/login.html" # redirect_authenticated_user = True class OTPView(View, LoginRequiredMixin): template_name = "account/otp_verification.html" def get(self, request, *args, **kwargs): # device = default_device(request.user) # device.generate_challenge() return render(request, self.template_name) def post(self, request, *args, **kwargs): otp_code = request.POST.get("otp_code") if self.verify_otp(otp_code, request.user): messages.success(request, _("OTP verified successfully!")) return redirect("home") messages.error(request, _("Invalid OTP. Please try again.")) return render(request, self.template_name) # def verify_otp(self, otp_code, user): # device = default_device(user) # if device and device.verify_token(otp_code): # return True # return False class HomeView(TemplateView): template_name = "index.html" def dispatch(self, request, *args, **kwargs): if not request.user.is_authenticated: return redirect("welcome") return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) dealer = get_user_type(self.request) total_cars = models.Car.objects.filter(dealer=dealer).count() total_reservations = models.CarReservation.objects.filter( reserved_until__gte=timezone.now() ).count() cars_in_house = models.CarLocation.objects.filter( owner=dealer, ).count() cars_outside = total_cars - cars_in_house stats = models.CarFinance.objects.aggregate( total_cost_price=Sum("cost_price"), total_selling_price=Sum("selling_price"), ) total_cost_price = stats["total_cost_price"] or 0 total_selling_price = stats["total_selling_price"] or 0 total_profit = total_selling_price - total_cost_price context["dealer"] = dealer context["total_cars"] = total_cars context["cars_in_house"] = cars_in_house context["cars_outside"] = cars_outside context["total_reservations"] = total_reservations context["total_cost_price"] = total_cost_price context["total_selling_price"] = total_selling_price context["total_profit"] = total_profit return context class TestView(TemplateView): template_name = "test.html" class AccountingDashboard(LoginRequiredMixin, TemplateView): template_name = "dashboards/accounting.html" def dispatch(self, request, *args, **kwargs): if ( # not any(hasattr(request.user, attr) for attr in ["dealer", "subdealer"]) not request.user.is_authenticated ): # messages.error(request, _("You are not associated with any dealer.")) return redirect("welcome") return super().dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) dealer = get_user_type(self.request) total_cars = models.Car.objects.filter(dealer=dealer).count() total_reservations = models.CarReservation.objects.filter( reserved_until__gte=timezone.now() ).count() stats = models.CarFinance.objects.aggregate( total_cost_price=Sum("cost_price"), total_selling_price=Sum("selling_price"), ) total_cost_price = stats["total_cost_price"] or 0 total_selling_price = stats["total_selling_price"] or 0 total_profit = total_selling_price - total_cost_price context["dealer"] = dealer context["total_cars"] = total_cars context["total_reservations"] = total_reservations context["total_cost_price"] = total_cost_price context["total_selling_price"] = total_selling_price context["total_profit"] = total_profit return context class WelcomeView(TemplateView): template_name = "login_test.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) dealer = get_user_type(self.request) plan_list = Plan.objects.all() # pricing = PlanPricing.objects.filter(plan=plan). context["plan_list"] = plan_list return context class CarCreateView(LoginRequiredMixin, CreateView): model = models.Car form_class = forms.CarForm template_name = "inventory/car_form.html" # success_url = reverse_lazy('inventory_stats') def get_form(self, form_class=None): form = super().get_form(form_class) dealer = get_user_type(self.request) form.fields["vendor"].queryset = dealer.entity.get_vendors().filter(active=True) return form def get_success_url(self): """Determine the redirect URL based on user choice.""" if self.request.POST.get("add_another"): return reverse("car_add") return reverse("inventory_stats") def form_valid(self, form): dealer = get_user_type(self.request) form.instance.dealer = dealer form.save() messages.success(self.request, "Car saved successfully.") return super().form_valid(form) class AjaxHandlerView(LoginRequiredMixin, View): def get(self, request, *args, **kwargs): action = request.GET.get("action") handlers = { "decode_vin": self.decode_vin, "get_models": self.get_models, "get_series": self.get_series, "get_trims": self.get_trims, "get_specifications": self.get_specifications, "get_equipments": self.get_equipments, "get_options": self.get_options, } handler = handlers.get(action) if handler: return handler(request) else: return JsonResponse({"error": "Invalid action"}, status=400) def decode_vin(self, request): vin_no = request.GET.get("vin_no") car_existed = models.Car.objects.filter(vin=vin_no).exists() if car_existed: return JsonResponse({"error": _("VIN number exists")}, status=400) if not vin_no or len(vin_no.strip()) != 17: return JsonResponse( {"success": False, "error": "Invalid VIN number provided."}, status=400 ) vin_no = vin_no.strip() vin_data = {} decoding_method = "" # manufacturer_name = model_name = year_model = None if not (result := decodevin(vin_no)): return JsonResponse( {"success": False, "error": "VIN not found in all sources."}, status=404 ) manufacturer_name, model_name, year_model = result.values() make = get_make(manufacturer_name) model = get_model(model_name, make) logger.info( f"VIN decoded using {decoding_method}: Make={manufacturer_name}, Model={model_name}, Year={year_model}" ) car_model = model car_make = make if not car_make: return JsonResponse( {"success": False, "error": "Manufacturer not found in the database."}, status=404, ) vin_data["make_id"] = car_make.id_car_make vin_data["name"] = car_make.name vin_data["arabic_name"] = car_make.arabic_name if not car_model: vin_data["model_id"] = "" else: vin_data["model_id"] = car_model.id_car_model vin_data["year"] = year_model return JsonResponse({"success": True, "data": vin_data}) def get_models(self, request): make_id = request.GET.get("make_id") car_models = ( models.CarModel.objects.filter(id_car_make=make_id) .values("id_car_model", "name", "arabic_name") .order_by("name") ) return JsonResponse(list(car_models), safe=False) def get_series(self, request): model_id = request.GET.get("model_id") year = request.GET.get("year") # Validate inputs if not model_id or not year: return JsonResponse( {"error": "Missing required parameters: model_id or year"}, status=400 ) try: year = int(year) except ValueError: return JsonResponse({"error": "Invalid year format"}, status=400) series = models.CarSerie.objects.filter( id_car_model=model_id, year_begin__lte=year, year_end__gte=year ).values("id_car_serie", "name", "arabic_name", "generation_name") return JsonResponse(list(series), safe=False) def get_trims(self, request): serie_id = request.GET.get("serie_id") # model_id = request.GET.get('model_id') trims = models.CarTrim.objects.filter(id_car_serie=serie_id).values( "id_car_trim", "name", "arabic_name" ) return JsonResponse(list(trims), safe=False) def get_specifications(self, request): trim_id = request.GET.get("trim_id") car_spec_values = models.CarSpecificationValue.objects.filter( id_car_trim=trim_id ) lang = translation.get_language() specs_by_parent = {} for value in car_spec_values: specification = value.id_car_specification parent = specification.id_parent parent_id = parent.id_car_specification if parent else 0 if lang == "ar": parent_name = parent.arabic_name if parent else "Root" else: parent_name = parent.name if parent else "Root" if parent_id not in specs_by_parent: specs_by_parent[parent_id] = { "parent_name": parent_name, "specifications": [], } spec_data = { "specification_id": specification.id_car_specification, "s_name": specification.arabic_name if lang == "ar" else specification.name, "s_value": value.value, "s_unit": value.unit if value.unit else "", "trim_name": value.id_car_trim.name, } specs_by_parent[parent_id]["specifications"].append(spec_data) serialized_specs = [ {"parent_name": v["parent_name"], "specifications": v["specifications"]} for v in specs_by_parent.values() ] return JsonResponse(serialized_specs, safe=False) def get_equipments(self, request): trim_id = request.GET.get("trim_id") equipments = ( models.CarEquipment.objects.filter(id_car_trim=trim_id) .values("id_car_equipment", "name") .order_by("name") ) return JsonResponse(list(equipments), safe=False) def get_options(self, request): equipment_id = request.GET.get("equipment_id") car_option_values = models.CarOptionValue.objects.filter( id_car_equipment=equipment_id ) options_by_parent = {} for value in car_option_values: option = value.id_car_option parent = option.id_parent parent_id = parent.id_car_option if parent else 0 parent_name = parent.name if parent else "Root" if parent_id not in options_by_parent: options_by_parent[parent_id] = { "parent_name": parent_name, "options": [], } option_data = { "option_id": option.id_car_option, "option_name": option.name, "is_base": value.is_base, "equipment_name": value.id_car_equipment.name, } options_by_parent[parent_id]["options"].append(option_data) serialized_options = [ {"parent_name": v["parent_name"], "options": v["options"]} for v in options_by_parent.values() ] return JsonResponse(serialized_options, safe=False) @method_decorator(csrf_exempt, name="dispatch") class SearchCodeView(View): template_name = "inventory/scan_vin.html" def get(self, request, *args, **kwargs): """Render the form page.""" return render(request, self.template_name) def post(self, request, *args, **kwargs): image_file = request.FILES.get("image") if image_file: print("image received!") image = cv2.imdecode( np.frombuffer(image_file.read(), np.uint8), cv2.IMREAD_COLOR ) decoded_objects = decode(image) if decoded_objects: print("image decoded!") print(decoded_objects[0]) code = decoded_objects[0].data.decode("utf-8") print("code received!") print(code) car = get_object_or_404(models.Car, vin=code) name = car.id_car_make.get_local_name print(name) return redirect("car_detail", pk=car.pk) else: print("back to else statement") return JsonResponse({"success": False, "error": "No code detected"}) else: return JsonResponse({"success": False, "error": "No image provided"}) class CarInventory(LoginRequiredMixin, ListView): model = models.Car home_label = _("inventory") template_name = "inventory/car_inventory.html" context_object_name = "cars" paginate_by = 10 ordering = ["receiving_date"] def get_queryset(self, *args, **kwargs): query = self.request.GET.get("q") make_id = self.kwargs["make_id"] model_id = self.kwargs["model_id"] trim_id = self.kwargs["trim_id"] dealer = get_user_type(self.request) cars = models.Car.objects.filter( dealer=dealer, id_car_make=make_id, id_car_model=model_id, id_car_trim=trim_id, ).order_by("receiving_date") if query: cars = cars.filter(Q(vin__icontains=query)) return cars def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["query"] = self.request.GET.get("q", "") context["make_id"] = self.kwargs["make_id"] context["model_id"] = self.kwargs["model_id"] context["trim_id"] = self.kwargs["trim_id"] return context class CarColorCreate(LoginRequiredMixin, CreateView): model = models.CarColors form_class = forms.CarColorsForm template_name = "inventory/add_colors.html" def form_valid(self, form): car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) form.instance.car = car return super().form_valid(form) def get_success_url(self): return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]}) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) return context @login_required def inventory_stats_view(request): dealer = get_user_type(request) # Base queryset for cars belonging to the dealer cars = models.Car.objects.filter(dealer=dealer) # Count for total, reserved, showroom, and unreserved cars total_cars = cars.count() reserved_cars = models.CarReservation.objects.count() # showroom_cars = cars.filter(location='showroom').count() # unreserved_cars = total_cars - reserved_cars # Annotate total cars by make, model, and trim cars = cars.select_related("id_car_make", "id_car_model", "id_car_trim").annotate( make_total=Count("id_car_make"), model_total=Count("id_car_model"), trim_total=Count("id_car_trim"), ) inventory = {} for car in cars: make = car.id_car_make if make.id_car_make not in inventory: inventory[make.id_car_make] = { "make_id": make.id_car_make, "make_name": make.get_local_name(), "total_cars": 0, "models": {}, } inventory[make.id_car_make]["total_cars"] += 1 model = car.id_car_model if model and model.id_car_model not in inventory[make.id_car_make]["models"]: inventory[make.id_car_make]["models"][model.id_car_model] = { "model_id": model.id_car_model, "model_name": model.get_local_name(), "total_cars": 0, "trims": {}, } try: inventory[make.id_car_make]["models"][model.id_car_model]["total_cars"] += 1 trim = car.id_car_trim if ( trim and trim.id_car_trim not in inventory[make.id_car_make]["models"][model.id_car_model][ "trims" ] ): inventory[make.id_car_make]["models"][model.id_car_model]["trims"][ trim.id_car_trim ] = { "trim_id": trim.id_car_trim, "trim_name": trim.name, "total_cars": 0, } inventory[make.id_car_make]["models"][model.id_car_model]["trims"][ trim.id_car_trim ]["total_cars"] += 1 except Exception as e: print(e) result = { "total_cars": total_cars, "reserved_cars": reserved_cars, "makes": [ { "make_id": make_data["make_id"], "make_name": make_data["make_name"], "total_cars": make_data["total_cars"], "models": [ { "model_id": model_data["model_id"], "model_name": model_data["model_name"], "total_cars": model_data["total_cars"], "trims": list(model_data["trims"].values()), } for model_data in make_data["models"].values() ], } for make_data in inventory.values() ], } return render(request, "inventory/inventory_stats.html", {"inventory": result}) class CarDetailView(LoginRequiredMixin, DetailView): model = models.Car template_name = "inventory/car_detail.html" context_object_name = "car" class CarFinanceCreateView(LoginRequiredMixin, CreateView): model = models.CarFinance form_class = forms.CarFinanceForm template_name = "inventory/car_finance_form.html" def dispatch(self, request, *args, **kwargs): self.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) return super().dispatch(request, *args, **kwargs) def form_valid(self, form): form.instance.car = self.car messages.success(self.request, _("Car finance details saved successfully.")) return super().form_valid(form) def get_success_url(self): return reverse("car_detail", kwargs={"pk": self.car.pk}) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["car"] = self.car return context def get_form(self, form_class=None): form = super().get_form(form_class) dealer = get_user_type(self.request) form.fields[ "additional_finances" ].queryset = models.AdditionalServices.objects.filter(dealer=dealer) return form # def get_initial(self): # initial = super().get_initial() # instance = self.get_object() # dealer = get_user_type(self.request.user.dealer) # selected_items = instance.additional_services.filter(dealer=dealer) # initial["additional_finances"] = selected_items # return initial class CarFinanceUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = models.CarFinance form_class = forms.CarFinanceForm template_name = "inventory/car_finance_form.html" success_message = _("Car finance details updated successfully.") def get_success_url(self): return reverse("car_detail", kwargs={"pk": self.object.car.pk}) def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["instance"] = self.get_object() return kwargs def get_initial(self): initial = super().get_initial() instance = self.get_object() dealer = get_user_type(self.request) selected_items = instance.additional_services.filter(dealer=dealer) initial["additional_finances"] = selected_items return initial def get_form(self, form_class=None): form = super().get_form(form_class) dealer = get_user_type(self.request) form.fields[ "additional_finances" ].queryset = models.AdditionalServices.objects.filter(dealer=dealer) return form class CarUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = models.Car form_class = forms.CarUpdateForm template_name = "inventory/car_edit.html" success_message = _("Car updated successfully.") def get_success_url(self): return reverse("car_detail", kwargs={"pk": self.object.pk}) class CarDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): model = models.Car template_name = "inventory/car_confirm_delete.html" success_url = reverse_lazy("inventory_stats") def delete(self, request, *args, **kwargs): messages.success(request, _("Car deleted successfully.")) return super().delete(request, *args, **kwargs) class CarLocationCreateView(CreateView): model = models.CarLocation form_class = forms.CarLocationForm template_name = "inventory/car_location_form.html" def get_success_url(self): return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk}) def form_valid(self, form): form.instance.car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) dealer = get_user_type(self.request) form.instance.owner = dealer form.save() messages.success(self.request, "Car saved successfully.") return super().form_valid(form) class CarLocationUpdateView(UpdateView): model = models.CarLocation form_class = forms.CarLocationForm template_name = "inventory/car_location_form.html" def get_success_url(self): return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk}) class CarTransferCreateView(CreateView): model = models.CarTransfer form_class = forms.CarTransferForm template_name = "inventory/car_location_form.html" def get_form(self, form_class=None): form = super().get_form(form_class) form.fields["to_dealer"].queryset = models.Dealer.objects.exclude( pk=get_user_type(self.request).pk ).all() form.fields["car"].queryset = models.Car.objects.filter(pk=self.kwargs["pk"]) return form def get_initial(self): initial = super().get_initial() initial["car"] = get_object_or_404(models.Car, pk=self.kwargs["pk"]) return initial def form_valid(self, form): form.instance.from_dealer = get_user_type(self.request) form.instance.car.status = "transfer" form.instance.car.save() return super().form_valid(form) def get_success_url(self): return reverse_lazy("car_detail", kwargs={"pk": self.object.car.pk}) # def CarTransferDetailView(request, pk): # transfer = get_object_or_404(models.CarTransfer, pk=pk) # context = {"transfer": transfer} # return render(request, "inventory/transfer_details.html", context) class CarTransferDetailView(LoginRequiredMixin, SuccessMessageMixin, DetailView): model = models.CarTransfer template_name = "inventory/transfer_details.html" context_object_name = "transfer" def car_transfer_approve(request, car_pk, transfer_pk): car = get_object_or_404(models.Car, pk=car_pk) transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) action = request.GET.get("action") if action == "cancel": transfer.status = "cancel" transfer.active = False transfer.save() transfer.car.status = "available" transfer.car.save() messages.success(request, _("Car transfer canceled successfully.")) models.Notification.objects.create( user=transfer.from_dealer.user, message=f"Car transfer request from {transfer.to_dealer} is canceled.", ) return redirect("car_detail", pk=car.pk) transfer.status = "approved" transfer.save() url = request.build_absolute_uri( reverse( "transfer_preview", kwargs={"car_pk": car.pk, "transfer_pk": transfer.pk} ) ) models.Notification.objects.create( user=transfer.to_dealer.user, message=f"Car transfer request from {transfer.from_dealer} is waiting for your acceptance. Accept", ) messages.success(request, _("Car transfer approved successfully.")) return redirect("car_detail", pk=car.pk) def car_transfer_accept_reject(request, car_pk, transfer_pk): car = get_object_or_404(models.Car, pk=car_pk) transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) status = request.GET.get("status") if status == "rejected": transfer.status = "reject" transfer.active = False messages.success(request, _("Car transfer rejected successfully.")) models.Notification.objects.create( user=transfer.from_dealer.user, message=f"Car transfer request from {transfer.to_dealer} is rejected.", ) transfer.save() elif status == "accepted": transfer.status = "accept" transfer.save() transfer_process = CarTransfer(car, transfer) success = transfer_process.transfer_car() # success = CarTransfer(car, transfer) if success: messages.success(request, _("Car Transfer Completed successfully.")) models.Notification.objects.create( user=transfer.from_dealer.user, message=f"Car transfer request from {transfer.to_dealer} is completed.", ) return redirect("inventory_stats") def CarTransferPreviewView(request, car_pk, transfer_pk): transfer = get_object_or_404(models.CarTransfer, pk=transfer_pk) if transfer.to_dealer != get_user_type(request): return redirect("car_detail", pk=car_pk) return render(request, "inventory/transfer_preview.html", {"transfer": transfer}) # def get_context_data(self, **kwargs): # estimate = kwargs.get("object") # if estimate.get_itemtxs_data(): # data = get_financial_values(estimate) # kwargs["vat_amount"] = data["vat_amount"] # kwargs["total"] = data["grand_total"] # kwargs["discount_amount"] = data["discount_amount"] # kwargs["vat"] = data["vat"] # kwargs["car_and_item_info"] = data["car_and_item_info"] # kwargs["additional_services"] = data["additional_services"] # return super().get_context_data(**kwargs) # class CarTransferView(View): # template_name = "inventory/car_location_form.html" # def get(self, request, *args, **kwargs): # form = forms.CarTransferForm() # car = models.Car.objects.filter(pk=self.kwargs["pk"]) # form.fields['to_dealer'].queryset = form.fields['to_dealer'].queryset.exclude(pk=get_user_type(request).pk) # form.fields['car'].queryset = car # form.initial['car'] = car.first() # context = {"form": form} # return render(request, self.template_name,context) # def post(self, request, *args, **kwargs): # form = forms.CarTransferForm(request.POST) # if form.is_valid(): # from_dealer = get_user_type(request) # car = form.cleaned_data['car'] # to_dealer = form.cleaned_data['to_dealer'] # remarks = form.cleaned_data['remarks'] # models.CarTransferLog.objects.create(car=car, from_dealer=from_dealer, to_dealer=to_dealer, remarks=remarks) # # car = models.Car.objects.filter(pk=self.kwargs["pk"]) # # form.instance.car = car.first() # # form.instance.to_dealer = get_user_type(request) # # form.save() # # messages.success(request, "Car transfered successfully.") # return redirect("car_detail", pk=self.kwargs["pk"]) class CustomCardCreateView(LoginRequiredMixin, CreateView): model = models.CustomCard form_class = forms.CustomCardForm template_name = "inventory/add_custom_card.html" def form_valid(self, form): car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) form.instance.car = car return super().form_valid(form) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) return context def get_success_url(self): messages.success(self.request, _("Custom Card added successfully.")) return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]}) class CarRegistrationCreateView(LoginRequiredMixin, CreateView): model = models.CarRegistration form_class = forms.CarRegistrationForm template_name = 'inventory/car_registration_form.html' def form_valid(self, form): car = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) form.instance.car = car return super().form_valid(form) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["car"] = get_object_or_404(models.Car, pk=self.kwargs["car_pk"]) return context def get_success_url(self): messages.success(self.request, _("Registration added successfully.")) return reverse_lazy("car_detail", kwargs={"pk": self.kwargs["car_pk"]}) @login_required() def reserve_car_view(request, car_id): if request.method == "POST": car = get_object_or_404(models.Car, pk=car_id) if car.is_reserved(): messages.error(request, _("This car is already reserved.")) return redirect("car_detail", pk=car.pk) response = reserve_car(car, request) return response return JsonResponse( {"success": False, "message": "Invalid request method."}, status=400 ) @login_required def manage_reservation(request, reservation_id): reservation = get_object_or_404( models.CarReservation, pk=reservation_id, reserved_by=request.user ) if request.method == "POST": action = request.POST.get("action") if action == "renew": reservation.reserved_until = timezone.now() + timezone.timedelta(hours=24) reservation.save() messages.success(request, _("Reservation renewed successfully.")) return redirect("car_detail", pk=reservation.car.pk) elif action == "cancel": car = reservation.car reservation.delete() car.status = models.CarStatusChoices.AVAILABLE car.save() messages.success(request, _("Reservation canceled successfully.")) return redirect("car_detail", pk=reservation.car.pk) else: return JsonResponse( {"success": False, "message": _("Invalid action.")}, status=400 ) return JsonResponse( {"success": False, "message": _("Invalid request method.")}, status=400 ) class DealerDetailView(LoginRequiredMixin, DetailView): model = models.Dealer template_name = "dealers/dealer_detail.html" context_object_name = "dealer" def get_queryset(self): total_count = models.Dealer.objects.annotate( staff_count=Coalesce(Count("staff"), Value(0)), total_count=F("staff_count") + Value(1), ) return total_count class DealerUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = models.Dealer form_class = forms.DealerForm template_name = "dealers/dealer_form.html" success_url = reverse_lazy("dealer_detail") success_message = _("Dealer updated successfully.") def get_success_url(self): return reverse("dealer_detail", kwargs={"pk": self.object.pk}) # def get_form(self, form_class=None): # form = super().get_form(form_class) # if hasattr(form.fields, "dealer_type"): # form.fields.pop("dealer_type") # return form # # def get_form_class(self): # if self.request.user.dealer.dealer_type == "OWNER": # return forms.DealerForm # else: # return forms.UserForm class CustomerListView(LoginRequiredMixin, ListView): model = CustomerModel home_label = _("customers") context_object_name = "customers" paginate_by = 10 template_name = "customers/customer_list.html" ordering = ["-created"] def get_queryset(self): query = self.request.GET.get("q") dealer = get_user_type(self.request) customers = dealer.entity.get_customers().filter( active=True, additional_info__type="customer" ) if query: customers = customers.filter( Q(first_name__icontains=query) | Q(last_name__icontains=query) | Q(additional_info__info__icontains=query) ) return customers def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["query"] = self.request.GET.get("q", "") return context class CustomerDetailView(LoginRequiredMixin, DetailView): model = CustomerModel template_name = "customers/view_customer.html" context_object_name = "customer" def get_context_data(self, **kwargs): dealer = get_user_type(self.request) entity = dealer.entity context = super().get_context_data(**kwargs) # customer = f"{context['customer'].first_name} {context['customer'].middle_name} {context['customer'].last_name}" # context["estimates"] = entity.get_estimates().filter( # customer__customer_name=name # ) context["estimates"] = context["customer"].estimatemodel_set.all() # context["notes"] = models.Notes.objects.filter( # content_type__model="customer", object_id=self.object.id # ) # context["activities"] = models.Activity.objects.filter( # content_type__model="customer", object_id=self.object.id # ) return context def add_note_to_customer(request, pk): customer = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": form = forms.NoteForm(request.POST) if form.is_valid(): note = form.save(commit=False) note.content_object = customer note.created_by = request.user note.save() return redirect("customer_detail", pk=pk) else: form = forms.NoteForm() return render(request, "crm/add_note.html", {"form": form, "customer": customer}) def add_activity_to_customer(request, pk): customer = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": form = forms.ActivityForm(request.POST) if form.is_valid(): activity = form.save(commit=False) activity.content_object = customer activity.created_by = request.user activity.save() return redirect("customer_detail", pk=pk) else: form = forms.ActivityForm() return render( request, "crm/add_activity.html", {"form": form, "customer": customer} ) def CustomerCreateView(request): if request.method == "POST": customer_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken" } dealer = get_user_type(request) customer_name = ( customer_dict["first_name"] + " " + customer_dict["middle_name"] + " " + customer_dict["last_name"] ) instance = dealer.entity.create_customer( customer_model_kwargs={ "customer_name": customer_name, "address_1": customer_dict["address"], "phone": customer_dict["phone_number"], "email": customer_dict["email"], } ) customer_dict["pk"] = str(instance.pk) instance.additional_info["customer_info"] = customer_dict instance.additional_info["type"] = "customer" instance.save() messages.success(request, _("Customer created successfully.")) return redirect("customer_list") form = forms.CustomerForm() return render(request, "customers/customer_form.html", {"form": form}) def CustomerUpdateView(request, pk): customer = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": # form = forms.CustomerForm(request.POST, instance=customer) customer_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken" } dealer = get_user_type(request) customer_name = ( customer_dict["first_name"] + " " + customer_dict["middle_name"] + " " + customer_dict["last_name"] ) instance = dealer.entity.get_customers().get(pk=pk) instance.customer_name = customer_name instance.address_1 = customer_dict["address"] instance.phone = customer_dict["phone_number"] instance.email = customer_dict["email"] customer_dict["pk"] = str(instance.pk) instance.additional_info["customer_info"] = customer_dict instance.save() messages.success(request, _("Customer updated successfully.")) return redirect("customer_list") else: form = forms.CustomerForm( initial=customer.additional_info["customer_info"] if "customer_info" in customer.additional_info else {} ) return render(request, "customers/customer_form.html", {"form": form}) @login_required def delete_customer(request, pk): customer = get_object_or_404(models.Customer, pk=pk) customer.delete() messages.success(request, _("Customer deleted successfully.")) return redirect("customer_list") class VendorListView(LoginRequiredMixin, ListView): model = VendorModel context_object_name = "vendors" paginate_by = 10 template_name = "vendors/vendors_list.html" ordering = ["-created"] def get_queryset(self): dealer = get_user_type(self.request) # vendors = models.Vendor.objects.filter(dealer=dealer) return dealer.entity.get_vendors().filter(active=True) # return vendors # class VendorDetailView(LoginRequiredMixin, DetailView): # model = models.Vendor # template_name = "vendors/view_vendor.html" def vendorDetailView(request, pk): vendor = get_object_or_404(models.Vendor, pk=pk) return render(request, template_name="vendors/view_vendor.html", context={"vendor": vendor}) class VendorCreateView( LoginRequiredMixin, SuccessMessageMixin, CreateView, ): model = models.Vendor form_class = forms.VendorForm template_name = "vendors/vendor_form.html" success_url = reverse_lazy("vendor_list") success_message = _("Vendor created successfully.") def form_valid(self, form): dealer = get_user_type(self.request) form.instance.dealer = dealer # instance = form.save(commit=False) # instance.entity_model = dealer.entity form.instance.save() return super().form_valid(form) class VendorUpdateView( LoginRequiredMixin, SuccessMessageMixin, UpdateView, ): model = models.Vendor form_class = forms.VendorForm template_name = "vendors/vendor_form.html" success_url = reverse_lazy("vendor_list") success_message = _("Vendor updated successfully.") @login_required def delete_vendor(request, pk): vendor = get_object_or_404(models.Vendor, pk=pk) # vendor.active = False vendor.delete() messages.success(request, _("Vendor deleted successfully.")) return redirect("vendor_list") # class QuotationCreateView(LoginRequiredMixin, CreateView): # model = models.SaleQuotation # form_class = forms.QuotationForm # template_name = "sales/quotation_form.html" # # def form_valid(self, form): # form.instance.dealer = get_user_type(self.request) # quotation = form.save() # selected_cars = form.cleaned_data.get("cars") # for car in selected_cars: # car_finance = car.finances # if car_finance: # models.SaleQuotationCar.objects.create( # quotation=quotation, # car=car, # ) # # messages.success(self.request, _("Quotation created successfully.")) # return redirect("quotation_list") # class QuotationListView(LoginRequiredMixin, ListView): # model = models.SaleQuotation # template_name = "sales/quotation_list.html" # context_object_name = "quotations" # paginate_by = 10 # # def get_queryset(self): # status = self.request.GET.get("status") # dealer = get_user_type(self.request) # queryset = dealer.sales.all() # if status: # queryset = queryset.filter(status=status) # return queryset # class QuotationDetailView(LoginRequiredMixin, DetailView): # model = models.SaleQuotation # template_name = "sales/quotation_detail.html" # context_object_name = "quotation" # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # quotation = self.object # # context_result = get_calculations(quotation) # context.update(context_result) # # return context # @login_required # def generate_invoice(request, pk): # quotation = get_object_or_404(models.SaleQuotation, pk=pk) # dealer = get_user_type(request) # entity = dealer.entity # if not quotation.is_approved: # messages.error( # request, "Quotation must be approved before converting to an invoice." # ) # else: # coa_qs, coa_map = entity.get_all_coa_accounts() # cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash") # recivable_account = ( # coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable") # ) # customer = ( # entity.get_customers() # .filter(customer_name=quotation.customer.get_full_name) # .first() # ) # # invoice_model = entity.create_invoice( # customer_model=customer, # terms=InvoiceModel.TERMS_ON_RECEIPT, # cash_account=cash_account.first(), # prepaid_account=recivable_account.first(), # coa_model=coa_qs.first(), # ) # # name_list = [ # f"{instance.car.year} {instance.car.id_car_make} {instance.car.id_car_model} {instance.car.id_car_trim}" # for instance in quotation.quotation_cars.all() # ] # # invoices_item_models = invoice_model.get_item_model_qs().filter( # name__in=name_list # ) # # invoice_itemtxs = { # im.item_number: { # "unit_cost": im.default_amount, # "quantity": 1, # "total_amount": im.default_amount, # } # for im in invoices_item_models # } # # invoice_itemtxs = invoice_model.migrate_itemtxs( # itemtxs=invoice_itemtxs, commit=True, operation=InvoiceModel.ITEMIZE_APPEND # ) # ledger = ( # entity.get_ledgers() # .filter(name=f"Payment Ledger for Invoice {invoice_model}") # .first() # ) # if not ledger: # ledger = entity.create_ledger( # name=f"Payment Ledger for Invoice {invoice_model}", posted=True # ) # journal_entry = JournalEntryModel.objects.create( # posted=False, # description=f"Payment for Invoice {invoice_model}", # ledger=ledger, # locked=False, # origin="Payment", # ) # # quotation.payment_id = journal_entry.pk # quotation.is_approved = True # date = datetime.datetime.now() # quotation.date_draft = date # invoice_model.date_draft = date # invoice_model.save() # quotation.save() # # if not invoice_model.can_review(): # messages.error(request, "Quotation is not ready for review") # return redirect("quotation_detail", pk=pk) # # invoice_model.mark_as_review() # invoice_model.date_in_review = date # quotation.date_in_review = date # quotation.status = "In Review" # invoice_model.save() # quotation.save() # elif status == "approved": # if qoutation.status == "Approved": # messages.error(request, "Quotation is already approved") # return redirect("quotation_detail", pk=pk) # invoice_model = invoice_model.filter(date_in_review=qoutation.date_in_review).first() # if not invoice_model.can_approve(): # messages.error(request, "Quotation is not ready for approval") # return redirect("quotation_detail", pk=pk) # invoice_model.mark_as_approved(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user) # invoice_model.date_approved = date # qoutation.date_approved = date # invoice_model.save() # qoutation.status = "Approved" # qoutation.save() # messages.success(request, _("Quotation Approved")) # ledger = entity.create_ledger( # name=f"Payment Ledger for Invoice {invoice_model}", # posted=True # ) # entity_unit,created = EntityUnitModel.objects.get_or_create( # name="Sales Department", # entity=entity, # document_prefix="SD" # ) # journal_entry = JournalEntryModel.objects.create( # entity_unit=entity_unit, # posted=False, # description=f"Payment for Invoice {invoice_model}", # ledger=ledger, # locked=False, # origin="Payment", # ) # accounts_receivable = coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable").first() # if not accounts_receivable: # accounts_receivable = entity.create_account( # code="AR", # role="asset", # name="Accounts Receivable", # coa_model=coa_qs.first(), # balance_type="credit" # ) # TransactionModel.objects.create( # journal_entry=journal_entry, # account=cash_account.first(), # Debit Cash # amount=invoice_model.amount_due, # Payment amount # tx_type='debit', # description="Payment Received", # ) # TransactionModel.objects.create( # journal_entry=journal_entry, # account=accounts_receivable, # Credit Accounts Receivable # amount=invoice_model.amount_due, # Payment amount # tx_type='credit', # description="Payment Received", # ) # invoice_model.mark_as_review() # print("reviewed") # invoice_model.mark_as_approved(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user) # print("approved") # invoice_model.mark_as_paid(entity_slug=entity.slug, user_model=request.user.dealer.get_root_dealer.user) # print("paid") # invoice_model.save() # messages.success(request, "Invoice created") # return redirect("quotation_detail", pk=pk) # return redirect('django_ledger:invoice-detail', entity_slug=quotation.entity.slug, invoice_pk=invoice.uuid) # @login_required # def post_quotation(request, pk): # qoutation = get_object_or_404(models.SaleQuotation, pk=pk) # dealer = get_user_type(request) # entity = dealer.entity # if qoutation.posted: # messages.error(request, "Quotation is already posted") # return redirect("quotation_detail", pk=pk) # coa_qs, coa_map = entity.get_all_coa_accounts() # cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash") # recivable_account = ( # coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable") # ) # customer = ( # entity.get_customers() # .filter(customer_name=qoutation.customer.get_full_name) # .first() # ) # invoice_model = ( # entity.get_invoices() # .filter(customer=customer, date_paid=qoutation.date_paid) # .first() # ) # ledger = ( # entity.get_ledgers() # .filter(name=f"Payment Ledger for Invoice {invoice_model}") # .first() # ) # return # if not ledger: # ledger = entity.create_ledger(name=f"Payment Ledger for Invoice {invoice_model}",posted=True) # entity_unit,created = EntityUnitModel.objects.get_or_create( # name="Sales Department", # entity=entity, # document_prefix="SD" # ) # journal_entry = JournalEntryModel.objects.create( # entity_unit=entity_unit, # posted=False, # description=f"Payment for Invoice {invoice_model}", # ledger=ledger, # locked=False, # origin="Payment", # ) # TransactionModel.objects.create( # journal_entry=journal_entry, # account=cash_account.first(), # Debit Cash # amount=invoice_model.amount_due, # Payment amount # tx_type='debit', # description="Payment Received", # ) # TransactionModel.objects.create( # journal_entry=journal_entry, # account=recivable_account.first(), # Credit Accounts Receivable # amount=invoice_model.amount_due, # Payment amount # tx_type='credit', # description="Payment Received", # ) # journal_entry.posted = True # qoutation.posted = True # qoutation.save() # journal_entry.save() # messages.success(request, "Invoice posted") # return redirect("quotation_detail", pk=pk) # @login_required # def mark_quotation(request, pk): # qoutation = get_object_or_404(models.SaleQuotation, pk=pk) # status = request.GET.get("status") # dealer = request.user.dealer # entity = dealer.entity # date = datetime.datetime.now() # customer = ( # entity.get_customers() # .filter(customer_name=qoutation.customer.get_full_name) # .first() # ) # invoice_model = entity.get_invoices().filter(customer=customer) # if status == "approved": # if qoutation.status == "Approved": # messages.error(request, "Quotation is already approved") # return redirect("quotation_detail", pk=pk) # # invoice_model = invoice_model.filter( # date_in_review=qoutation.date_in_review # ).first() # if not invoice_model.can_approve(): # messages.error(request, "Quotation is not ready for approval") # return redirect("quotation_detail", pk=pk) # # invoice_model.mark_as_approved( # entity_slug=entity.slug, user_model=request.user.dealer # ) # invoice_model.date_approved = date # qoutation.date_approved = date # invoice_model.save() # qoutation.status = "Approved" # qoutation.save() # for car in qoutation.quotation_cars.all(): # car.car.status = "reserved" # car.car.save() # messages.success(request, _("Quotation Approved")) # elif status == "paid": # if qoutation.status == "Paid": # messages.error(request, "Quotation is already paid") # return redirect("quotation_detail", pk=pk) # # invoice_model = invoice_model.filter( # date_approved=qoutation.date_approved # ).first() # if not invoice_model.can_pay(): # messages.error(request, "Quotation is not ready for payment") # return redirect("quotation_detail", pk=pk) # # invoice_model.mark_as_paid( # entity_slug=entity.slug, user_model=request.user.dealer # ) # invoice_model.date_paid = date # qoutation.date_paid = date # invoice_model.save() # qoutation.status = "Paid" # qoutation.save() # messages.success(request, _("Quotation Paid")) # return redirect("quotation_detail", pk=pk) # @login_required # def confirm_quotation(request, pk): # quotation = get_object_or_404(models.SaleQuotation, pk=pk) # if quotation.is_approved: # messages.error(request, _("Quotation already approved.")) # return redirect("quotation_detail", pk=pk) # # try: # # quotation.confirm() # # quotation_cars = quotation.quotation_cars.annotate(total_price=F('car__total') * F('quantity')) # # total = quotation.quotation_cars.aggregate(total_price=Sum(F('car__finances__selling_price') * F('quantity'))) # # models.SalesOrder.objects.create( # quotation=quotation, # total_amount=quotation.total_vat, # # total_amount=quotation.quotation_cars.aggregate(Sum("total_amount"))["total_amount__sum"], # ) # quotation.is_approved = True # quotation.save() # messages.success(request, _("Quotation confirmed and sales order created.")) # except ValueError as e: # messages.error(request, str(e)) # return redirect("quotation_detail", pk=pk) # # class SalesOrderDetailView(LoginRequiredMixin, DetailView): # model = models.SalesOrder # template_name = "sales/sales_order_detail.html" # context_object_name = "sales_order" # slug_field = "order_id" # slug_url_kwarg = "order_id" # Users class UserListView(LoginRequiredMixin, ListView): model = models.Staff context_object_name = "users" paginate_by = 10 template_name = "users/user_list.html" def get_queryset(self): dealer = get_user_type(self.request) return models.Staff.objects.filter(dealer=dealer).all() class UserDetailView(LoginRequiredMixin, DetailView): model = models.Staff template_name = "users/user_detail.html" context_object_name = "user_" class UserCreateView( LoginRequiredMixin, SuccessMessageMixin, CreateView, ): model = models.Staff form_class = forms.StaffForm template_name = "users/user_form.html" success_url = reverse_lazy("user_list") success_message = _("User created successfully.") def form_valid(self, form): dealer = get_user_type(self.request) form.instance.dealer = dealer email = form.cleaned_data["email"] password = "Tenhal@123" user = User.objects.create_user(username=email, email=email, password=password) staff = form.save(commit=False) staff.user = user staff.save() return super().form_valid(form) class UserUpdateView( LoginRequiredMixin, SuccessMessageMixin, UpdateView, ): model = models.Staff form_class = forms.StaffForm template_name = "users/user_form.html" success_url = reverse_lazy("user_list") success_message = _("User updated successfully.") def get_form_kwargs(self): kwargs = super().get_form_kwargs() kwargs["instance"] = self.get_object() # Pass the Staff instance to the form return kwargs def UserDeleteview(request, pk): user = get_object_or_404(models.Staff, pk=pk) user.delete() messages.success(request, _("User deleted successfully.")) return redirect("user_list") # errors def custom_page_not_found_view(request, exception): return render(request, "errors/404.html", {}) def custom_error_view(request, exception=None): return render(request, "errors/500.html", {}) def custom_permission_denied_view(request, exception=None): return render(request, "errors/403.html", {}) def custom_bad_request_view(request, exception=None): return render(request, "errors/400.html", {}) class OrganizationListView(LoginRequiredMixin, ListView): model = CustomerModel template_name = "organizations/organization_list.html" context_object_name = "organizations" paginate_by = 10 def get_queryset(self): dealer = get_user_type(self.request) return ( dealer.entity.get_customers() .filter(additional_info__type="organization", active=True) .all() ) class OrganizationDetailView(DetailView): model = models.Organization template_name = "organizations/organization_detail.html" context_object_name = "organization" def OrganizationCreateView(request): if request.method == "POST": form = forms.OrganizationForm(request.POST) # upload logo image = request.FILES.get("logo") file_name = default_storage.save("images/{}".format(image.name), image) file_url = default_storage.url(file_name) organization_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken" } dealer = get_user_type(request) instance = dealer.entity.create_customer( customer_model_kwargs={ "customer_name": organization_dict["name"], "address_1": organization_dict["address"], "phone": organization_dict["phone_number"], "email": organization_dict["email"], } ) organization_dict["logo"] = file_url organization_dict["pk"] = str(instance.pk) instance.additional_info["organization_info"] = organization_dict instance.additional_info["type"] = "organization" instance.save() messages.success(request, _("Organization created successfully.")) return redirect("organization_list") else: form = forms.OrganizationForm() return render(request, "organizations/organization_form.html", {"form": form}) def OrganizationUpdateView(request,pk): organization = get_object_or_404(CustomerModel, pk=pk) if request.method == "POST": form = forms.OrganizationForm(request.POST) organization_dict = { x: request.POST[x] for x in request.POST if x != "csrfmiddlewaretoken" } dealer = get_user_type(request) instance = dealer.entity.get_customers().get( pk=organization.additional_info["organization_info"]["pk"] ) instance.customer_name = organization_dict["name"] instance.address_1 = organization_dict["address"] instance.phone = organization_dict["phone_number"] instance.email = organization_dict["email"] organization_dict["logo"] = organization.additional_info["organization_info"][ "logo" ] organization_dict["pk"] = str(instance.pk) instance.additional_info["organization_info"] = organization_dict instance.additional_info["type"] = "organization" instance.save() messages.success(request, _("Organization created successfully.")) return redirect("organization_list") else: form = forms.OrganizationForm( initial=organization.additional_info["organization_info"] or {} ) form.fields.pop("logo", None) return render(request, "organizations/organization_form.html", {"form": form}) class OrganizationDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): model = models.Organization template_name = "organizations/organization_confirm_delete.html" success_url = reverse_lazy("organization_list") success_message = "Organization deleted successfully." class RepresentativeListView(LoginRequiredMixin, ListView): model = models.Representative template_name = "representatives/representative_list.html" context_object_name = "representatives" paginate_by = 10 def get_queryset(self): dealer = get_user_type(self.request) return models.Representative.objects.filter(dealer=dealer).all() class RepresentativeDetailView(DetailView): model = models.Representative template_name = "representatives/representative_detail.html" context_object_name = "representative" class RepresentativeCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = models.Representative form_class = forms.RepresentativeForm template_name = "representatives/representative_form.html" success_url = reverse_lazy("representative_list") success_message = "Representative created successfully." def form_valid(self, form): if form.is_valid(): form.instance.dealer = self.request.user.dealer form.save() return super().form_valid(form) else: return form.errors class RepresentativeUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = models.Representative form_class = forms.RepresentativeForm template_name = "representatives/representative_form.html" success_url = reverse_lazy("representative_list") success_message = "Representative updated successfully." class RepresentativeDeleteView(LoginRequiredMixin, SuccessMessageMixin, DeleteView): model = models.Representative template_name = "representatives/representative_confirm_delete.html" success_url = reverse_lazy("representative_list") success_message = "Representative deleted successfully." # def quotation_pdf_view(request, pk): # # Get the quotation object # quotation = models.SaleQuotation.objects.get(pk=pk) # # # Render the HTML template for the quotation page # context = { # "quotation": quotation, # } # context_result = get_calculations(quotation) # context = context.update(context_result) # # html_content = render_to_string("sales/quotation_pdf.html", context) # # # Create a PDF file # # pdf_file = HTML(string=html_content).render() # # # Save the PDF file to a file # with open("quotation.pdf", "wb") as f: # f.write(pdf_file.write_pdf()) # # # Return the PDF file as a response # return HttpResponse(pdf_file, content_type="application/pdf") # @login_required # def download_quotation_pdf(request, quotation_id): # try: # # Retrieve the quotation object # quotation = models.SaleQuotation.objects.get(id=quotation_id) # cars = models.SaleQuotationCar.objects.get(id=quotation_id) # print(cars) # services = cars.finance.additional_services.all() # print(services) # # # Create a response object # response = HttpResponse(content_type="application/pdf") # response["Content-Disposition"] = ( # f'attachment; filename="quotation_{quotation.id}.pdf"' # ) # # # Call the PDF generation function # # generate_quotation_pdf(response, quotation, services) # # return response # except models.SaleQuotation.DoesNotExist: # return HttpResponse("Quotation not found", status=404) # @login_required # def invoice_detail(request, pk): # quotation = get_object_or_404(models.SaleQuotation, pk=pk) # dealer = request.user.dealer # entity = dealer.entity # customer = ( # entity.get_customers() # .filter(customer_name=quotation.customer.get_full_name) # .first() # ) # invoice_model = entity.get_invoices() # # invoice = invoice_model.filter( # customer=customer, date_draft=quotation.date_draft # ).first() # return redirect("quotation_detail", pk=pk) # @login_required # def payment_invoice(request, pk): # quotation = get_object_or_404(models.SaleQuotation, pk=pk) # dealer = request.user.dealer # entity = dealer.entity # customer = ( # entity.get_customers() # .filter(customer_name=quotation.customer.get_full_name) # .first() # ) # invoice_model = entity.get_invoices() # invoice = invoice_model.filter( # customer=customer, date_draft=quotation.date_draft # ).first() # # return redirect("quotation_detail", pk=pk) # class PaymentCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): # model = models.Payment # form_class = forms.PaymentForm # template_name = "sales/payments/payment_form.html" # success_url = reverse_lazy("quotation_list") # success_message = "Payment created successfully." # def form_valid(self, form): # quotation = get_object_or_404(models.SaleQuotation, pk=self.kwargs["pk"]) # form.instance.quotation = quotation # form.save() # return super().form_valid(form) # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context["quotation"] = get_object_or_404(models.SaleQuotation, pk=self.kwargs["pk"]) # return context # def payment_create(request, pk): # quotation = get_object_or_404(models.SaleQuotation, pk=pk) # dealer = get_user_type(request) # if request.method == "POST": # form = forms.PaymentForm(request.POST) # if form.is_valid(): # form.instance.quotation = quotation # insatnce = form.save() # # dealer = dealer # entity = dealer.entity # customer = ( # entity.get_customers() # .filter(customer_name=quotation.customer.get_full_name) # .first() # ) # coa_qs, coa_map = entity.get_all_coa_accounts() # cash_account = coa_qs.first().get_coa_accounts().filter(name="Cash") # recivable_account = ( # coa_qs.first().get_coa_accounts().filter(name="Accounts Receivable") # ) # journal_entry = JournalEntryModel.objects.filter( # pk=quotation.payment_id # ).first() # TransactionModel.objects.create( # journal_entry=journal_entry, # account=cash_account.first(), # Debit Cash # amount=insatnce.amount, # Payment amount # tx_type="debit", # description="Payment Received", # ) # # TransactionModel.objects.create( # journal_entry=journal_entry, # account=recivable_account.first(), # Credit Accounts Receivable # amount=insatnce.amount, # Payment amount # tx_type="credit", # description="Payment Received", # ) # journal_entry.posted = True # quotation.posted = True # quotation.save() # journal_entry.save() # # invoice_model = ( # entity.get_invoices() # .filter(date_approved=quotation.date_approved) # .first() # ) # # invoice_model.mark_as_paid( # entity_slug=entity.slug, user_model=request.user.dealer # ) # date = timezone.now() # invoice_model.date_paid = date # quotation.date_paid = date # invoice_model.save() # quotation.status = "Paid" # quotation.save() # # messages.success(request, "Payment created successfully.") # return redirect("quotation_detail", pk=pk) # else: # form = forms.PaymentForm() # return render( # request, # "sales/payments/payment_create.html", # {"quotation": quotation, "form": form}, # ) # Ledger # BANK ACCOUNT class BankAccountListView(LoginRequiredMixin, ListView): model = BankAccountModel template_name = "ledger/bank_accounts/bank_account_list.html" context_object_name = "bank_accounts" paginate_by = 10 def get_queryset(self): dealer = get_user_type(self.request) return BankAccountModel.objects.filter(entity_model=dealer.entity) class BankAccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = BankAccountModel form_class = BankAccountCreateForm template_name = "ledger/bank_accounts/bank_account_form.html" success_url = reverse_lazy("bank_account_list") success_message = "Bank account created successfully." def form_valid(self, form): dealer = get_user_type(self.request) form.instance.entity_model = dealer.entity return super().form_valid(form) def get_form_kwargs(self): dealer = get_user_type(self.request) entity = dealer.entity kwargs = super().get_form_kwargs() kwargs["entity_slug"] = entity.slug kwargs["user_model"] = entity.admin return kwargs class BankAccountDetailView(LoginRequiredMixin, DetailView): model = BankAccountModel template_name = "ledger/bank_accounts/bank_account_detail.html" context_object_name = "bank_account" class BankAccountUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = BankAccountModel form_class = BankAccountUpdateForm template_name = "ledger/bank_accounts/bank_account_form.html" success_url = reverse_lazy("bank_account_list") success_message = "Bank account updated successfully." def get_form_kwargs(self): dealer = get_user_type(self.request) entity = dealer.entity kwargs = super().get_form_kwargs() kwargs["entity_slug"] = entity.slug # Get entity_slug from URL kwargs["user_model"] = entity.admin # Get user_model from the request return kwargs @login_required def bank_account_delete(request, pk): bank_account = get_object_or_404(BankAccountModel, pk=pk) if request.method == "POST": bank_account.delete() messages.success(request, "Bank account deleted successfully.") return redirect("bank_account_list") return render( request, "ledger/bank_accounts/bank_account_delete.html", {"bank_account": bank_account}, ) # Accounts class AccountListView(LoginRequiredMixin, ListView): model = AccountModel template_name = "ledger/coa_accounts/account_list.html" context_object_name = "accounts" paginate_by = 10 def get_queryset(self): dealer = get_user_type(self.request) entity = dealer.entity return entity.get_all_accounts() class AccountCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = AccountModel form_class = AccountModelCreateForm template_name = "ledger/coa_accounts/account_form.html" success_url = reverse_lazy("account_list") success_message = "Account created successfully." def form_valid(self, form): dealer = get_user_type(self.request) form.instance.entity_model = dealer.entity form.instance.coa_model = dealer.entity.get_default_coa() form.instance.depth = 0 return super().form_valid(form) def get_form_kwargs(self): dealer = get_user_type(self.request) kwargs = super().get_form_kwargs() kwargs["coa_model"] = dealer.entity.get_default_coa() return kwargs def get_form(self, form_class=None): form = super().get_form(form_class) entity = get_user_type(self.request).entity form.initial['coa_model'] = entity.get_default_coa() return form class AccountDetailView(LoginRequiredMixin, DetailView): model = AccountModel template_name = "ledger/coa_accounts/account_detail.html" context_object_name = "account" slug_field = "uuid" DEFAULT_TXS_DAYS = 30 extra_context = { "DEFAULT_TXS_DAYS": DEFAULT_TXS_DAYS, "header_subtitle_icon": "ic:round-account-tree", } def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) account_model: AccountModel = context["object"] context["header_title"] = f"Account {account_model.code} - {account_model.name}" context["page_title"] = f"Account {account_model.code} - {account_model.name}" context["total_debits"] = sum( x.amount for x in account_model.transactionmodel_set.filter(tx_type="debit") ) context["total_credits"] = sum( x.amount for x in account_model.transactionmodel_set.filter(tx_type="credit") ) txs_qs = ( account_model.transactionmodel_set.all() .posted() .order_by("journal_entry__timestamp") .select_related( "journal_entry", "journal_entry__entity_unit", "journal_entry__ledger__billmodel", "journal_entry__ledger__invoicemodel", ) ) return context class AccountUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = AccountModel form_class = AccountModelUpdateForm template_name = "ledger/coa_accounts/account_form.html" success_url = reverse_lazy("account_list") success_message = "Account updated successfully." def get_form(self, form_class=None): form = super().get_form(form_class) form.fields["_ref_node_id"].widget = HiddenInput() form.fields["_position"].widget = HiddenInput() return form @login_required def account_delete(request, pk): account = get_object_or_404(AccountModel, pk=pk) account.delete() messages.success(request, "Account deleted successfully.") return redirect("account_list") # Estimates class EstimateListView(LoginRequiredMixin, ListView): model = EstimateModel template_name = "sales/estimates/estimate_list.html" context_object_name = "estimates" paginate_by = 10 def get_queryset(self): dealer = get_user_type(self.request) entity = dealer.entity status = self.request.GET.get('status') queryset = entity.get_estimates() if status: queryset = queryset.filter(status=status) return queryset # class EstimateCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): # model = EstimateModel # form_class = EstimateModelCreateForm # template_name = "sales/estimates/estimate_form.html" # success_url = reverse_lazy("estimate_list") # success_message = "Estimate created successfully." # def get_form_kwargs(self): # """ # Override this method to pass additional keyword arguments to the form. # """ # entity = self.request.user.dealer.entity # kwargs = super().get_form_kwargs() # kwargs['entity_slug'] = entity.slug # kwargs['user_model'] = entity.admin # return kwargs # def get_context_data(self, **kwargs): # entity = self.request.user.dealer.entity # kwargs['items'] = entity.get_items_all() # return super().get_context_data(**kwargs) # def get_customer_queryset(self): # entity = self.request.user.dealer.entity # return entity.get_customer_queryset() # def form_valid(self, form): # form.instance.entity = self.request.user.dealer.entity # return super().form_valid(form) # @csrf_exempt @login_required def create_estimate(request): dealer = get_user_type(request) entity = dealer.entity if request.method == "POST": # try: data = json.loads(request.body) title = data.get("title") customer_id = data.get("customer") terms = data.get("terms") customer = entity.get_customers().filter(pk=customer_id).first() items = data.get("item", []) quantities = data.get("quantity", []) if not all([items, quantities]): return JsonResponse( {"status": "error", "message": "Items and Quantities are required"}, status=400, ) if isinstance(quantities, list): if "0" in quantities: return JsonResponse( {"status": "error", "message": "Quantity must be greater than zero"} ) else: if int(quantities) <= 0: return JsonResponse( {"status": "error", "message": "Quantity must be greater than zero"} ) estimate = entity.create_estimate( estimate_title=title, customer_model=customer, contract_terms=terms ) if isinstance(items, list): item_quantity_map = {} for item, quantity in zip(items, quantities): if item in item_quantity_map: item_quantity_map[item] += int(quantity) else: item_quantity_map[item] = int(quantity) item_list = list(item_quantity_map.keys()) quantity_list = list(item_quantity_map.values()) items_list = [ {"item_id": item_list[i], "quantity": quantity_list[i]} for i in range(len(item_list)) ] items_txs = [] for item in items_list: item_instance = ItemModel.objects.get(pk=item.get("item_id")) car_instance = models.Car.objects.get(vin=item_instance.name) items_txs.append( { "item_number": item_instance.item_number, "quantity": Decimal(item.get("quantity")), "unit_cost": car_instance.finances.selling_price, "unit_revenue": car_instance.finances.selling_price, "total_amount": (car_instance.finances.total_vat) * int(item.get("quantity")), } ) estimate_itemtxs = { item.get("item_number"): { "unit_cost": item.get("unit_cost"), "unit_revenue": item.get("unit_revenue"), "quantity": item.get("quantity"), "total_amount": item.get("total_amount"), } for item in items_txs } else: item = entity.get_items_all().filter(pk=items).first() instance = models.Car.objects.get(vin=item.name) estimate_itemtxs = { item.item_number: { "unit_cost": instance.finances.cost_price, "unit_revenue": instance.finances.selling_price, "quantity": Decimal(quantities), "total_amount": instance.finances.total_vat * int(quantities), } } estimate.migrate_itemtxs( itemtxs=estimate_itemtxs, commit=True, operation=EstimateModel.ITEMIZE_APPEND, ) if isinstance(items, list): for item in items: item_instance = ItemModel.objects.get(pk=item) instance = models.Car.objects.get(vin=item_instance.name) reserve_car(instance, request) else: item_instance = ItemModel.objects.get(pk=items) instance = models.Car.objects.get(vin=item_instance.name) response = reserve_car(instance, request) url = reverse("estimate_detail", kwargs={"pk": estimate.pk}) return JsonResponse( { "status": "success", "message": "Estimate created successfully!", "url": f"{url}", } ) form = forms.EstimateModelCreateForm( entity_slug=entity.slug, user_model=entity.admin ) form.fields["customer"].queryset = entity.get_customers().filter(active=True) car_list = models.Car.objects.filter( dealer=dealer, finances__selling_price__gt=0 ).exclude(status="reserved") context = { "form": form, "items": [ { "car": x, "product": entity.get_items_all() .filter(item_role=ItemModel.ITEM_ROLE_PRODUCT, name=x.vin) .first(), } for x in car_list ], } return render(request, "sales/estimates/estimate_form.html", context) class EstimateDetailView(LoginRequiredMixin, DetailView): model = EstimateModel template_name = "sales/estimates/estimate_detail.html" context_object_name = "estimate" def get_context_data(self, **kwargs): estimate = kwargs.get("object") if estimate.get_itemtxs_data(): calculator = CarFinanceCalculator(estimate) finance_data = calculator.get_finance_data() kwargs["data"] = finance_data kwargs["invoice"] = ( InvoiceModel.objects.all().filter(ce_model=estimate).first() ) return super().get_context_data(**kwargs) def create_sale_order(request, pk): estimate = get_object_or_404(EstimateModel, pk=pk) items = estimate.get_itemtxs_data()[0].all() if request.method == "POST": form = forms.SaleOrderForm(request.POST) if form.is_valid(): form.save() if not estimate.is_approved(): estimate.mark_as_approved() estimate.save() messages.success(request, "Sale Order created successfully") return redirect("estimate_detail", pk=pk) form = forms.SaleOrderForm() form.fields["estimate"].queryset = EstimateModel.objects.filter(pk=pk) form.initial["estimate"] = estimate # data = get_car_finance_data(estimate) calculator = CarFinanceCalculator(estimate) finance_data = calculator.get_finance_data() return render( request, "sales/estimates/sale_order_form.html", {"form": form, "estimate": estimate, "items": items, "data": finance_data}, ) def preview_sale_order(request, pk): estimate = get_object_or_404(EstimateModel, pk=pk) data = get_car_finance_data(estimate) return render( request, "sales/estimates/sale_order_preview.html", {"order": estimate.sale_orders.first(), "data": data, "estimate": estimate}, ) class PaymentRequest(LoginRequiredMixin, DetailView): model = EstimateModel template_name = "sales/estimates/payment_request_detail.html" context_object_name = "estimate" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["cars"] = [ models.Car.objects.get(vin=car.item_model.name) for car in context["estimate"].get_itemtxs_data()[0].all() ] return context class EstimatePreviewView(LoginRequiredMixin, DetailView): model = EstimateModel context_object_name = "estimate" template_name = "sales/estimates/estimate_preview.html" def get_context_data(self, **kwargs): estimate = kwargs.get("object") if estimate.get_itemtxs_data(): data = get_financial_values(estimate) kwargs["vat_amount"] = data["vat_amount"] kwargs["total"] = data["grand_total"] kwargs["discount_amount"] = data["discount_amount"] kwargs["vat"] = data["vat"] # kwargs["car_and_item_info"] = data["car_and_item_info"] kwargs["additional_services"] = data["additional_services"] return super().get_context_data(**kwargs) @login_required def estimate_mark_as(request, pk): estimate = get_object_or_404(EstimateModel, pk=pk) entity = estimate.entity mark = request.GET.get("mark") if mark: if mark == "review": if not estimate.can_review(): messages.error(request, "Estimate is not ready for review") return redirect("estimate_detail", pk=estimate.pk) estimate.mark_as_review() elif mark == "approved": if not estimate.can_approve(): messages.error(request, "Estimate is not ready for approval") return redirect("estimate_detail", pk=estimate.pk) estimate.mark_as_approved() messages.success(request, "Estimate approved successfully.") elif mark == "rejected": if not estimate.can_cancel(): messages.error(request, "Estimate is not ready for rejection") return redirect("estimate_detail", pk=estimate.pk) estimate.mark_as_canceled() messages.success(request, "Estimate canceled successfully.") elif mark == "completed": if not estimate.can_complete(): messages.error(request, "Estimate is not ready for completion") return redirect("estimate_detail", pk=estimate.pk) estimate.save() messages.success(request, "Estimate marked as " + mark.upper()) return redirect("estimate_detail", pk=estimate.pk) # Invoice class InvoiceListView(LoginRequiredMixin, ListView): model = InvoiceModel template_name = "sales/invoices/invoice_list.html" context_object_name = "invoices" paginate_by = 20 def get_queryset(self): dealer = get_user_type(self.request) entity = dealer.entity return entity.get_invoices() class InvoiceDetailView(LoginRequiredMixin, DetailView): model = InvoiceModel template_name = "sales/invoices/invoice_detail.html" context_object_name = "invoice" def get_context_data(self, **kwargs): invoice = kwargs.get("object") if invoice.get_itemtxs_data(): calculator = CarFinanceCalculator(invoice) finance_data = calculator.get_finance_data() print( (finance_data["total_vat_amount"] + finance_data["total_price"]) == finance_data["grand_total"] ) kwargs["data"] = finance_data kwargs["payments"] = JournalEntryModel.objects.filter( ledger=invoice.ledger ).all() return super().get_context_data(**kwargs) class DraftInvoiceModelUpdateFormView(LoginRequiredMixin, UpdateView): model = InvoiceModel form_class = DraftInvoiceModelUpdateForm template_name = "sales/invoices/draft_invoice_update.html" success_url = reverse_lazy("invoice_list") def get_form_kwargs(self): kwargs = super().get_form_kwargs() dealer = get_user_type(self.request) kwargs["entity_slug"] = dealer.entity kwargs["user_model"] = dealer.entity.admin return kwargs class ApprovedInvoiceModelUpdateFormView(LoginRequiredMixin, UpdateView): model = InvoiceModel form_class = ApprovedInvoiceModelUpdateForm template_name = "sales/invoices/approved_invoice_update.html" success_url = reverse_lazy("invoice_list") def get_form_kwargs(self): kwargs = super().get_form_kwargs() dealer = get_user_type(self.request) kwargs["entity_slug"] = dealer.entity kwargs["user_model"] = dealer.entity.admin return kwargs def get_success_url(self): return reverse_lazy("invoice_detail", kwargs={"pk": self.object.pk}) class PaidInvoiceModelUpdateFormView(LoginRequiredMixin, UpdateView): model = InvoiceModel form_class = PaidInvoiceModelUpdateForm template_name = "sales/invoices/paid_invoice_update.html" success_url = reverse_lazy("invoice_list") def get_form_kwargs(self): kwargs = super().get_form_kwargs() dealer = get_user_type(self.request) kwargs["entity_slug"] = dealer.entity kwargs["user_model"] = dealer.entity.admin return kwargs def get_success_url(self): return reverse_lazy("invoice_detail", kwargs={"pk": self.object.pk}) def form_valid(self, form): invoice = form.save() if invoice.get_amount_open() > 0: messages.error(self.request, "Invoice is not fully paid") return redirect("invoice_detail", pk=invoice.pk) else: invoice.post_ledger() invoice.save() return super().form_valid(form) @login_required def invoice_mark_as(request, pk): invoice = get_object_or_404(InvoiceModel, pk=pk) dealer = get_user_type(request) mark = request.GET.get("mark") if mark and mark == "accept": if not invoice.can_approve(): messages.error(request, "invoice is not ready for approval") return redirect("invoice_detail", pk=invoice.pk) invoice.mark_as_approved( entity_slug=dealer.entity.slug, user_model=dealer.entity.admin ) invoice.save() return redirect("invoice_detail", pk=invoice.pk) def invoice_create(request, pk): estimate = get_object_or_404(EstimateModel, pk=pk) dealer = get_user_type(request) entity = dealer.entity if request.method == "POST": form = forms.InvoiceModelCreateForm( request.POST, entity_slug=entity.slug, user_model=entity.admin ) if form.is_valid(): invoice = form.save(commit=False) ledger = entity.create_ledger(name=str(invoice.pk)) invoice.ledgar = ledger ledger.invoicemodel = invoice ledger.save() invoice.save() # unit_items = estimate.get_itemtxs_data()[0] # vat = models.VatRate.objects.filter(is_active=True).first() calculator = CarFinanceCalculator(estimate) finance_data = calculator.get_finance_data() # total = 0 # discount_amount = 0 # itemtxs = [] # for item in unit_items: # car = models.Car.objects.get(vin=item.item_model.name) # total = Decimal(car.finances.total) * Decimal(item.ce_quantity) # discount_amount = car.finances.discount_amount # grand_total = Decimal(total) - Decimal(discount_amount) # vat_amount = round(Decimal(grand_total) * Decimal(vat.rate), 2) # grand_total += Decimal(vat_amount) # unit_cost = grand_total / Decimal(item.ce_quantity) # itemtxs.append( # { # "item_number": item.item_model.item_number, # "unit_cost": unit_cost, # "unit_revenue": unit_cost, # "quantity": item.ce_quantity, # "total_amount": grand_total, # } # ) invoice_itemtxs = { i.get("item_number"): { "unit_cost": i.get("total_vat"), "quantity": i.get("quantity"), "total_amount": i.get("total_vat"), } for i in finance_data.get("cars") } invoice_itemtxs = invoice.migrate_itemtxs( itemtxs=invoice_itemtxs, commit=True, operation=InvoiceModel.ITEMIZE_APPEND, ) invoice.bind_estimate(estimate) invoice.mark_as_review() estimate.mark_as_completed() estimate.save() invoice.save() messages.success(request, "Invoice created successfully!") return redirect("invoice_detail", pk=invoice.pk) form = forms.InvoiceModelCreateForm( entity_slug=entity.slug, user_model=entity.admin ) form.initial.update( { "customer": estimate.customer, "cash_account": entity.get_default_coa_accounts().get(name="Cash"), "prepaid_account": entity.get_default_coa_accounts().get( name="Accounts Receivable" ), "unearned_account": entity.get_default_coa_accounts().get( name="Deferred Revenue" ), } ) context = { "form": form, "estimate": estimate, } return render(request, "sales/invoices/invoice_create.html", context) class InvoicePreviewView(LoginRequiredMixin, DetailView): model = InvoiceModel context_object_name = "invoice" template_name = "sales/invoices/invoice_preview.html" def get_context_data(self, **kwargs): invoice = kwargs.get("object") if invoice.get_itemtxs_data(): # data = get_financial_values(invoice) calculator = CarFinanceCalculator(invoice) finance_data = calculator.get_finance_data() kwargs["data"] = finance_data # kwargs["vat_amount"] = data["vat_amount"] # kwargs["total"] = data["grand_total"] # kwargs["discount_amount"] = data["discount_amount"] # kwargs["vat"] = data["vat"] # kwargs["car_and_item_info"] = data["car_and_item_info"] # kwargs["additional_services"] = data["additional_services"] return super().get_context_data(**kwargs) # payments def PaymentCreateView(request, pk): invoice = InvoiceModel.objects.filter(pk=pk).first() bill = BillModel.objects.filter(pk=pk).first() model = invoice if invoice else bill dealer = get_user_type(request) entity = dealer.entity form = forms.PaymentForm() if request.method == "POST": form = forms.PaymentForm(request.POST) if form.is_valid(): amount = form.cleaned_data.get("amount") invoice = form.cleaned_data.get("invoice") bill = form.cleaned_data.get("bill") payment_method = form.cleaned_data.get("payment_method") redirect_url = "invoice_detail" if invoice else "bill_detail" model = invoice if invoice else bill if not model.is_approved(): model.mark_as_approved(user_model=entity.admin) try: if invoice: set_invoice_payment(dealer, entity, invoice, amount, payment_method) elif bill: set_bill_payment(dealer, entity, bill, amount, payment_method) messages.success(request, "Payment created successfully!") return redirect(redirect_url, pk=model.pk) except Exception as e: messages.error(request, f"Error creating payment: {str(e)}") else: messages.error(request, f"Invalid form data: {str(form.errors)}") # return redirect(redirect_url, pk=model.pk) form = forms.PaymentForm() if model: form.initial["amount"] = model.amount_due - model.amount_paid if isinstance(model, InvoiceModel): form.initial["invoice"] = model form.fields["bill"].widget = HiddenInput() elif isinstance(model, BillModel): form.initial["bill"] = model form.fields["invoice"].widget = HiddenInput() return render( request, "sales/payments/payment_form.html", {"model": model, "form": form} ) def PaymentListView(request): dealer = get_user_type(request) entity = dealer.entity journals = JournalEntryModel.objects.filter(ledger__entity=entity).all() return render(request, "sales/payments/payment_list.html", {"journals": journals}) def PaymentDetailView(request, pk): journal = JournalEntryModel.objects.filter(pk=pk).first() transactions = ( TransactionModel.objects.filter(journal_entry=journal) .order_by("account__code") .all() ) return render( request, "sales/payments/payment_details.html", {"journal": journal, "transactions": transactions}, ) def payment_mark_as_paid(request, pk): invoice = get_object_or_404(InvoiceModel, pk=pk) if request.method == "POST": try: if invoice.amount_due == invoice.amount_paid: if not invoice.is_paid() and invoice.can_pay(): invoice.mark_as_paid( entity_slug=invoice.ledger.entity.slug, user_model=invoice.ledger.entity.admin, ) invoice.save() invoice.ledger.lock_journal_entries() invoice.ledger.post_journal_entries() invoice.ledger.post() invoice.ledger.save() messages.success(request, "Payment created successfully!") else: messages.error( request, "Invoice is not fully paid. Payment cannot be marked as paid.", ) except Exception as e: messages.error(request, f"Error: {str(e)}") return redirect("invoice_detail", pk=invoice.pk) # activity log class UserActivityLogListView(ListView): model = models.UserActivityLog template_name = "dealers/activity_log.html" context_object_name = "logs" paginate_by = 20 def get_queryset(self): queryset = super().get_queryset() if "user" in self.request.GET: queryset = queryset.filter(user__email=self.request.GET["user"]) return queryset # CRM RELATED VIEWS class LeadListView(ListView): model = models.Lead template_name = "crm/leads/lead_list.html" context_object_name = "leads" paginate_by = 10 def get_queryset(self): dealer = get_user_type(self.request) return models.Lead.objects.filter(dealer=dealer).all() class LeadDetailView(DetailView): model = models.Lead template_name = "crm/leads/lead_detail.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["notes"] = models.Notes.objects.filter( content_type__model="lead", object_id=self.object.id ) context["activities"] = models.Activity.objects.filter( content_type__model="lead", object_id=self.object.id ) context["status_history"] = models.LeadStatusHistory.objects.filter( lead=self.object ) return context class LeadCreateView(CreateView, SuccessMessageMixin, LoginRequiredMixin): model = models.Lead form_class = forms.LeadForm template_name = "crm/leads/lead_form.html" # success_message = "Lead created successfully!" success_url = reverse_lazy("lead_list") def form_valid(self, form): print("Form data:", form.cleaned_data) # Debug form data dealer = get_user_type(self.request) form.instance.dealer = dealer return super().form_valid(form) def get_car_models(request): make_id = request.GET.get("id_car_make") if make_id: car_models = models.CarModel.objects.filter(id_car_make=make_id).values( "id_car_model", "name", "arabic_name" ) return JsonResponse(list(car_models), safe=False) return JsonResponse([], safe=False) class LeadUpdateView(UpdateView): model = models.Lead form_class = forms.LeadForm template_name = "crm/leads/lead_form.html" success_url = reverse_lazy("lead_list") class LeadDeleteView(DeleteView): model = models.Lead template_name = "crm/leads/lead_confirm_delete.html" success_url = reverse_lazy("lead_list") def add_note_to_lead(request, pk): lead = get_object_or_404(models.Lead, pk=pk) if request.method == "POST": form = forms.NoteForm(request.POST) if form.is_valid(): note = form.save(commit=False) note.content_object = lead note.created_by = request.user note.save() return redirect("lead_detail", pk=pk) else: form = forms.NoteForm() return render(request, "crm/add_note.html", {"form": form, "lead": lead}) def add_activity_to_lead(request, pk): lead = get_object_or_404(models.Lead, pk=pk) if request.method == "POST": form = forms.ActivityForm(request.POST) if form.is_valid(): activity = form.save(commit=False) activity.content_object = lead activity.created_by = request.user activity.save() return redirect("lead_detail", pk=pk) else: form = forms.ActivityForm() return render(request, "crm/add_activity.html", {"form": form, "lead": lead}) class OpportunityCreateView(CreateView): model = models.Opportunity form_class = forms.OpportunityForm template_name = "crm/opportunities/opportunity_form.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) dealer = get_user_type(self.request) context["customer"] = models.Customer.objects.filter(dealer=dealer) context["cars"] = models.Car.objects.filter(dealer=dealer) return context def form_valid(self, form): dealer = get_user_type(self.request) form.instance.dealer = dealer # staff = dealer.staff print(dealer) # print(staff) # form.instance.staff = staff return super().form_valid(form) def get_success_url(self): return reverse_lazy("opportunity_detail", kwargs={"pk": self.object.pk}) class OpportunityUpdateView(UpdateView): model = models.Opportunity form_class = forms.OpportunityForm template_name = "crm/opportunities/opportunity_form.html" def get_success_url(self): return reverse_lazy("opportunity_detail", kwargs={"pk": self.object.pk}) class OpportunityDetailView(DetailView): model = models.Opportunity template_name = "crm/opportunities/opportunity_detail.html" context_object_name = "opportunity" class OpportunityListView(ListView): model = models.Opportunity template_name = "crm/opportunities/opportunity_list.html" context_object_name = "opportunities" paginate_by = 10 def get_queryset(self): dealer = get_user_type(self.request) return models.Opportunity.objects.filter(dealer=dealer).all() @login_required def delete_opportunity(request, pk): opportunity = get_object_or_404(models.Opportunity, pk=pk) opportunity.delete() messages.success(request, _("Opportunity deleted successfully.")) return redirect("opportunity_list") # class OpportunityLogsView(LoginRequiredMixin, ListView): # model = models.OpportunityLog # template_name = "crm/opportunity_logs.html" # context_object_name = "logs" # # def get_queryset(self): # opportunity_id = self.kwargs["pk"] # return models.OpportunityLog.objects.filter( # opportunity_id=opportunity_id # ).order_by("-created_at") # # def get_context_data(self, **kwargs): # context = super().get_context_data(**kwargs) # context["opportunity"] = models.Opportunity.objects.get(pk=self.kwargs["pk"]) # return context class NotificationListView(LoginRequiredMixin, ListView): model = models.Notification template_name = "crm/notifications_history.html" context_object_name = "notifications" paginate_by = 20 ordering = "-created" def get_queryset(self): return models.Notification.objects.filter(user=self.request.user) @login_required def mark_notification_as_read(request, pk): notification = get_object_or_404(models.Notification, pk=pk, user=request.user) notification.is_read = True notification.save() messages.success(request, _("Notification marked as read.")) return redirect("notifications_history") @login_required def fetch_notifications(request): notifications = models.Notification.objects.filter( user=request.user, is_read=False ).order_by("-created") # notifications_data = [ # { # "id": notification.id, # "message": notification.message, # "created": notification.created.strftime("%Y-%m-%d %H:%M:%S"), # } # for notification in notifications # ] # return JsonResponse({"notifications": notifications_data}) return render(request, "notifications.html", {"notifications_": notifications}) class ItemServiceCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): model = models.AdditionalServices form_class = forms.AdditionalServiceForm template_name = "items/service/service_create.html" success_url = reverse_lazy("item_service_list") success_message = _("Service created successfully.") context_object_name = "service" def form_valid(self, form): vat = models.VatRate.objects.get(is_active=True) form.instance.dealer = get_user_type(self.request.user.dealer) if form.instance.taxable: form.instance.price = (form.instance.price * vat.rate) + form.instance.price return super().form_valid(form) class ItemServiceUpdateView(LoginRequiredMixin, SuccessMessageMixin, UpdateView): model = models.AdditionalServices form_class = forms.AdditionalServiceForm template_name = "items/service/service_create.html" success_url = reverse_lazy("item_service_list") success_message = _("Service updated successfully.") context_object_name = "service" def form_valid(self, form): vat = models.VatRate.objects.get(is_active=True) form.instance.dealer = get_user_type(self.request.user.dealer) if form.instance.taxable: form.instance.price = (form.instance.price * vat.rate) + form.instance.price return super().form_valid(form) class ItemServiceListView(ListView): model = models.AdditionalServices template_name = "items/service/service_list.html" context_object_name = "services" paginate_by = 20 def get_queryset(self): dealer = get_user_type(self.request) return models.AdditionalServices.objects.filter(dealer=dealer).all() class ItemExpenseCreateView(CreateView): model = ItemModel form_class = ExpenseItemCreateForm template_name = "items/expenses/expense_create.html" success_url = reverse_lazy("item_expense_list") def get_form_kwargs(self): dealer = get_user_type(self.request) kwargs = super().get_form_kwargs() kwargs["entity_slug"] = dealer.entity.slug kwargs["user_model"] = dealer.entity.admin return kwargs def form_valid(self, form): dealer = get_user_type(self.request) form.instance.entity = dealer.entity return super().form_valid(form) class ItemExpenseUpdateView(UpdateView): model = ItemModel form_class = ExpenseItemUpdateForm template_name = "items/expenses/expense_update.html" success_url = reverse_lazy("item_expense_list") def get_form_kwargs(self): dealer = get_user_type(self.request) kwargs = super().get_form_kwargs() kwargs["entity_slug"] = dealer.entity.slug kwargs["user_model"] = dealer.entity.admin return kwargs def form_valid(self, form): dealer = get_user_type(self.request) form.instance.entity = dealer.entity return super().form_valid(form) class ItemExpenseListView(ListView): model = ItemModel template_name = "items/expenses/expenses_list.html" context_object_name = "expenses" paginate_by = 20 def get_queryset(self): dealer = get_user_type(self.request) return dealer.entity.get_items_expenses() class BillListView(ListView): model = BillModel template_name = "ledger/bills/bill_list.html" context_object_name = "bills" def get_queryset(self): dealer = get_user_type(self.request) qs = dealer.entity.get_bills() return qs class BillDetailView(LoginRequiredMixin, DetailView): model = BillModel template_name = "ledger/bills/bill_detail.html" context_object_name = "bill" def get_context_data(self, **kwargs): bill = kwargs.get("object") if bill.get_itemtxs_data(): txs = bill.get_itemtxs_data()[0] car_and_item_info = [ { "car": models.Car.objects.get(vin=x.item_model.name), "total": models.Car.objects.get( vin=x.item_model.name ).finances.cost_price * Decimal(x.quantity), "itemmodel": x, } for x in txs ] grand_total = sum( Decimal( models.Car.objects.get(vin=x.item_model.name).finances.cost_price ) * Decimal(x.quantity) for x in txs ) vat = models.VatRate.objects.filter(is_active=True).first() if vat: grand_total += round(Decimal(grand_total) * Decimal(vat.rate), 2) kwargs["car_and_item_info"] = car_and_item_info kwargs["grand_total"] = grand_total return super().get_context_data(**kwargs) class InReviewBillView(LoginRequiredMixin, UpdateView): model = BillModel form_class = InReviewBillModelUpdateForm template_name = "ledger/bills/bill_update_form.html" success_url = reverse_lazy("bill_list") success_message = _("Bill updated successfully.") context_object_name = "bill" def get_form_kwargs(self): kwargs = super().get_form_kwargs() dealer = get_user_type(self.request) kwargs["entity_model"] = dealer.entity kwargs["user_model"] = dealer.entity.admin return kwargs def get_success_url(self): return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]}) def form_valid(self, form): dealer = get_user_type(self.request) form.instance.entity = dealer.entity self.object.mark_as_review() return super().form_valid(form) class ApprovedBillModelView(LoginRequiredMixin, UpdateView): model = BillModel form_class = ApprovedBillModelUpdateForm template_name = "ledger/bills/bill_update_form.html" success_url = reverse_lazy("bill_list") success_message = _("Bill updated successfully.") context_object_name = "bill" def get_form_kwargs(self): kwargs = super().get_form_kwargs() dealer = get_user_type(self.request) kwargs["entity_model"] = dealer.entity kwargs["user_model"] = dealer.entity.admin return kwargs def get_success_url(self): return reverse_lazy("bill_detail", kwargs={"pk": self.kwargs["pk"]}) def form_valid(self, form): dealer = get_user_type(self.request) form.instance.entity = dealer.entity if not self.object.is_approved(): self.object.mark_as_approved(user_model=dealer.entity.admin) return super().form_valid(form) def bill_mark_as_approved(request, pk): bill = get_object_or_404(BillModel, pk=pk) if request.method == "POST": dealer = get_user_type(request) if bill.is_approved(): messages.error(request, _("Bill is already approved.")) return redirect("bill_detail", pk=bill.pk) bill.mark_as_approved(user_model=dealer.entity.admin) bill.save() messages.success(request, _("Bill marked as approved successfully.")) return redirect("bill_detail", pk=bill.pk) def bill_mark_as_paid(request, pk): bill = get_object_or_404(BillModel, pk=pk) if request.method == "POST": dealer = get_user_type(request) if bill.is_paid(): messages.error(request, _("Bill is already paid.")) return redirect("bill_detail", pk=bill.pk) if bill.amount_due == bill.amount_paid: bill.mark_as_paid(user_model=dealer.entity.admin) bill.save() bill.ledger.lock_journal_entries() bill.ledger.post_journal_entries() bill.ledger.post() bill.ledger.save() messages.success(request, _("Bill marked as paid successfully.")) else: messages.error(request, _("Amount paid is not equal to amount due.")) return redirect("bill_detail", pk=bill.pk) # def get_context_data(self, **kwargs): # dealer = get_user_type(self.request) # context = super().get_context_data(**kwargs) # context['entity_model'] = dealer.entity # context['user_model'] = dealer.entity.admin # return context # class BillCreateView(LoginRequiredMixin, SuccessMessageMixin, CreateView): # model = BillModel # form_class = BillModelCreateForm # template_name = "ledger/bills/bill_form.html" # success_url = reverse_lazy("bill_list") # success_message = _("Bill created successfully.") # def get_form_kwargs(self): # dealer = get_user_type(self.request) # kwargs = super().get_form_kwargs() # kwargs["entity_model"] = dealer.entity # return kwargs # def form_valid(self, form): # dealer = get_user_type(self.request) # form.instance.entity = dealer.entity # ledger = dealer.entity.create_ledger( # name=f"Bill for Vendor {form.instance.vendor.vendor_name}", posted=True # ) # form.instance.ledger = ledger # return super().form_valid(form) @login_required def bill_create(request): dealer = get_user_type(request) entity = dealer.entity if request.method == "POST": data = json.loads(request.body) vendor_id = data.get("vendor") terms = data.get("terms") vendor = entity.get_vendors().filter(pk=vendor_id).first() items = data.get("item", []) quantities = data.get("quantity", []) if not all([items, quantities]): return JsonResponse( {"status": "error", "message": "Items and Quantities are required"}, status=400, ) if isinstance(quantities, list): if "0" in quantities: return JsonResponse( {"status": "error", "message": "Quantity must be greater than zero"} ) else: if int(quantities) <= 0: return JsonResponse( {"status": "error", "message": "Quantity must be greater than zero"} ) bill = entity.create_bill(vendor_model=vendor, terms=terms) if isinstance(items, list): item_quantity_map = {} for item, quantity in zip(items, quantities): if item in item_quantity_map: item_quantity_map[item] += int(quantity) else: item_quantity_map[item] = int(quantity) item_list = list(item_quantity_map.keys()) quantity_list = list(item_quantity_map.values()) items_list = [ {"item_id": item_list[i], "quantity": quantity_list[i]} for i in range(len(item_list)) ] items_txs = [] for item in items_list: item_instance = ItemModel.objects.get(pk=item.get("item_id")) car = models.Car.objects.get(vin=item_instance.name) quantity = Decimal(item.get("quantity")) items_txs.append( { "item_number": item_instance.item_number, "quantity": quantity, "unit_cost": car.finances.cost_price, "total_amount": car.finances.cost_price * quantity, } ) bill_itemtxs = { item.get("item_number"): { "unit_cost": item.get("unit_cost"), "quantity": item.get("quantity"), "total_amount": item.get("total_amount"), } for item in items_txs } else: item = entity.get_items_all().filter(pk=items).first() instance = models.Car.objects.get(vin=item.name) bill_itemtxs = { item.item_number: { "unit_cost": instance.finances.cost_price, "quantity": Decimal(quantities), "total_amount": instance.finances.cost_price * Decimal(quantities), } } bill_itemtxs = bill.migrate_itemtxs( itemtxs=bill_itemtxs, commit=True, operation=BillModel.ITEMIZE_APPEND, ) url = reverse("bill_detail", kwargs={"pk": bill.pk}) return JsonResponse( { "status": "success", "message": "Bill created successfully!", "url": f"{url}", } ) form = forms.BillModelCreateForm(entity_model=entity) form.initial.update( { "cash_account": entity.get_default_coa_accounts().get(name="Cash"), "prepaid_account": entity.get_default_coa_accounts().get( name="Prepaid Expenses" ), "unearned_account": entity.get_default_coa_accounts().get( name="Accounts Payable" ), } ) car_list = models.Car.objects.filter( dealer=dealer, finances__selling_price__gt=0, status="available" ) context = { "form": form, "items": [ { "car": x, "product": entity.get_items_products().filter(name=x.vin).first(), } for x in car_list ], } return render(request, "ledger/bills/bill_form.html", context) def BillDeleteView(request, pk): bill = get_object_or_404(BillModel, pk=pk) bill.delete() return redirect("bill_list") class SubscriptionPlans(ListView): model = models.SubscriptionPlan template_name = "subscriptions/subscription_plan.html" context_object_name = "plans" # orders class OrderListView(ListView): model = models.SaleOrder template_name = "sales/orders/order_list.html" context_object_name = "orders" # email def send_email_view(request, pk): estimate = get_object_or_404(EstimateModel, pk=pk) if request.method == "POST": # if not estimate.can_review(): # messages.error(request, "Estimate is not ready for review") # return redirect("estimate_detail", pk=estimate.pk) if not estimate.get_itemtxs_data()[0]: messages.error(request, "Estimate has no items") return redirect("estimate_detail", pk=estimate.pk) send_email( "manager@tenhal.com", request.POST.get("to"), request.POST.get("subject"), request.POST.get("message"), ) estimate.mark_as_review() messages.success(request, "Email sent successfully!") return redirect("estimate_detail", pk=estimate.pk) link = reverse_lazy("estimate_preview", kwargs={"pk": estimate.pk}) msg = f""" السلام عليكم Dear {estimate.customer.customer_name}, أود أن أشارككم تقدير المشروع الذي ناقشناه. يرجى العثور على الوثيقة التفصيلية للمقترح المرفقة. I hope this email finds you well. I wanted to share with you the estimate for the project we discussed. Please find the detailed estimate document attached. يرجى مراجعة المقترح وإعلامي إذا كانت لديك أي أسئلة أو مخاوف. إذا كانت كل شيء يبدو جيدًا، يمكننا المضي قدمًا في المشروع. Please review the estimate and let me know if you have any questions or concerns. If everything looks good, we can proceed with the project. Estimate Link: {link} شكراً لاهتمامكم بهذا الأمر. Thank you for your attention to this matter. تحياتي, Best regards, [Your Name] [Your Position] [Your Company] [Your Contact Information] """ return render( request, "sales/estimates/estimate_send.html", {"estimate": estimate, "message": msg}, ) # errors def custom_page_not_found_view(request, exception): return render(request, "errors/404.html", {}) def custom_error_view(request, exception=None): return render(request, "errors/500.html", {}) def custom_permission_denied_view(request, exception=None): return render(request, "errors/403.html", {}) def custom_bad_request_view(request, exception=None): return render(request, "errors/400.html", {}) # from django_ledger.io.io_core import get_localdate # from django_ledger.views.mixins import (DjangoLedgerSecurityMixIn) # from django.views.generic import RedirectView from django_ledger.views.financial_statement import ( FiscalYearBalanceSheetView, BaseIncomeStatementRedirectView, FiscalYearIncomeStatementView, BaseCashFlowStatementRedirectView, FiscalYearCashFlowStatementView, ) from django_ledger.views.entity import EntityModelDetailBaseView,EntityModelDetailHandlerView from django.views.generic import DetailView, RedirectView from django_ledger.io.io_core import get_localdate from django_ledger.models import EntityModel, EntityUnitModel from django_ledger.views.mixins import ( QuarterlyReportMixIn, YearlyReportMixIn, MonthlyReportMixIn, DateReportMixIn, DjangoLedgerSecurityMixIn, EntityUnitMixIn, BaseDateNavigationUrlMixIn, PDFReportMixIn, ) # BALANCE SHEET ----------- class BaseBalanceSheetRedirectView(DjangoLedgerSecurityMixIn, RedirectView): def get_redirect_url(self, *args, **kwargs): year = get_localdate().year return reverse( "entity-bs-year", kwargs={"entity_slug": self.kwargs["entity_slug"], "year": year}, ) class FiscalYearBalanceSheetViewBase(FiscalYearBalanceSheetView): template_name = "ledger/reports/balance_sheet.html" class QuarterlyBalanceSheetView(FiscalYearBalanceSheetViewBase, QuarterlyReportMixIn): """ Quarter Balance Sheet View. """ class MonthlyBalanceSheetView(FiscalYearBalanceSheetViewBase, MonthlyReportMixIn): """ Monthly Balance Sheet View. """ class DateBalanceSheetView(FiscalYearBalanceSheetViewBase, DateReportMixIn): """ Date Balance Sheet View. """ # Income Statement ----------- class BaseIncomeStatementRedirectViewBase(BaseIncomeStatementRedirectView): def get_redirect_url(self, *args, **kwargs): year = get_localdate().year dealer = get_user_type(self.request) return reverse( "entity-ic-year", kwargs={"entity_slug": dealer.entity.slug, "year": year} ) class FiscalYearIncomeStatementViewBase(FiscalYearIncomeStatementView): template_name = "ledger/reports/income_statement.html" class QuarterlyIncomeStatementView( FiscalYearIncomeStatementViewBase, QuarterlyReportMixIn ): """ Quarter Income Statement View. """ class MonthlyIncomeStatementView(FiscalYearIncomeStatementViewBase, MonthlyReportMixIn): """ Monthly Income Statement View. """ class DateModelIncomeStatementView(FiscalYearIncomeStatementViewBase, DateReportMixIn): """ Date Income Statement View. """ # Cash Flow ----------- class BaseCashFlowStatementRedirectViewBase(BaseCashFlowStatementRedirectView): def get_redirect_url(self, *args, **kwargs): year = get_localdate().year dealer = get_user_type(self.request) return reverse( "entity-cf-year", kwargs={"entity_slug": dealer.entity.slug, "year": year} ) class FiscalYearCashFlowStatementViewBase(FiscalYearCashFlowStatementView): template_name = "ledger/reports/cash_flow_statement.html" class QuarterlyCashFlowStatementView( FiscalYearCashFlowStatementViewBase, QuarterlyReportMixIn ): """ Quarter Cash Flow Statement View. """ class MonthlyCashFlowStatementView( FiscalYearCashFlowStatementViewBase, MonthlyReportMixIn ): """ Monthly Cash Flow Statement View. """ class DateCashFlowStatementView(FiscalYearCashFlowStatementViewBase, DateReportMixIn): """ Date Cash Flow Statement View. """ # Dashboard class EntityModelDetailHandlerViewBase(EntityModelDetailHandlerView): def get_redirect_url(self, *args, **kwargs): loc_date = get_localdate() dealer = get_user_type(self.request) unit_slug = self.get_unit_slug() if unit_slug: return reverse('unit-dashboard-month', kwargs={ 'entity_slug': dealer.entity.slug, 'unit_slug': unit_slug, 'year': loc_date.year, 'month': loc_date.month, }) return reverse('entity-dashboard-month', kwargs={ 'entity_slug': dealer.entity.slug, 'year': loc_date.year, 'month': loc_date.month, }) class EntityModelDetailBaseViewBase(EntityModelDetailBaseView): template_name = "ledger/reports/dashboard.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) dealer = get_user_type(self.request) entity_model: EntityModel = dealer.entity context['page_title'] = entity_model.name context['header_title'] = entity_model.name context['header_subtitle'] = _('Dashboard') context['header_subtitle_icon'] = 'mdi:monitor-dashboard' unit_slug = context.get('unit_slug', self.get_unit_slug()) KWARGS = dict(entity_slug=self.kwargs['entity_slug']) if unit_slug: KWARGS['unit_slug'] = unit_slug url_pointer = 'entity' if not unit_slug else 'unit' context['pnl_chart_id'] = f'djl-entity-pnl-chart-{randint(10000, 99999)}' context['pnl_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-pnl', kwargs=KWARGS) context['payables_chart_id'] = f'djl-entity-payables-chart-{randint(10000, 99999)}' context['payables_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-net-payables', kwargs=KWARGS) context['receivables_chart_id'] = f'djl-entity-receivables-chart-{randint(10000, 99999)}' context['receivables_chart_endpoint'] = reverse(f'django_ledger:{url_pointer}-json-net-receivables', kwargs=KWARGS) return context class FiscalYearEntityModelDashboardView(EntityModelDetailBaseViewBase): """ Entity Fiscal Year Dashboard View. """ class QuarterlyEntityDashboardView(FiscalYearEntityModelDashboardView, QuarterlyReportMixIn): """ Entity Quarterly Dashboard View. """ class MonthlyEntityDashboardView(FiscalYearEntityModelDashboardView, MonthlyReportMixIn): """ Monthly Entity Dashboard View. """ class DateEntityDashboardView(FiscalYearEntityModelDashboardView, DateReportMixIn): """ Date-specific Entity Dashboard View. """ class PayableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): http_method_names = ['get'] def get(self, request, *args, **kwargs): if request.user.is_authenticated: dealer = get_user_type(request) bill_qs = BillModel.objects.for_entity( entity_slug=dealer.entity.slug, user_model=dealer.entity.admin, ).unpaid() # todo: implement this... # unit_slug = self.get_unit_slug() # if unit_slug: # bill_qs.filter(ledger__journal_entry__entity_unit__slug__exact=unit_slug) net_summary = accruable_net_summary(bill_qs) net_payables = { 'net_payable_data': net_summary } return JsonResponse({ 'results': net_payables }) return JsonResponse({ 'message': 'Unauthorized' }, status=401) class ReceivableNetAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): http_method_names = ['get'] def get(self, request, *args, **kwargs): if request.user.is_authenticated: dealer = get_user_type(request) invoice_qs = InvoiceModel.objects.for_entity( entity_slug=dealer.entity.slug, user_model=dealer.entity.admin, ).unpaid() # todo: implement this... # unit_slug = self.get_unit_slug() # if unit_slug: # invoice_qs.filter(ledger__journal_entry__entity_unit__slug__exact=unit_slug) net_summary = accruable_net_summary(invoice_qs) net_receivable = { 'net_receivable_data': net_summary } return JsonResponse({ 'results': net_receivable }) return JsonResponse({ 'message': 'Unauthorized' }, status=401) class PnLAPIView(DjangoLedgerSecurityMixIn, EntityUnitMixIn, View): http_method_names = ['get'] def get(self, request, *args, **kwargs): if request.user.is_authenticated: dealer = get_user_type(request) entity = EntityModel.objects.for_user( user_model=dealer.entity.admin).get( slug__exact=dealer.entity.slug) unit_slug = self.get_unit_slug() io_digest = entity.digest( user_model=self.request.user, unit_slug=unit_slug, equity_only=True, signs=False, by_period=True, process_groups=True, from_date=self.request.GET.get('fromDate'), to_date=self.request.GET.get('toDate'), # todo: For PnL to display proper period values must not use closing entries. use_closing_entries=False ) io_data = io_digest.get_io_data() group_balance_by_period = io_data['group_balance_by_period'] group_balance_by_period = dict(sorted((k, v) for k, v in group_balance_by_period.items())) entity_data = { f'{month_name[k[1]]} {k[0]}': {d: float(f) for d, f in v.items()} for k, v in group_balance_by_period.items()} entity_pnl = { 'entity_slug': entity.slug, 'entity_name': entity.name, 'pnl_data': entity_data } return JsonResponse({ 'results': entity_pnl }) return JsonResponse({ 'message': 'Unauthorized' }, status=401)