From c880eb88b6bf24e74b634720e32dba6d046c1d99 Mon Sep 17 00:00:00 2001 From: Faheedkhan Date: Wed, 27 Aug 2025 13:06:32 +0300 Subject: [PATCH 1/6] before pull --- templates/plans/billing_info_create_or_update.html | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/templates/plans/billing_info_create_or_update.html b/templates/plans/billing_info_create_or_update.html index 64e6ca47..bc6521d8 100644 --- a/templates/plans/billing_info_create_or_update.html +++ b/templates/plans/billing_info_create_or_update.html @@ -4,6 +4,7 @@ {% trans 'Billing Information' %} {% endblock %} {% block content %} +
@@ -24,12 +25,6 @@ {{ _("Save") }} - {% if object %} - - - {{ _("Delete") }} - - {% endif %}
From e9f301ee20e545844d7aa66e7e72ae61b2898c6e Mon Sep 17 00:00:00 2001 From: Faheedkhan Date: Wed, 27 Aug 2025 14:11:33 +0300 Subject: [PATCH 2/6] car list filter fix --- templates/inventory/car_detail.html | 370 ++++++++---------- templates/inventory/car_list_view.html | 12 +- .../plans/billing_info_create_or_update.html | 89 ++--- 3 files changed, 211 insertions(+), 260 deletions(-) diff --git a/templates/inventory/car_detail.html b/templates/inventory/car_detail.html index 313aca0a..4298c875 100644 --- a/templates/inventory/car_detail.html +++ b/templates/inventory/car_detail.html @@ -77,15 +77,182 @@
-
-
+
+

{% trans 'Car Details' %}

+
{{ car.vin }}
-
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {% if car.vendor %} + + + + + {% endif %} + + + + + + + + + {% if car.custom_cards %} + + + + + + + + + {% else %} + + + + + {% endif %} + {% if car.registrations %} + + + + + + + + + {% else %} + + + + + {% endif %} + + + + +
{% trans "VIN" %}{{ car.vin }}
{% trans "year"|capfirst %}{{ car.year }}
{% trans "make"|capfirst %}{{ car.id_car_make.get_local_name }}
{% trans "model"|capfirst %}{{ car.id_car_model.get_local_name }}
{% trans "series"|capfirst %}{{ car.id_car_serie.name }}
{% trans "trim"|capfirst %}{{ car.id_car_trim.name }}
{% trans "Status"|capfirst %}{{ car.get_status_display }}
{% trans "Stock Type"|capfirst %}{{ car.get_stock_type_display }}
{% trans "Mileage"|capfirst %}{{ car.mileage }}
{% trans "Receiving Date"|capfirst %}{{ car.receiving_date|timesince }}
{% trans "Vendor"|capfirst %}{{ car.vendor.name }}
{% trans "Remarks"|capfirst %}{{ car.remarks }}
{% trans 'specifications'|capfirst %} + +
{% trans "Custom Number"|capfirst %}{{ car.custom_cards.custom_number }}
{% trans "Custom Date"|capfirst %}{{ car.custom_cards.custom_date|date }}
{% trans "Custom Card" %} + {% if perms.inventory.add_customcard %} + + {% endif %} +
{% trans "Registration"|capfirst %} + {{ car.registrations.plate_number }} | {{ car.registrations.text1 }} {{ car.registrations.text2 }} {{ car.registrations.text3 }} +
{% trans "Registration Date"|capfirst %}{{ car.registrations.registration_date|date }}
{% trans "Registration" %} + {% if perms.inventory.add_carregistration %} + + {% endif %} +
{% trans 'Location'|capfirst %} + {% if car.marked_price and not car.get_transfer %} + {% if car.location %} + {% if car.location.is_owner_showroom %} + {% trans 'Our Showroom' %} + {% else %} + {{ car.location.showroom.get_local_name }} + {% endif %} + {% if perms.inventory.add_cartransfer %} + + {% trans "transfer"|capfirst %} + + {% endif %} + {% else %} + {% trans "No location available." %} + {% if perms.inventory.add_carlocation %} + {% trans "Add" %} + {% endif %} + {% endif %} + {% endif %} +
+
+
+ {% if not car.get_transfer %} + {% if perms.inventory.change_car %} + {% trans "Edit" %} + + + + {% trans "Sell to another dealer"|capfirst %} + + {% endif %} + {% else %} + {% trans "Cannot Edit, Car in Transfer." %} + {% endif %} +
+
+
+
+
+
+

{% trans 'Financial Details' %}

@@ -100,34 +267,7 @@ {% trans "Marked Price"|capfirst %} {{ car.marked_price|floatformat:2 }} - {% comment %} - {% trans "Selling Price"|capfirst %} - {{ car.finances.selling_price|floatformat:2 }} - - - {% trans "Discount Amount"|capfirst %} - {{ car.finances.discount_amount|floatformat:2 }} - - - {% trans "Additional Fee"|capfirst %} - - - {% if car.finances.additional_services.first.pk %} - {% for service in car.finances.additional_services.all %} - - {{ service.name }} - {{ service.price_|floatformat:2 }} - - {% endfor %} - {% endif %} - - {% trans "VAT Amount"|capfirst %} - {{ car.finances.vat_amount|floatformat:2 }} - - - {% trans "Total"|capfirst %} - {{ car.finances.total_vat|floatformat:2 }} - {% endcomment %} + {% if not car.get_transfer %} @@ -327,173 +467,7 @@
{% endif %}
-
-
-

{% trans 'Car Details' %}

-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {% if car.vendor %} - - - - - {% endif %} - - - - - - - - - {% if car.custom_cards %} - - - - - - - - - {% else %} - - - - - {% endif %} - {% if car.registrations %} - - - - - - - - - {% else %} - - - - - {% endif %} - - - - -
{% trans "VIN" %}{{ car.vin }}
{% trans "year"|capfirst %}{{ car.year }}
{% trans "make"|capfirst %}{{ car.id_car_make.get_local_name }}
{% trans "model"|capfirst %}{{ car.id_car_model.get_local_name }}
{% trans "series"|capfirst %}{{ car.id_car_serie.name }}
{% trans "trim"|capfirst %}{{ car.id_car_trim.name }}
{% trans "Status"|capfirst %}{{ car.get_status_display }}
{% trans "Stock Type"|capfirst %}{{ car.get_stock_type_display }}
{% trans "Mileage"|capfirst %}{{ car.mileage }}
{% trans "Receiving Date"|capfirst %}{{ car.receiving_date|timesince }}
{% trans "Vendor"|capfirst %}{{ car.vendor.name }}
{% trans "Remarks"|capfirst %}{{ car.remarks }}
{% trans 'specifications'|capfirst %} - -
{% trans "Custom Number"|capfirst %}{{ car.custom_cards.custom_number }}
{% trans "Custom Date"|capfirst %}{{ car.custom_cards.custom_date|date }}
{% trans "Custom Card" %} - {% if perms.inventory.add_customcard %} - - {% endif %} -
{% trans "Registration"|capfirst %} - {{ car.registrations.plate_number }} | {{ car.registrations.text1 }} {{ car.registrations.text2 }} {{ car.registrations.text3 }} -
{% trans "Registration Date"|capfirst %}{{ car.registrations.registration_date|date }}
{% trans "Registration" %} - {% if perms.inventory.add_carregistration %} - - {% endif %} -
{% trans 'Location'|capfirst %} - {% if car.marked_price and not car.get_transfer %} - {% if car.location %} - {% if car.location.is_owner_showroom %} - {% trans 'Our Showroom' %} - {% else %} - {{ car.location.showroom.get_local_name }} - {% endif %} - {% if perms.inventory.add_cartransfer %} - - {% trans "transfer"|capfirst %} - - {% endif %} - {% else %} - {% trans "No location available." %} - {% if perms.inventory.add_carlocation %} - {% trans "Add" %} - {% endif %} - {% endif %} - {% endif %} -
-
-
- {% if not car.get_transfer %} - {% if perms.inventory.change_car %} - {% trans "Edit" %} - - - - {% trans "Sell to another dealer"|capfirst %} - - {% endif %} - {% else %} - {% trans "Cannot Edit, Car in Transfer." %} - {% endif %} -
-
-
-
+
{% if car.status == 'sold' %} {{ _("Inventory Ready") }} {% if car.ready %} - Ready + {% trans "Ready" %} {{ _("Yes") }} {% else %} - Not Ready + {% trans "Not Ready" %} {{ _("No") }} {% endif %} @@ -306,3 +306,11 @@ {% include "empty-illustration-page.html" with value="car" url=create_car_url %} {% endif %} {% endblock %} +{% block customJS%} + +{% endblock %} \ No newline at end of file diff --git a/templates/plans/billing_info_create_or_update.html b/templates/plans/billing_info_create_or_update.html index 92caf5bb..a63bd0c1 100644 --- a/templates/plans/billing_info_create_or_update.html +++ b/templates/plans/billing_info_create_or_update.html @@ -1,68 +1,37 @@ {% extends 'base.html' %} {% load i18n crispy_forms_filters %} {% block title %} - {% trans 'Billing Information' %} + {% trans 'Billing Information' %} {% endblock %} {% block content %} - {% comment %}
-
-
- -

{% trans "Provide billing data"|upper %}

- {% csrf_token %} - {{ form|crispy }} - - - {% if object %} - {{ _("Delete") }} - {% endif %} - -
-
-
{% endcomment %} - -
-
-
-
-

- {% trans "Provide billing data"|upper %} -

- {% trans 'Billing Information' %} - {% endblock %} - {% block content %} -
-
-
-
-

- {% trans "Provide billing data"|upper %} - -

-
-
- {% endblock %} +
+ +
+
+
+
+{% endblock %} \ No newline at end of file From 36f279a386343966fb9c9e9afd7525832fb8ec4b Mon Sep 17 00:00:00 2001 From: Faheedkhan Date: Wed, 27 Aug 2025 18:40:10 +0300 Subject: [PATCH 3/6] help center in the header as link --- inventory/models.py | 3 +- inventory/urls.py | 167 +- inventory/views.py | 1615 ++++++++----------- templates/base.html | 2 +- templates/dashboards/chart.html | 2 +- templates/dashboards/general_dashboard.html | 10 +- templates/header.html | 8 +- templates/index.html | 11 +- templates/items/expenses/expenses_list.html | 1 + 9 files changed, 751 insertions(+), 1068 deletions(-) diff --git a/inventory/models.py b/inventory/models.py index fd499f47..8f3af378 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -762,7 +762,8 @@ class Car(Base): make = self.id_car_make.name if self.id_car_make else "Unknown Make" model = self.id_car_model.name if self.id_car_model else "Unknown Model" trim = self.id_car_trim.name if self.id_car_trim else "Unknown Trim" - return f"{self.year} - {make} - {model} - {trim}" + vin=self.vin if self.vin else None + return f"{self.year} - {make} - {model} - {trim}-{vin}" @property def product(self): diff --git a/inventory/urls.py b/inventory/urls.py index 72e48500..028098d7 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -40,18 +40,13 @@ urlpatterns = [ views.assign_car_makes, name="assign_car_makes", ), - # dashboards for manager, dealer, inventory and accounatant - path( - "dashboards//general/", - views.general_dashboard, - name="general_dashboard", - ), - # dashboard for sales - path( - "dashboards//sales/", - views.sales_dashboard, - name="sales_dashboard", - ), + + + #dashboards for manager, dealer, inventory and accounatant + path("dashboards//general/", views.general_dashboard,name="general_dashboard"), + #dashboard for sales + path("dashboards//sales/", views.sales_dashboard, name="sales_dashboard"), + path( "/cars/aging-inventory/list", views.aging_inventory_list_view, @@ -782,11 +777,7 @@ urlpatterns = [ views.EstimateDetailView.as_view(), name="estimate_detail", ), - path( - "/sales/estimates/print//", - views.EstimatePrintView.as_view(), - name="estimate_print", - ), + path('/sales/estimates/print//', views.EstimatePrintView.as_view(), name='estimate_print'), path( "/sales/estimates/create/", views.create_estimate, @@ -943,6 +934,7 @@ urlpatterns = [ views.ItemServiceUpdateView.as_view(), name="item_service_update", ), + # Expanese path( "/items/expeneses/", @@ -1101,47 +1093,32 @@ urlpatterns = [ name="entity-ic-date", ), # Chart of Accounts... - path( - "/chart-of-accounts//list/", + path('/chart-of-accounts//list/', views.ChartOfAccountModelListView.as_view(), - name="coa-list", - ), - path( - "/chart-of-accounts//list/inactive/", - views.ChartOfAccountModelListView.as_view(inactive=True), - name="coa-list-inactive", - ), - path( - "//create/", - views.ChartOfAccountModelCreateView.as_view(), - name="coa-create", - ), - path( - "//detail//", - views.ChartOfAccountModelListView.as_view(), - name="coa-detail", - ), - path( - "//update//", - views.ChartOfAccountModelUpdateView.as_view(), - name="coa-update", - ), + name='coa-list'), + path('/chart-of-accounts//list/inactive/', + views.ChartOfAccountModelListView.as_view(inactive=True), + name='coa-list-inactive'), + path('//create/', + views.ChartOfAccountModelCreateView.as_view(), + name='coa-create'), + path('//detail//', + views.ChartOfAccountModelListView.as_view(), + name='coa-detail'), + path('//update//', + views.ChartOfAccountModelUpdateView.as_view(), + name='coa-update'), + # ACTIONS.... - path( - "//action//mark-as-default/", - views.CharOfAccountModelActionView.as_view(action_name="mark_as_default"), - name="coa-action-mark-as-default", - ), - path( - "//action//mark-as-active/", - views.CharOfAccountModelActionView.as_view(action_name="mark_as_active"), - name="coa-action-mark-as-active", - ), - path( - "//action//mark-as-inactive/", - views.CharOfAccountModelActionView.as_view(action_name="mark_as_inactive"), - name="coa-action-mark-as-inactive", - ), + path('//action//mark-as-default/', + views.CharOfAccountModelActionView.as_view(action_name='mark_as_default'), + name='coa-action-mark-as-default'), + path('//action//mark-as-active/', + views.CharOfAccountModelActionView.as_view(action_name='mark_as_active'), + name='coa-action-mark-as-active'), + path('//action//mark-as-inactive/', + views.CharOfAccountModelActionView.as_view(action_name='mark_as_inactive'), + name='coa-action-mark-as-inactive'), # CASH FLOW STATEMENTS... # Entities... path( @@ -1317,74 +1294,40 @@ urlpatterns = [ views.PurchaseOrderMarkAsVoidView.as_view(), name="po-action-mark-as-void", ), + # reports - path( + path( "/purchase-report/", views.purchase_report_view, name="po-report", ), - path( - "purchase-report//csv/", - views.purchase_report_csv_export, - name="purchase-report-csv-export", - ), - path( + path('purchase-report//csv/', views.purchase_report_csv_export, name='purchase-report-csv-export'), + + path( "/car-sale-report/", views.car_sale_report_view, name="car-sale-report", ), - path( - "car-sale-report//csv/", - views.car_sale_report_csv_export, - name="car-sale-report-csv-export", - ), - path("feature/recall/", views.RecallListView.as_view(), name="recall_list"), - path("feature/recall/filter/", views.RecallFilterView, name="recall_filter"), - path( - "feature/recall//view/", - views.RecallDetailView.as_view(), - name="recall_detail", - ), - path( - "feature/recall/create/", views.RecallCreateView.as_view(), name="recall_create" - ), - path( - "feature/recall/success/", - views.RecallSuccessView.as_view(), - name="recall_success", - ), - path( - "/schedules/calendar/", - views.schedule_calendar, - name="schedule_calendar", - ), + path('car-sale-report//csv/', views.car_sale_report_csv_export, name='car-sale-report-csv-export'), + + path('feature/recall/', views.RecallListView.as_view(), name='recall_list'), + path('feature/recall/filter/', views.RecallFilterView, name='recall_filter'), + path('feature/recall//view/', views.RecallDetailView.as_view(), name='recall_detail'), + path('feature/recall/create/', views.RecallCreateView.as_view(), name='recall_create'), + path('feature/recall/success/', views.RecallSuccessView.as_view(), name='recall_success'), + + path('/schedules/calendar/', views.schedule_calendar, name='schedule_calendar'), + # staff profile - path( - "/staff/detail/", - views.StaffDetailView.as_view(), - name="staff_detail", - ), + path('/staff/detail/', views.StaffDetailView.as_view(), name='staff_detail'), # tickets - path("help_center/view/", views.help_center, name="help_center"), - path( - "/help_center/tickets/", views.ticket_list, name="ticket_list" - ), - path( - "help_center/tickets//create/", - views.create_ticket, - name="create_ticket", - ), - path( - "/help_center/tickets//", - views.ticket_detail, - name="ticket_detail", - ), - path( - "help_center/tickets//update/", - views.ticket_update, - name="ticket_update", - ), + path('help_center/view/', views.help_center, name='help_center'), + path('/help_center/tickets/', views.ticket_list, name='ticket_list'), + path('help_center/tickets//create/', views.create_ticket, name='create_ticket'), + path('/help_center/tickets//', views.ticket_detail, name='ticket_detail'), + path('help_center/tickets//update/', views.ticket_update, name='ticket_update'), # path('help_center/tickets//ticket_mark_resolved/', views.ticket_mark_resolved, name='ticket_mark_resolved'), + ] handler404 = "inventory.views.custom_page_not_found_view" diff --git a/inventory/views.py b/inventory/views.py index cb935c33..90d0efe2 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -17,7 +17,7 @@ from random import randint from decimal import Decimal from io import TextIOWrapper from django.apps import apps -from datetime import datetime, timedelta, date +from datetime import datetime, timedelta,date from calendar import month_name from pyzbar.pyzbar import decode from urllib.parse import urlparse, urlunparse @@ -35,7 +35,6 @@ from django.core.exceptions import PermissionDenied from django.contrib.contenttypes.models import ContentType from django.views.decorators.http import require_POST from django.template.loader import render_to_string - # Django from django.db.models import Q from django.conf import settings @@ -108,7 +107,7 @@ from django_ledger.forms.bank_account import ( BankAccountUpdateForm, ) from django_ledger.views.chart_of_accounts import ( - ChartOfAccountModelListView as ChartOfAccountModelListViewBase, + ChartOfAccountModelListView as ChartOfAccountModelListViewBase ) from django_ledger.views.bill import ( BillModelCreateView, @@ -174,7 +173,7 @@ from django_ledger.models import ( BillModel, LedgerModel, PurchaseOrderModel, - ChartOfAccountModel, + ChartOfAccountModel ) from django_ledger.views.financial_statement import ( FiscalYearBalanceSheetView, @@ -289,10 +288,8 @@ def switch_language(request): logger.warning(f"Invalid language code: {language}") return redirect("/") - def dealer_signup(request): from django_q.tasks import async_task - """ Handles the dealer signup wizard process, including forms validation, user and group creation, permissions assignment, and dealer data storage. This view supports GET @@ -365,7 +362,6 @@ def dealer_signup(request): "account/signup-wizard.html", ) - class HomeView(LoginRequiredMixin, TemplateView): """ HomeView class responsible for rendering the home page. @@ -401,26 +397,25 @@ class TestView(TemplateView): template_name = "inventory/cars_list_api.html" - @login_required -def general_dashboard(request, dealer_slug): +def general_dashboard(request,dealer_slug): """ Renders the dealer dashboard with key performance indicators and chart data. """ - dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - vat = models.VatRate.objects.filter(dealer=dealer, is_active=True).first() - VAT_RATE = vat.rate + dealer = get_object_or_404(models.Dealer,slug=dealer_slug) + vat = models.VatRate.objects.filter(dealer=dealer,is_active=True).first() + VAT_RATE=vat.rate today_local = timezone.localdate() # ---------------------------------------------------- # 1. Date Filtering # ---------------------------------------------------- - start_date_str = request.GET.get("start_date") - end_date_str = request.GET.get("end_date") + start_date_str = request.GET.get('start_date') + end_date_str = request.GET.get('end_date') if start_date_str and end_date_str: - start_date = timezone.datetime.strptime(start_date_str, "%Y-%m-%d").date() - end_date = timezone.datetime.strptime(end_date_str, "%Y-%m-%d").date() + start_date = timezone.datetime.strptime(start_date_str, '%Y-%m-%d').date() + end_date = timezone.datetime.strptime(end_date_str, '%Y-%m-%d').date() else: start_date = today_local - timedelta(days=30) end_date = today_local @@ -428,149 +423,104 @@ def general_dashboard(request, dealer_slug): # ---------------------------------------------------- # 2. Inventory KPIs # ---------------------------------------------------- - active_cars = models.Car.objects.filter(dealer=dealer).exclude(status="sold") + active_cars = models.Car.objects.filter(dealer=dealer).exclude(status='sold') total_cars_in_inventory = active_cars.count() - total_inventory_value = active_cars.aggregate(total=Sum("cost_price"))["total"] or 0 - new_cars_qs = active_cars.filter(stock_type="new") + total_inventory_value = active_cars.aggregate(total=Sum('cost_price'))['total'] or 0 + new_cars_qs = active_cars.filter(stock_type='new') total_new_cars_in_inventory = new_cars_qs.count() - new_car_value = new_cars_qs.aggregate(total=Sum("cost_price"))["total"] or 0 - used_cars_qs = active_cars.filter(stock_type="used") + new_car_value = new_cars_qs.aggregate(total=Sum('cost_price'))['total'] or 0 + used_cars_qs = active_cars.filter(stock_type='used') total_used_cars_in_inventory = used_cars_qs.count() - used_car_value = used_cars_qs.aggregate(total=Sum("cost_price"))["total"] or 0 + used_car_value = used_cars_qs.aggregate(total=Sum('cost_price'))['total'] or 0 aging_threshold_days = 60 - aging_inventory_count = active_cars.filter( - receiving_date__date__lte=today_local - timedelta(days=aging_threshold_days) - ).count() + aging_inventory_count = active_cars.filter(receiving_date__date__lte=today_local - timedelta(days=aging_threshold_days)).count() # ---------------------------------------------------- # 3. Sales KPIs (filtered by date) # ---------------------------------------------------- cars_sold_filtered = models.Car.objects.filter( dealer=dealer, - status="sold", + status='sold', sold_date__date__gte=start_date, - sold_date__date__lte=end_date, + sold_date__date__lte=end_date ) # General sales KPIs total_cars_sold = cars_sold_filtered.count() - total_cost_of_cars_sold = ( - cars_sold_filtered.aggregate(total=Sum("cost_price"))["total"] or 0 - ) - total_revenue_from_cars = ( - cars_sold_filtered.aggregate( - total=Sum(F("marked_price") - F("discount_amount")) - )["total"] - or 0 - ) + total_cost_of_cars_sold = cars_sold_filtered.aggregate(total=Sum('cost_price'))['total'] or 0 + total_revenue_from_cars = cars_sold_filtered.aggregate( + total=Sum(F('marked_price') - F('discount_amount')) + )['total'] or 0 - total_vat_collected_from_cars = ( - cars_sold_filtered.annotate( - final_price=F("marked_price") - F("discount_amount") - ).aggregate(total=Sum(F("final_price") * VAT_RATE))["total"] - or 0 - ) + total_vat_collected_from_cars = cars_sold_filtered.annotate( + final_price=F('marked_price') - F('discount_amount')).aggregate( + total=Sum(F('final_price') * VAT_RATE))['total'] or 0 net_profit_from_cars = total_revenue_from_cars - total_cost_of_cars_sold - total_discount = ( - cars_sold_filtered.aggregate(total=Sum("discount_amount"))["total"] or 0 - ) + total_discount = cars_sold_filtered.aggregate(total=Sum('discount_amount'))['total'] or 0 # Sales breakdown by type - new_cars_sold = cars_sold_filtered.filter(stock_type="new") + new_cars_sold = cars_sold_filtered.filter(stock_type='new') total_new_cars_sold = new_cars_sold.count() - total_cost_of_new_cars_sold = ( - new_cars_sold.aggregate(total=Sum("cost_price"))["total"] or 0 - ) + total_cost_of_new_cars_sold = new_cars_sold.aggregate(total=Sum('cost_price'))['total'] or 0 # total_revenue_from_new_cars=sum([ car.final_price for car in new_cars_sold]) - total_revenue_from_new_cars = ( - new_cars_sold.aggregate(total=Sum(F("marked_price") - F("discount_amount")))[ - "total" - ] - or 0 - ) + total_revenue_from_new_cars = new_cars_sold.aggregate( + total=Sum(F('marked_price') - F('discount_amount')) + )['total'] or 0 - total_vat_collected_from_new_cars = ( - new_cars_sold.annotate( - final_price=F("marked_price") - F("discount_amount") - ).aggregate(total=Sum(F("final_price") * VAT_RATE))["total"] - or 0 - ) + total_vat_collected_from_new_cars = new_cars_sold.annotate( + final_price=F('marked_price') - F('discount_amount')).aggregate( + total=Sum(F('final_price') * VAT_RATE))['total'] or 0 net_profit_from_new_cars = total_revenue_from_new_cars - total_cost_of_new_cars_sold - used_cars_sold = cars_sold_filtered.filter(stock_type="used") + + + used_cars_sold = cars_sold_filtered.filter(stock_type='used') total_used_cars_sold = used_cars_sold.count() - total_cost_of_used_cars_sold = ( - used_cars_sold.aggregate(total=Sum("cost_price"))["total"] or 0 - ) - total_revenue_from_used_cars = ( - used_cars_sold.aggregate(total=Sum(F("marked_price") - F("discount_amount")))[ - "total" - ] - or 0 - ) + total_cost_of_used_cars_sold = used_cars_sold.aggregate(total=Sum('cost_price'))['total'] or 0 + total_revenue_from_used_cars = used_cars_sold.aggregate( + total=Sum(F('marked_price') - F('discount_amount')) + )['total'] or 0 - total_vat_collected_from_used_cars = ( - used_cars_sold.annotate( - final_price=F("marked_price") - F("discount_amount") - ).aggregate(total=Sum(F("final_price") * VAT_RATE))["total"] - or 0 - ) + total_vat_collected_from_used_cars = used_cars_sold.annotate( + final_price=F('marked_price') - F('discount_amount')).aggregate( + total=Sum(F('final_price') * VAT_RATE))['total'] or 0 - net_profit_from_used_cars = ( - total_revenue_from_used_cars - total_cost_of_used_cars_sold - ) + net_profit_from_used_cars = total_revenue_from_used_cars - total_cost_of_used_cars_sold # Service & Overall KPIs - total_revenue_from_services = sum( - [car.get_additional_services()["total"] for car in cars_sold_filtered] - ) - total_vat_collected_from_services = sum( - [car.get_additional_services()["services_vat"] for car in cars_sold_filtered] - ) - total_vat_collected = ( - total_vat_collected_from_cars + total_vat_collected_from_services - ) + total_revenue_from_services = sum([car.get_additional_services()['total'] for car in cars_sold_filtered]) + total_vat_collected_from_services = sum([car.get_additional_services()['services_vat'] for car in cars_sold_filtered]) + total_vat_collected = total_vat_collected_from_cars + total_vat_collected_from_services total_revenue_generated = total_revenue_from_cars + total_revenue_from_services - expenses = models.ItemModel.objects.filter( - entity__admin__dealer=dealer, item_role="expense" - ) - total_expenses = expenses.aggregate(total=Sum("default_amount"))["total"] or 0 + expenses = models.ItemModel.objects.filter(entity__admin__dealer=dealer, item_role='expense') + total_expenses = expenses.aggregate(total=Sum('default_amount'))['total'] or 0 gross_profit = net_profit_from_cars - total_expenses # ---------------------------------------------------- # 4. Chart Data Aggregation # ---------------------------------------------------- - monthly_sales_data = ( - cars_sold_filtered.annotate(month=ExtractMonth("sold_date")) - .values("month") - .annotate( - total_cars=Count("pk"), - total_revenue=Sum(F("marked_price") - F("discount_amount")), - total_profit=Sum( - F("marked_price") - F("discount_amount") - F("cost_price") - ), - ) - .order_by("month") - ) + monthly_sales_data = cars_sold_filtered.annotate( + month=ExtractMonth('sold_date') + ).values('month').annotate( + total_cars=Count('pk'), + total_revenue=Sum(F('marked_price') - F('discount_amount')), + total_profit=Sum(F('marked_price') - F('discount_amount') - F('cost_price')) + ).order_by('month') monthly_cars_sold = [0] * 12 monthly_revenue = [0] * 12 monthly_net_profit = [0] * 12 for data in monthly_sales_data: - month_index = data["month"] - 1 - monthly_cars_sold[month_index] = data["total_cars"] - monthly_revenue[month_index] = ( - float(data["total_revenue"]) if data["total_revenue"] else 0 - ) - monthly_net_profit[month_index] = ( - float(data["total_profit"]) if data["total_profit"] else 0 - ) + month_index = data['month'] - 1 + monthly_cars_sold[month_index] = data['total_cars'] + monthly_revenue[month_index] = float(data['total_revenue']) if data['total_revenue'] else 0 + monthly_net_profit[month_index] = float(data['total_profit']) if data['total_profit'] else 0 monthly_cars_sold_json = json.dumps(monthly_cars_sold) monthly_revenue_json = json.dumps(monthly_revenue) @@ -579,215 +529,222 @@ def general_dashboard(request, dealer_slug): # ---------------------------------------------------- # Sales by MAKE # ---------------------------------------------------- - sales_by_make_data = ( - cars_sold_filtered.values("id_car_make__name") - .annotate(car_count=Count("id_car_make__name")) - .order_by("-car_count") - ) + sales_by_make_data = cars_sold_filtered.values('id_car_make__name').annotate( + car_count=Count('id_car_make__name') + ).order_by('-car_count') + + sales_by_make_labels = [data['id_car_make__name'] for data in sales_by_make_data] + sales_by_make_counts = [data['car_count'] for data in sales_by_make_data] + - sales_by_make_labels = [data["id_car_make__name"] for data in sales_by_make_data] - sales_by_make_counts = [data["car_count"] for data in sales_by_make_data] # ---------------------------------------------------- # DATA FOR CAR SALES BY MODELS (for the new interactive chart) # ---------------------------------------------------- + # Get the selected make from the URL query parameter - selected_make_sales = request.GET.get("make_sold", None) + selected_make_sales= request.GET.get('make_sold', None) + # Get a list of all unique makes for the dropdown - all_makes_sold = list( - cars_sold_filtered.values_list("id_car_make__name", flat=True) - .distinct() - .order_by("id_car_make__name") - ) + all_makes_sold = list(cars_sold_filtered.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name')) if selected_make_sales: # If a make is selected, filter the queryset - sales_data_by_model = ( - cars_sold_filtered.filter(id_car_make__name=selected_make_sales) - .values("id_car_model__name") - .annotate(count=Count("id_car_model__name")) - .order_by("-count") - ) + sales_data_by_model = cars_sold_filtered.filter( + id_car_make__name=selected_make_sales + ).values('id_car_model__name').annotate( + count=Count('id_car_model__name') + ).order_by('-count') else: # If no make is selected, pass an empty list or some default data sales_data_by_model = [] + + + # 1. Inventory by Make (Pie Chart) - inventory_by_make_data = ( - active_cars.values("id_car_make__name") - .annotate(car_count=Count("id_car_make__name")) - .order_by("-car_count") - ) + inventory_by_make_data = active_cars.values('id_car_make__name').annotate( + car_count=Count('id_car_make__name') + ).order_by('-car_count') - inventory_by_make_labels = [ - data["id_car_make__name"] for data in inventory_by_make_data - ] - inventory_by_make_counts = [data["car_count"] for data in inventory_by_make_data] + inventory_by_make_labels = [data['id_car_make__name'] for data in inventory_by_make_data] + inventory_by_make_counts = [data['car_count'] for data in inventory_by_make_data] # 2. Inventory by Model (Bar Chart) - selected_make_inventory = request.GET.get("make_inventory", None) + selected_make_inventory = request.GET.get('make_inventory', None) # Get all unique makes in inventory for the dropdown - all_makes_inventory = list( - active_cars.values_list("id_car_make__name", flat=True) - .distinct() - .order_by("id_car_make__name") - ) + all_makes_inventory = list(active_cars.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name')) if selected_make_inventory: - inventory_data_by_model = ( - active_cars.filter(id_car_make__name=selected_make_inventory) - .values("id_car_model__name") - .annotate(count=Count("id_car_model__name")) - .order_by("-count") - ) + inventory_data_by_model = active_cars.filter( + id_car_make__name=selected_make_inventory + ).values('id_car_model__name').annotate( + count=Count('id_car_model__name') + ).order_by('-count') else: # Default data inventory_data_by_model = [] context = { - "start_date": start_date, - "end_date": end_date, - "today": today_local, + 'start_date': start_date, + 'end_date': end_date, + 'today': today_local, + # Inventory KPIs - "total_cars_in_inventory": total_cars_in_inventory, - "total_inventory_value": total_inventory_value, - "total_new_cars_in_inventory": total_new_cars_in_inventory, - "total_used_cars_in_inventory": total_used_cars_in_inventory, - "new_car_value": new_car_value, - "used_car_value": used_car_value, - "aging_inventory_count": aging_inventory_count, + 'total_cars_in_inventory': total_cars_in_inventory, + 'total_inventory_value': total_inventory_value, + 'total_new_cars_in_inventory': total_new_cars_in_inventory, + 'total_used_cars_in_inventory': total_used_cars_in_inventory, + 'new_car_value': new_car_value, + 'used_car_value': used_car_value, + 'aging_inventory_count': aging_inventory_count, + # Sales KPIs - "total_cars_sold": total_cars_sold, - "total_cost_of_cars_sold": total_cost_of_cars_sold, - "total_revenue_from_cars": total_revenue_from_cars, - "net_profit_from_cars": net_profit_from_cars, - "total_vat_collected_from_cars": total_vat_collected_from_cars, - "total_discount_on_cars": total_discount, + 'total_cars_sold': total_cars_sold, + 'total_cost_of_cars_sold': total_cost_of_cars_sold, + 'total_revenue_from_cars': total_revenue_from_cars, + 'net_profit_from_cars': net_profit_from_cars, + 'total_vat_collected_from_cars': total_vat_collected_from_cars, + 'total_discount_on_cars': total_discount, + # Sales by Type - "total_new_cars_sold": total_new_cars_sold, - "total_used_cars_sold": total_used_cars_sold, - "total_cost_of_new_cars_sold": total_cost_of_new_cars_sold, - "total_revenue_from_new_cars": total_revenue_from_new_cars, - "net_profit_from_new_cars": net_profit_from_new_cars, - "total_vat_collected_from_new_cars": total_vat_collected_from_new_cars, - "total_cost_of_used_cars_sold": total_cost_of_used_cars_sold, - "total_revenue_from_used_cars": total_revenue_from_used_cars, - "net_profit_from_used_cars": net_profit_from_used_cars, - "total_vat_collected_from_used_cars": total_vat_collected_from_used_cars, + 'total_new_cars_sold': total_new_cars_sold, + 'total_used_cars_sold': total_used_cars_sold, + 'total_cost_of_new_cars_sold': total_cost_of_new_cars_sold, + 'total_revenue_from_new_cars': total_revenue_from_new_cars, + 'net_profit_from_new_cars': net_profit_from_new_cars, + 'total_vat_collected_from_new_cars': total_vat_collected_from_new_cars, + 'total_cost_of_used_cars_sold': total_cost_of_used_cars_sold, + 'total_revenue_from_used_cars': total_revenue_from_used_cars, + 'net_profit_from_used_cars': net_profit_from_used_cars, + 'total_vat_collected_from_used_cars': total_vat_collected_from_used_cars, + # Services and Overall KPIs - "total_revenue_from_services": total_revenue_from_services, - "total_vat_collected_from_services": total_vat_collected_from_services, - "total_revenue_generated": total_revenue_generated, - "total_vat_collected": total_vat_collected, - "total_expenses": total_expenses, - "gross_profit": gross_profit, + 'total_revenue_from_services': total_revenue_from_services, + 'total_vat_collected_from_services': total_vat_collected_from_services, + 'total_revenue_generated': total_revenue_generated, + 'total_vat_collected': total_vat_collected, + 'total_expenses': total_expenses, + 'gross_profit': gross_profit, + # Chart Data - "monthly_cars_sold_json": monthly_cars_sold_json, - "monthly_revenue_json": monthly_revenue_json, - "monthly_net_profit_json": monthly_net_profit_json, - # Sales Chart Data - "sales_by_make_labels_json": json.dumps(sales_by_make_labels), - "sales_by_make_counts_json": json.dumps(sales_by_make_counts), - "all_makes_sold": all_makes_sold, - "selected_make_sales": selected_make_sales, - "sales_data_by_model_json": json.dumps(list(sales_data_by_model)), + + 'monthly_cars_sold_json': monthly_cars_sold_json, + 'monthly_revenue_json': monthly_revenue_json, + 'monthly_net_profit_json': monthly_net_profit_json, + + + # Sales Chart Data + 'sales_by_make_labels_json': json.dumps(sales_by_make_labels), + 'sales_by_make_counts_json': json.dumps(sales_by_make_counts), + 'all_makes_sold': all_makes_sold, + 'selected_make_sales': selected_make_sales, + 'sales_data_by_model_json': json.dumps(list(sales_data_by_model)), + # New Inventory Chart Data - "inventory_by_make_labels_json": json.dumps(inventory_by_make_labels), - "inventory_by_make_counts_json": json.dumps(inventory_by_make_counts), - "all_makes_inventory": all_makes_inventory, - "selected_make_inventory": selected_make_inventory, - "inventory_data_by_model_json": json.dumps(list(inventory_data_by_model)), + 'inventory_by_make_labels_json': json.dumps(inventory_by_make_labels), + 'inventory_by_make_counts_json': json.dumps(inventory_by_make_counts), + 'all_makes_inventory': all_makes_inventory, + 'selected_make_inventory': selected_make_inventory, + 'inventory_data_by_model_json': json.dumps(list(inventory_data_by_model)), + + } - return render(request, "dashboards/general_dashboard.html", context) + + return render(request, 'dashboards/general_dashboard.html', context) @login_required -def sales_dashboard(request, dealer_slug): - dealer = get_object_or_404(models.Dealer, slug=dealer_slug) +def sales_dashboard(request,dealer_slug): + dealer = get_object_or_404(models.Dealer,slug=dealer_slug) today_local = timezone.localdate() # ---------------------------------------------------- # 1. Date Filtering # ---------------------------------------------------- - start_date_str = request.GET.get("start_date") - end_date_str = request.GET.get("end_date") + start_date_str = request.GET.get('start_date') + end_date_str = request.GET.get('end_date') if start_date_str and end_date_str: - start_date = timezone.datetime.strptime(start_date_str, "%Y-%m-%d").date() - end_date = timezone.datetime.strptime(end_date_str, "%Y-%m-%d").date() + start_date = timezone.datetime.strptime(start_date_str, '%Y-%m-%d').date() + end_date = timezone.datetime.strptime(end_date_str, '%Y-%m-%d').date() else: start_date = today_local - timedelta(days=30) end_date = today_local # Filter leads by date range and dealer leads_filtered = models.Lead.objects.filter( - dealer=dealer, created__date__gte=start_date, created__date__lte=end_date + dealer=dealer, + created__date__gte=start_date, + created__date__lte=end_date ) + # ---------------------------------------------------- # 2. Lead Sources Chart Logic # ---------------------------------------------------- # Group leads by source and count them # This generates a list of dictionaries like [{'source': 'Showroom', 'count': 45}, ...] - lead_sources_data = ( - leads_filtered.values("source") - .annotate(count=Count("source")) - .order_by("-count") - ) + lead_sources_data = leads_filtered.values('source').annotate( + count=Count('source') + ).order_by('-count') # Separate the labels and counts for the chart - lead_sources_labels = [item["source"] for item in lead_sources_data] - lead_sources_counts = [item["count"] for item in lead_sources_data] + lead_sources_labels = [item['source'] for item in lead_sources_data] + lead_sources_counts = [item['count'] for item in lead_sources_data] # ---------------------------------------------------- # 2. Lead Funnel Chart Logic # ---------------------------------------------------- opportunity_filtered = models.Opportunity.objects.filter( - dealer=dealer, created__date__gte=start_date, created__date__lte=end_date + dealer=dealer, + created__date__gte=start_date, + created__date__lte=end_date ) - opportunity_stage_data = ( - opportunity_filtered.values("stage") - .annotate(count=Count("stage")) - .order_by("-count") - ) - # Separate the labels and counts for the chart - opportunity_stage_labels = [item["stage"] for item in opportunity_stage_data] - opportunity_stage_counts = [item["count"] for item in opportunity_stage_data] + opportunity_stage_data = opportunity_filtered.values('stage').annotate( + count=Count('stage') + ).order_by('-count') + # Separate the labels and counts for the chart + opportunity_stage_labels = [item['stage'] for item in opportunity_stage_data ] + opportunity_stage_counts = [item['count'] for item in opportunity_stage_data ] + # 2. Inventory KPIs # ---------------------------------------------------- - active_cars = models.Car.objects.filter(dealer=dealer).exclude(status="sold") + active_cars = models.Car.objects.filter(dealer=dealer).exclude(status='sold') total_cars_in_inventory = active_cars.count() - new_cars_qs = active_cars.filter(stock_type="new") + new_cars_qs = active_cars.filter(stock_type='new') total_new_cars_in_inventory = new_cars_qs.count() - used_cars_qs = active_cars.filter(stock_type="used") + used_cars_qs = active_cars.filter(stock_type='used') total_used_cars_in_inventory = used_cars_qs.count() aging_threshold_days = 60 - aging_inventory_count = active_cars.filter( - receiving_date__date__lte=today_local - timedelta(days=aging_threshold_days) - ).count() + aging_inventory_count = active_cars.filter(receiving_date__date__lte=today_local - timedelta(days=aging_threshold_days)).count() + context = { - "start_date": start_date, - "end_date": end_date, - "lead_sources_labels_json": json.dumps(lead_sources_labels), - "lead_sources_counts_json": json.dumps(lead_sources_counts), - "opportunity_stage_labels_json": json.dumps(opportunity_stage_labels), - "opportunity_stage_counts_json": json.dumps(opportunity_stage_counts), - # Inventory KPIs - "total_cars_in_inventory": total_cars_in_inventory, - "total_new_cars_in_inventory": total_new_cars_in_inventory, - "total_used_cars_in_inventory": total_used_cars_in_inventory, - "aging_inventory_count": aging_inventory_count, + 'start_date': start_date, + 'end_date': end_date, + 'lead_sources_labels_json': json.dumps(lead_sources_labels), + 'lead_sources_counts_json': json.dumps(lead_sources_counts), + 'opportunity_stage_labels_json': json.dumps(opportunity_stage_labels), + 'opportunity_stage_counts_json': json.dumps(opportunity_stage_counts), + + # Inventory KPIs + 'total_cars_in_inventory': total_cars_in_inventory, + 'total_new_cars_in_inventory': total_new_cars_in_inventory, + 'total_used_cars_in_inventory': total_used_cars_in_inventory, + 'aging_inventory_count': aging_inventory_count, } - return render(request, "dashboards/sales_dashboard.html", context) + return render(request, 'dashboards/sales_dashboard.html', context) + + def aging_inventory_list_view(request, dealer_slug): @@ -799,78 +756,49 @@ def aging_inventory_list_view(request, dealer_slug): aging_threshold_days = 60 # Get filter parameters from the request - selected_make = request.GET.get("make") - selected_model = request.GET.get("model") - selected_series = request.GET.get( - "series" - ) # Changed 'serie' to 'series' for consistency - selected_year = request.GET.get("year") - selected_stock_type = request.GET.get("stock_type") + selected_make = request.GET.get('make') + selected_model = request.GET.get('model') + selected_series = request.GET.get('series') # Changed 'serie' to 'series' for consistency + selected_year = request.GET.get('year') + selected_stock_type = request.GET.get('stock_type') # Start with the base queryset for all aging cars. aging_cars_queryset = models.Car.objects.filter( dealer=dealer, - receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days), - ).exclude(status="sold") - total_aging_inventory_value = aging_cars_queryset.aggregate( - total=Sum("cost_price") - )["total"] + receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days) + ).exclude(status='sold') + total_aging_inventory_value=aging_cars_queryset.aggregate(total=Sum('cost_price'))['total'] # Apply filters to the queryset if they exist. Chaining is fine here. if selected_make: - aging_cars_queryset = aging_cars_queryset.filter( - id_car_make__name=selected_make - ) + aging_cars_queryset = aging_cars_queryset.filter(id_car_make__name=selected_make) if selected_model: - aging_cars_queryset = aging_cars_queryset.filter( - id_car_model__name=selected_model - ) + aging_cars_queryset = aging_cars_queryset.filter(id_car_model__name=selected_model) if selected_series: - aging_cars_queryset = aging_cars_queryset.filter( - id_car_series__name=selected_series - ) + aging_cars_queryset = aging_cars_queryset.filter(id_car_series__name=selected_series) if selected_year: - aging_cars_queryset = aging_cars_queryset.filter( - id_car_year__year=selected_year - ) + aging_cars_queryset = aging_cars_queryset.filter(id_car_year__year=selected_year) if selected_stock_type: aging_cars_queryset = aging_cars_queryset.filter(stock_type=selected_stock_type) + # Get distinct values for filter dropdowns based on the initial, unfiltered aging cars queryset. # This ensures all possible filter options are always available. aging_base_queryset = models.Car.objects.filter( dealer=dealer, - receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days), - ).exclude(status="sold") + receiving_date__date__lt=today_local - timedelta(days=aging_threshold_days) + ).exclude(status='sold') - all_makes = ( - aging_base_queryset.values_list("id_car_make__name", flat=True) - .distinct() - .order_by("id_car_make__name") - ) - all_models = ( - aging_base_queryset.values_list("id_car_model__name", flat=True) - .distinct() - .order_by("id_car_model__name") - ) - all_series = ( - aging_base_queryset.values_list("id_car_serie__name", flat=True) - .distinct() - .order_by("id_car_serie__name") - ) - all_stock_types = ( - aging_base_queryset.values_list("stock_type", flat=True) - .distinct() - .order_by("stock_type") - ) - all_years = ( - aging_base_queryset.values_list("year", flat=True).distinct().order_by("-year") - ) + all_makes = aging_base_queryset.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name') + all_models = aging_base_queryset.values_list('id_car_model__name', flat=True).distinct().order_by('id_car_model__name') + all_series = aging_base_queryset.values_list('id_car_serie__name', flat=True).distinct().order_by('id_car_serie__name') + all_stock_types = aging_base_queryset.values_list('stock_type', flat=True).distinct().order_by('stock_type') + all_years = aging_base_queryset.values_list('year', flat=True).distinct().order_by('-year') # # Set up pagination paginator = Paginator(aging_cars_queryset, 10) - page_number = request.GET.get("page") + page_number = request.GET.get('page') page_obj = paginator.get_page(page_number) # Iterate only on the cars for the current page to add the age attribute. @@ -880,21 +808,21 @@ def aging_inventory_list_view(request, dealer_slug): context = { "is_paginated": page_obj.has_other_pages, "cars": page_obj.object_list, - "selected_make": selected_make, - "selected_model": selected_model, - "selected_series": selected_series, # Corrected variable name - "selected_year": selected_year, - "selected_stock_type": selected_stock_type, - "all_makes": all_makes, - "all_models": all_models, - "all_series": all_series, - "all_stock_types": all_stock_types, - "all_years": all_years, - "total_aging_inventory_value": total_aging_inventory_value, + 'selected_make': selected_make, + 'selected_model': selected_model, + 'selected_series': selected_series, # Corrected variable name + 'selected_year': selected_year, + 'selected_stock_type': selected_stock_type, + 'all_makes': all_makes, + 'all_models': all_models, + 'all_series': all_series, + 'all_stock_types': all_stock_types, + 'all_years': all_years, + 'total_aging_inventory_value':total_aging_inventory_value + } - return render(request, "dashboards/aging_inventory_list.html", context) - + return render(request, 'dashboards/aging_inventory_list.html', context) def terms_and_privacy(request): return render(request, "terms_and_privacy.html") @@ -919,9 +847,7 @@ def WelcomeView(request): return render(request, "welcome.html", context) -class CarCreateView( - LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView -): +class CarCreateView(LoginRequiredMixin, PermissionRequiredMixin,SuccessMessageMixin, CreateView): """ Manages the creation of a new car entry in the inventory system. @@ -943,7 +869,7 @@ class CarCreateView( form_class = forms.CarForm template_name = "inventory/car_form.html" permission_required = ["inventory.add_car"] - success_message = _("Car Added successfully to the inventory") + success_message=_("Car Added successfully to the inventory") def get_form(self, form_class=None): form = super().get_form(form_class) @@ -1341,9 +1267,7 @@ class CarInventory(LoginRequiredMixin, PermissionRequiredMixin, ListView): return context -class CarColorCreate( - LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView -): +class CarColorCreate(LoginRequiredMixin, PermissionRequiredMixin,SuccessMessageMixin, CreateView): """ View for creating a new car color. @@ -1367,7 +1291,7 @@ class CarColorCreate( form_class = forms.CarColorsForm template_name = "inventory/add_colors.html" permission_required = ["inventory.add_carcolors"] - success_message = _("Car colors details added successfully") + success_message=_("Car colors details added successfully") def form_valid(self, form): car = get_object_or_404(models.Car, slug=self.kwargs["slug"]) @@ -1800,7 +1724,7 @@ class CarDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): permission_required = ["inventory.view_car"] -def CarFinanceUpdateView(request, dealer_slug, slug): +def CarFinanceUpdateView(request,dealer_slug,slug): car = get_object_or_404(models.Car, slug=slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug) @@ -1814,12 +1738,7 @@ def CarFinanceUpdateView(request, dealer_slug, slug): else: form = forms.CarFinanceForm(instance=car) - return render( - request, - "inventory/car_finance_form.html", - {"car": car, "dealer": dealer, "form": form}, - ) - + return render(request, "inventory/car_finance_form.html", {"car": car, "dealer": dealer, "form": form}) class CarUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView @@ -1850,10 +1769,7 @@ class CarUpdateView( permission_required = ["inventory.change_car"] def get_success_url(self): - return reverse( - "car_detail", - kwargs={"dealer_slug": self.request.dealer.slug, "slug": self.object.slug}, - ) + return reverse("car_detail", kwargs={"dealer_slug": self.request.dealer.slug,"slug": self.object.slug}) def get_form(self, form_class=None): form = super().get_form(form_class) @@ -2451,8 +2367,7 @@ class DealerUpdateView( def get_success_url(self): return reverse("dealer_detail", kwargs={"slug": self.object.slug}) - -class StaffDetailView(LoginRequiredMixin, DetailView): +class StaffDetailView(LoginRequiredMixin, DetailView): """ Represents a detailed view for a Dealer model. @@ -2474,6 +2389,7 @@ class StaffDetailView(LoginRequiredMixin, DetailView): context_object_name = "staff" + def dealer_vat_rate_update(request, slug): dealer = get_object_or_404(models.Dealer, slug=slug) models.VatRate.objects.filter(dealer=dealer).update(rate=request.POST.get("rate")) @@ -2571,7 +2487,6 @@ class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): # context["note_form"] = forms.NoteForm() # return context - class CustomerDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): """ CustomerDetailView handles retrieving and presenting detailed information about @@ -2601,11 +2516,13 @@ class CustomerDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView context = super().get_context_data(**kwargs) context["notes"] = models.Notes.objects.filter( - dealer=dealer, content_type__model="customer", object_id=self.object.id + dealer=dealer, + content_type__model="customer", object_id=self.object.id ) estimates = entity.get_estimates().filter(customer=self.object.customer_model) invoices = entity.get_invoices().filter(customer=self.object.customer_model) - context["leads"] = self.object.customer_leads.all() + context['leads']=self.object.customer_leads.all() + total = estimates.count() + invoices.count() @@ -2720,7 +2637,8 @@ class CustomerCreateView( def form_valid(self, form): dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) if customer := models.Customer.objects.filter( - dealer=dealer, email=form.instance.email + dealer=dealer, + email=form.instance.email ).first(): if not customer.active: messages.error( @@ -2899,25 +2817,16 @@ def vendorDetailView(request, dealer_slug, slug): :rtype: HttpResponse """ dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - vendor = get_object_or_404(models.Vendor, slug=slug, dealer=dealer) - cars = vendor.cars.all() - total_cars_from_vendor = cars.count() - vendor_makes = cars.values("id_car_make__name").annotate( - make_count=Count("id_car_make__name") - ) - vendor_bills = BillModel.objects.filter(vendor=vendor.vendor_model) - paginator = Paginator(vendor_bills, 20) + vendor = get_object_or_404(models.Vendor, slug=slug,dealer=dealer) + cars=vendor.cars.all() + total_cars_from_vendor=cars.count() + vendor_makes=cars.values('id_car_make__name').annotate(make_count=Count('id_car_make__name')) + vendor_bills=BillModel.objects.filter(vendor=vendor.vendor_model) + paginator=Paginator(vendor_bills,20) page_number = request.GET.get("page") - page_obj = paginator.get_page(page_number) + page_obj=paginator.get_page(page_number) return render( - request, - template_name="vendors/view_vendor.html", - context={ - "vendor": vendor, - "vendor_bills": page_obj, - "total_cars_from_vendor": total_cars_from_vendor, - "vendor_makes": vendor_makes, - }, + request, template_name="vendors/view_vendor.html", context={"vendor": vendor,"vendor_bills":page_obj,"total_cars_from_vendor":total_cars_from_vendor,"vendor_makes":vendor_makes} ) @@ -2954,9 +2863,7 @@ class VendorCreateView( def form_valid(self, form): dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) - if vendor := models.Vendor.objects.filter( - dealer=dealer, email=form.instance.email - ).first(): + if vendor := models.Vendor.objects.filter(dealer=dealer,email=form.instance.email).first(): if not vendor.active: messages.error( self.request, @@ -3744,7 +3651,7 @@ class UserCreateView( # staff_member, _ = StaffMember.objects.get_or_create(user=user) # for service in form.cleaned_data["service_offered"]: - # staff_member.services_offered.add(service) + # staff_member.services_offered.add(service) staff.user = user staff.dealer = dealer staff.save() @@ -3754,9 +3661,7 @@ class UserCreateView( return super().form_valid(form) def get_success_url(self): - return reverse_lazy( - "staff_password_reset", args=[self.request.dealer.slug, self.staff_pk] - ) + return reverse_lazy("staff_password_reset", args=[self.request.dealer.slug, self.staff_pk]) # return reverse_lazy("user_list", args=[self.request.dealer.slug]) @@ -3940,7 +3845,8 @@ class OrganizationCreateView(LoginRequiredMixin, PermissionRequiredMixin, Create def form_valid(self, form): dealer = get_object_or_404(models.Dealer, slug=self.kwargs["dealer_slug"]) if organization := models.Organization.objects.filter( - dealer=dealer, email=form.instance.email + dealer=dealer, + email=form.instance.email ).first(): if not organization.active: messages.error( @@ -4448,7 +4354,6 @@ class AccountListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): dealer = get_user_type(self.request) accounts = dealer.entity.get_all_accounts() return apply_search_filters(accounts, query) - def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["url_kwargs"] = self.kwargs @@ -4518,27 +4423,17 @@ class AccountCreateView( def get_success_url(self): return reverse( - "account_list", - kwargs={ - "dealer_slug": self.kwargs["dealer_slug"], - "coa_pk": self.kwargs["coa_pk"], - }, + "account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"], "coa_pk": self.kwargs["coa_pk"]} ) - - def get_context_data(self, **kwargs): + def get_context_data(self,**kwargs): context = super().get_context_data(**kwargs) context["url_kwargs"] = self.kwargs coa_pk = context["url_kwargs"]["coa_pk"] try: - kwargs["coa_model"] = ( - ChartOfAccountModel.objects.get(pk=coa_pk) - or self.request.entity.get_default_coa() - ) + kwargs["coa_model"] = ChartOfAccountModel.objects.get(pk=coa_pk) or self.request.entity.get_default_coa() except Exception: kwargs["coa_model"] = self.request.entity.get_default_coa() return context - - class AccountDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView): """ Represents the detailed view for an account with additional context data related to account @@ -4640,30 +4535,21 @@ class AccountUpdateView( def get_success_url(self): return reverse_lazy( - "account_list", - kwargs={ - "dealer_slug": self.kwargs["dealer_slug"], - "coa_pk": self.kwargs["coa_pk"], - }, + "account_list", kwargs={"dealer_slug": self.kwargs["dealer_slug"],"coa_pk":self.kwargs["coa_pk"]} ) - def get_context_data(self, **kwargs): + def get_context_data(self,**kwargs): context = super().get_context_data(**kwargs) context["url_kwargs"] = self.kwargs coa_pk = context["url_kwargs"]["coa_pk"] try: - kwargs["coa_model"] = ( - ChartOfAccountModel.objects.get(pk=coa_pk) - or self.request.entity.get_default_coa() - ) + kwargs["coa_model"] = ChartOfAccountModel.objects.get(pk=coa_pk) or self.request.entity.get_default_coa() except Exception: kwargs["coa_model"] = self.request.entity.get_default_coa() return context - - @login_required @permission_required("django_ledger.delete_accountmodel") -def account_delete(request, dealer_slug, coa_pk, pk): +def account_delete(request, dealer_slug,coa_pk, pk): """ Handles the deletion of an account object identified by its primary key (pk). Ensures that the user has the necessary permissions to perform the deletion. Successfully @@ -4708,19 +4594,17 @@ def sales_list_view(request, dealer_slug): qs = [] try: if any([request.is_dealer, request.is_manager, request.is_accountant]): - qs = models.ExtraInfo.get_sale_orders( - staff=staff, is_dealer=True, dealer=dealer - ) + qs = models.ExtraInfo.get_sale_orders(staff=staff, is_dealer=True,dealer=dealer) elif request.is_staff: - qs = models.ExtraInfo.get_sale_orders(staff=staff, dealer=dealer) + qs = models.ExtraInfo.get_sale_orders(staff=staff,dealer=dealer) except Exception as e: print(e) - search_query = request.GET.get("q", None) + search_query = request.GET.get('q', None) if search_query: qs = qs.filter( - Q(order_number__icontains=search_query) - | Q(customer__customer_name__icontains=search_query) + Q(order_number__icontains=search_query)| + Q(customer__customer_name__icontains=search_query) ).distinct() paginator = Paginator(qs, 30) @@ -4808,13 +4692,11 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): dealer=dealer, content_type=ContentType.objects.get_for_model(EstimateModel), related_content_type=ContentType.objects.get_for_model(models.Staff), - ).union( - models.ExtraInfo.objects.filter( - dealer=dealer, - content_type=ContentType.objects.get_for_model(EstimateModel), - related_content_type=ContentType.objects.get_for_model(User), - ) - ) + ).union(models.ExtraInfo.objects.filter( + dealer=dealer, + content_type=ContentType.objects.get_for_model(EstimateModel), + related_content_type=ContentType.objects.get_for_model(User), + )) elif self.request.is_staff and self.request.is_sales: qs = models.ExtraInfo.objects.filter( @@ -4824,11 +4706,11 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): related_object_id=self.request.staff.pk, ) qs = EstimateModel.objects.filter(pk__in=[x.content_object.pk for x in qs]) - search_query = self.request.GET.get("q", None) + search_query = self.request.GET.get('q', None) if search_query: qs = qs.filter( - Q(estimate_number__icontains=search_query) - | Q(customer__customer_name__icontains=search_query) + Q(estimate_number__icontains=search_query)| + Q(customer__customer_name__icontains=search_query) ).distinct() context["staff_estimates"] = qs return context @@ -4841,12 +4723,12 @@ class EstimateListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): queryset = entity.get_estimates() if status: queryset = queryset.filter(status=status) - search_query = self.request.GET.get("q", None) + search_query = self.request.GET.get('q', None) if search_query: queryset = queryset.filter( - Q(estimate_number__icontains=search_query) - | Q(customer__customer_name__icontains=search_query) + Q(estimate_number__icontains=search_query)| + Q(customer__customer_name__icontains=search_query) ).distinct() return queryset @@ -4883,9 +4765,7 @@ def create_estimate(request, dealer_slug, slug=None): data = json.loads(request.body) title = data.get("title") customer_id = data.get("customer") - customer = models.Customer.objects.filter( - pk=int(customer_id), dealer=dealer - ).first() + customer = models.Customer.objects.filter(pk=int(customer_id),dealer=dealer).first() items = data.get("item", []) quantities = data.get("quantity", []) @@ -4976,9 +4856,7 @@ def create_estimate(request, dealer_slug, slug=None): "quantity": 1, "unit_cost": round(float(i.marked_price)), "unit_revenue": round(float(i.marked_price)), - "total_amount": round( - float(i.final_price_plus_vat) - ), # TODO : check later + "total_amount": round(float(i.final_price_plus_vat)),# TODO : check later } ) @@ -5101,7 +4979,6 @@ def create_estimate(request, dealer_slug, slug=None): .annotate(hash_count=Count("hash")) .distinct() ) - context = { "form": form, "items": [ @@ -5157,7 +5034,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView if estimate.get_itemtxs_data(): # calculator = CarFinanceCalculator(estimate) # finance_data = calculator.get_finance_data() - finance_data = get_finance_data(estimate, dealer) + finance_data = get_finance_data(estimate,dealer) invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first() kwargs["data"] = finance_data @@ -5167,9 +5044,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView car = estimate.get_itemtxs_data()[0].first().item_model.car selected_items = car.additional_services.filter(dealer=dealer) form = forms.AdditionalFinancesForm() - form.fields["additional_finances"].queryset = form.fields[ - "additional_finances" - ].queryset.filter(dealer=dealer) # + form.fields["additional_finances"].queryset = form.fields["additional_finances"].queryset.filter(dealer=dealer) # form.initial["additional_finances"] = selected_items kwargs["additionals_form"] = form except Exception as e: @@ -5183,10 +5058,10 @@ class EstimatePrintView(EstimateDetailView): It reuses the data-fetching logic from EstimateDetailView but uses a dedicated, stripped-down print template. """ - template_name = "sales/estimates/estimate_preview.html" + @login_required @permission_required("inventory.add_saleorder", raise_exception=True) def create_sale_order(request, dealer_slug, pk): @@ -5238,10 +5113,8 @@ def create_sale_order(request, dealer_slug, pk): f"KeyError: 'car_info' or 'status' key missing when attempting to update status to 'sold' for item.item_model PK: {getattr(item.item_model, 'pk', 'N/A')}." ) pass - item.item_model.car.sold_date = ( - timezone.now() - ) # to be checked added by faheed - item.item_model.car.save() # to be checked added byfaheed + item.item_model.car.sold_date=timezone.now() # to be checked added by faheed + item.item_model.car.save()# to be checked added byfaheed item.item_model.car.mark_as_sold() messages.success(request, "Sale Order created successfully") @@ -5262,7 +5135,7 @@ def create_sale_order(request, dealer_slug, pk): # form.fields["opportunity"].widget = HiddenInput() # calculator = CarFinanceCalculator(estimate) - finance_data = get_finance_data(estimate, dealer) + finance_data = get_finance_data(estimate,dealer) return render( request, "sales/estimates/sale_order_form.html", @@ -5283,21 +5156,14 @@ def update_estimate_discount(request, dealer_slug, pk): # calculator = CarFinanceCalculator(estimate) # finance_data = calculator.get_finance_data() discount_amount = request.POST.get("discount_amount", 0) - finance_data = get_finance_data(estimate, dealer) - car = finance_data.get("car") + finance_data = get_finance_data(estimate,dealer) + car = finance_data.get('car') if Decimal(discount_amount) >= car.marked_price: - messages.error( - request, _("Discount amount cannot be greater than marked price") - ) + messages.error(request, _("Discount amount cannot be greater than marked price")) return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk) - if Decimal(discount_amount) > car.marked_price * Decimal("0.5"): - messages.warning( - request, - _( - "Discount amount is greater than 50% of the marked price, proceed with caution." - ), - ) + if Decimal(discount_amount) > car.marked_price * Decimal('0.5'): + messages.warning(request, _("Discount amount is greater than 50% of the marked price, proceed with caution.")) else: messages.success(request, _("Discount updated successfully")) extra_info.data.update({"discount": Decimal(discount_amount)}) @@ -5314,7 +5180,9 @@ def update_estimate_additionals(request, dealer_slug, pk): if form.is_valid(): estimate = get_object_or_404(EstimateModel, pk=pk) car = estimate.get_itemtxs_data()[0].first().item_model.car - car.additional_services.set(form.cleaned_data["additional_finances"]) + car.additional_services.set( + form.cleaned_data["additional_finances"] + ) car.save() messages.success(request, "Additional Finances updated successfully") return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk) @@ -5339,7 +5207,7 @@ class SaleOrderDetail(LoginRequiredMixin, PermissionRequiredMixin, DetailView): if estimate.get_itemtxs_data(): # calculator = CarFinanceCalculator(estimate) # finance_data = calculator.get_finance_data() - finance_data = get_finance_data(estimate, dealer) + finance_data = get_finance_data(estimate,dealer) kwargs["data"] = finance_data return super().get_context_data(**kwargs) @@ -5437,9 +5305,10 @@ class EstimatePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailVie def get_context_data(self, **kwargs): estimate = kwargs.get("object") if estimate.get_itemtxs_data(): + # data = get_financial_values(estimate) # calculator = CarFinanceCalculator(estimate) - kwargs["data"] = get_finance_data(estimate, self.request.dealer) + kwargs["data"] = get_finance_data(estimate,self.request.dealer) return super().get_context_data(**kwargs) @@ -5578,11 +5447,9 @@ class InvoiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): self.request.is_accountant, ] ): - qs = models.ExtraInfo.get_invoices( - staff=staff, is_dealer=True, dealer=dealer - ) + qs = models.ExtraInfo.get_invoices(staff=staff, is_dealer=True,dealer=dealer) elif self.request.is_staff: - qs = models.ExtraInfo.get_invoices(staff=staff, dealer=dealer) + qs = models.ExtraInfo.get_invoices(staff=staff,dealer=dealer) except Exception as e: print(e) @@ -5623,7 +5490,7 @@ class InvoiceDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView) if invoice.get_itemtxs_data(): # calculator = CarFinanceCalculator(invoice) # finance_data = calculator.get_finance_data() - finance_data = get_finance_data(invoice, self.request.dealer) + finance_data = get_finance_data(invoice,self.request.dealer) kwargs["data"] = finance_data kwargs["payments"] = JournalEntryModel.objects.filter( ledger=invoice.ledger @@ -5719,11 +5586,7 @@ class ApprovedInvoiceModelUpdateFormView( def get_success_url(self): return reverse_lazy( "invoice_detail", - kwargs={ - "dealer_slug": self.kwargs["dealer_slug"], - "entity_slug": self.kwargs["entity_slug"], - "pk": self.object.pk, - }, + kwargs={"dealer_slug": self.kwargs["dealer_slug"],"entity_slug": self.kwargs["entity_slug"], "pk": self.object.pk}, ) @@ -5771,11 +5634,7 @@ class PaidInvoiceModelUpdateFormView( def get_success_url(self): return reverse_lazy( "invoice_detail", - kwargs={ - "dealer_slug": self.kwargs["dealer_slug"], - "entity_slug": self.kwargs["entity_slug"], - "pk": self.object.pk, - }, + kwargs={"dealer_slug": self.kwargs["dealer_slug"],"entity_slug": self.kwargs["entity_slug"], "pk": self.object.pk}, ) def form_valid(self, form): @@ -5783,12 +5642,7 @@ class PaidInvoiceModelUpdateFormView( if invoice.get_amount_open() > 0: messages.error(self.request, "Invoice is not fully paid") - return redirect( - "invoice_detail", - dealer_slug=self.kwargs["dealer_slug"], - entity_slug=self.kwargs["entity_slug"], - pk=invoice.pk, - ) + return redirect("invoice_detail",dealer_slug=self.kwargs["dealer_slug"],entity_slug=self.kwargs["entity_slug"], pk=invoice.pk) else: invoice.post_ledger() invoice.save() @@ -5820,22 +5674,12 @@ def invoice_mark_as(request, dealer_slug, pk): if mark and mark == "accept": if not invoice.can_approve(): messages.error(request, "invoice is not ready for approval") - return redirect( - "invoice_detail", - dealer_slug=dealer_slug, - entity_slug=request.entity.slug, - pk=invoice.pk, - ) + return redirect("invoice_detail", dealer_slug=dealer_slug,entity_slug=request.entity.slug, pk=invoice.pk) invoice.mark_as_approved( entity_slug=dealer.entity.slug, user_model=dealer.entity.admin ) invoice.save() - return redirect( - "invoice_detail", - dealer_slug=dealer_slug, - entity_slug=request.entity.slug, - pk=invoice.pk, - ) + return redirect("invoice_detail", dealer_slug=dealer_slug,entity_slug=request.entity.slug, pk=invoice.pk) @login_required @@ -5875,7 +5719,7 @@ def invoice_create(request, dealer_slug, pk): # calculator = CarFinanceCalculator(estimate) # finance_data = calculator.get_finance_data() - finance_data = get_finance_data(estimate, dealer) + finance_data = get_finance_data(estimate,dealer) car = finance_data.get("car") invoice_itemtxs = { car.item_model.item_number: { @@ -5899,12 +5743,7 @@ def invoice_create(request, dealer_slug, pk): estimate.save() invoice.save() messages.success(request, "Invoice created successfully") - return redirect( - "invoice_detail", - dealer_slug=dealer.slug, - entity_slug=entity.slug, - pk=invoice.pk, - ) + return redirect("invoice_detail", dealer_slug=dealer.slug,entity_slug=entity.slug, pk=invoice.pk) else: print(form.errors) form = forms.InvoiceModelCreateForm( @@ -5956,7 +5795,7 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView invoice = kwargs.get("object") if invoice.get_itemtxs_data(): # calculator = CarFinanceCalculator(invoice) - finance_data = get_finance_data(invoice, dealer) + finance_data = get_finance_data(invoice,dealer) kwargs["data"] = finance_data kwargs["dealer"] = dealer return super().get_context_data(**kwargs) @@ -5964,9 +5803,8 @@ class InvoicePreviewView(LoginRequiredMixin, PermissionRequiredMixin, DetailView # payments - class InvoiceModelUpdateView(InvoiceModelUpdateViewBase): - template_name = "sales/invoices/invoice_update.html" + template_name = 'sales/invoices/invoice_update.html' permission_required = ["django_ledger.change_invoicemodel"] @@ -5992,7 +5830,6 @@ class InvoiceModelUpdateView(InvoiceModelUpdateViewBase): # context = { "invoice": invoice, "form": form } # return render(request, "sales/payments/payment_form1.html", context) - @login_required @permission_required("inventory.add_payment", raise_exception=True) def PaymentCreateView(request, dealer_slug, pk): @@ -6039,12 +5876,7 @@ def PaymentCreateView(request, dealer_slug, pk): invoice = form.cleaned_data.get("invoice") # bill = form.cleaned_data.get("bill") payment_method = form.cleaned_data.get("payment_method") - response = redirect( - "invoice_detail", - dealer_slug=dealer.slug, - entity_slug=entity.slug, - pk=model.pk, - ) # if invoice else "bill_detail" + response = redirect("invoice_detail", dealer_slug=dealer.slug,entity_slug=entity.slug, pk=model.pk)# if invoice else "bill_detail" # model = invoice if invoice else bill if not model.is_approved(): @@ -6221,12 +6053,7 @@ def payment_mark_as_paid(request, dealer_slug, pk): exc_info=True, ) messages.error(request, f"Error: {str(e)}") - return redirect( - "invoice_detail", - dealer_slug=dealer_slug, - entity_slug=request.entity.slug, - pk=invoice.pk, - ) + return redirect("invoice_detail", dealer_slug=dealer_slug,entity_slug=request.entity.slug, pk=invoice.pk) # activity log @@ -6431,7 +6258,8 @@ def lead_create(request, dealer_slug): if instance.lead_type == "customer": customer = models.Customer.objects.filter( - dealer=dealer, email=instance.email + dealer=dealer, + email=instance.email ).first() if not customer: customer = models.Customer( @@ -6450,7 +6278,8 @@ def lead_create(request, dealer_slug): if instance.lead_type == "organization": organization = models.Organization.objects.filter( - dealer=dealer, email=instance.email + dealer=dealer, + email=instance.email ).first() if not organization: organization = models.Organization( @@ -6471,9 +6300,7 @@ def lead_create(request, dealer_slug): f"lead created successfully for dealer {dealer_slug} by user:{user_username}" ) messages.success(request, _("Lead created successfully")) - return redirect( - "lead_detail", dealer_slug=dealer_slug, slug=instance.slug - ) + return redirect("lead_detail",dealer_slug=dealer_slug,slug=instance.slug) else: logger.error( f"error creating leading for dealer {dealer_slug} by user:{user_username}" @@ -6524,8 +6351,8 @@ def lead_create(request, dealer_slug): form.fields["id_car_make"].queryset = qs form.fields["id_car_make"].choices = [ (obj.id_car_make, obj.get_local_name()) for obj in qs - ] - + ] + if first_make := qs.first(): form.fields["id_car_model"].queryset = first_make.carmodel_set.all() @@ -6546,7 +6373,7 @@ def lead_tracking(request, dealer_slug): qs = models.Lead.objects.filter(dealer=dealer, staff=staff) else: qs = models.Lead.objects.filter(dealer=dealer) - leads = qs + leads=qs won = qs.filter(status="won") new = qs.filter(status="new") lose = qs.filter(status="lose") @@ -6559,7 +6386,7 @@ def lead_tracking(request, dealer_slug): "won": won, "lose": lose, "negotiation": negotiation, - "leads": leads, + "leads":leads } return render(request, "crm/leads/lead_tracking.html", context) @@ -6590,7 +6417,10 @@ def update_lead_actions(request, dealer_slug): f"User {user_username} submitted incomplete data to update lead actions " f"for dealer '{dealer_slug}'. Missing fields: lead_id='{lead_id}', current_action='{current_action}', next_action='{next_action}'." ) - messages.error(request, _("All fields are required")) + messages.error( + request, + _("All fields are required") + ) return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug) # return JsonResponse( # {"success": False, "message": "All fields are required"}, status=400 @@ -6598,6 +6428,7 @@ def update_lead_actions(request, dealer_slug): # Get the lead + # Update lead fields lead.status = current_action @@ -6626,7 +6457,10 @@ def update_lead_actions(request, dealer_slug): f"submitted invalid date format ('{next_action_date}') " f"for Lead ID: {lead.pk}. Error: {ve}" ) - messages.error(request, _("Invalid date format")) + messages.error( + request, + _("Invalid date format") + ) return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug) # return JsonResponse( # {"success": False, "message": "Invalid date format"}, status=400 @@ -6638,7 +6472,10 @@ def update_lead_actions(request, dealer_slug): f"User {user_username} successfully updated Lead ID: {lead.pk} ('{lead.slug}'). " f"New Status: '{lead.status}', Next Action: '{lead.next_action}', Next Action Date: '{lead.next_action_date}'." ) - messages.success(request, _("Actions updated successfully")) + messages.success( + request, + _("Actions updated successfully") + ) return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug) # return JsonResponse( # {"success": True, "message": "Actions updated successfully"} @@ -6650,7 +6487,10 @@ def update_lead_actions(request, dealer_slug): f"User {user_username} attempted to update non-existent Lead with ID: '{lead_id}' " f"for dealer '{dealer_slug}'. Returning 404." ) - messages.error(request, _("Lead not found")) + messages.error( + request, + _("Lead not found") + ) return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug) # return JsonResponse({"success": False, "message": "Lead not found"}, status=404) except Exception as e: @@ -6660,7 +6500,10 @@ def update_lead_actions(request, dealer_slug): f"for dealer '{dealer_slug}'. Error: {e}", exc_info=True, # CRUCIAL: Includes the full traceback ) - messages.error(request, _("An error occurred while updating lead actions")) + messages.error( + request, + _("An error occurred while updating lead actions") + ) return redirect("lead_detail", dealer_slug=dealer_slug, slug=lead.slug) # return JsonResponse({"success": False, "message": str(e)}, status=500) @@ -6954,7 +6797,7 @@ def schedule_event(request, dealer_slug, content_type, slug): form = forms.ScheduleForm(request.POST) if form.is_valid(): - reminder = form.cleaned_data["reminder"] + reminder = form.cleaned_data['reminder'] instance = form.save(commit=False) instance.dealer = dealer instance.content_object = obj @@ -7002,13 +6845,7 @@ def schedule_event(request, dealer_slug, content_type, slug): activity_type=instance.scheduled_type, ) if reminder: - scheduled_at_aware = ( - timezone.make_aware( - instance.scheduled_at, timezone.get_current_timezone() - ) - if timezone.is_naive(instance.scheduled_at) - else instance.scheduled_at - ) + scheduled_at_aware = timezone.make_aware(instance.scheduled_at, timezone.get_current_timezone()) if timezone.is_naive(instance.scheduled_at) else instance.scheduled_at reminder_time = scheduled_at_aware - timezone.timedelta(minutes=15) # Only schedule if the reminder time is in the future @@ -7016,17 +6853,15 @@ def schedule_event(request, dealer_slug, content_type, slug): if reminder_time > timezone.now(): DjangoQSchedule.objects.create( name=f"send_schedule_reminder_email_to_{instance.scheduled_by.email}_for_{content_type}_with_PK_{instance.pk}", - func="inventory.tasks.send_schedule_reminder_email", + func='inventory.tasks.send_schedule_reminder_email', args=f'"{instance.pk}"', schedule_type=DjangoQSchedule.ONCE, next_run=reminder_time, - hook="inventory.tasks.log_email_status", + hook='inventory.tasks.log_email_status', ) messages.success(request, _("Appointment Created Successfully")) - return redirect( - f"{content_type}_detail", dealer_slug=dealer_slug, slug=slug - ) + return redirect(f'{content_type}_detail',dealer_slug=dealer_slug, slug=slug) else: # Log for invalid form data @@ -7066,7 +6901,7 @@ def lead_transfer(request, dealer_slug, slug): messages.success(request, _("Lead transferred successfully")) else: messages.error(request, f"Invalid form data: {str(form.errors)}") - return redirect("lead_detail", dealer_slug=dealer.slug, slug=lead.slug) + return redirect("lead_detail", dealer_slug=dealer.slug ,slug=lead.slug) @login_required @@ -7152,7 +6987,7 @@ def send_lead_email(request, dealer_slug, slug, email_pk=None): # f"Lead's opportunity does not exist. Redirecting to lead list." # ) # return response - # return redirect("lead_list", dealer_slug=dealer.slug) + # return redirect("lead_list", dealer_slug=dealer.slug) if request.method == "POST": email_pk = request.POST.get("email_pk") @@ -7207,7 +7042,7 @@ def send_lead_email(request, dealer_slug, slug, email_pk=None): # f"Lead's opportunity does not exist. Redirecting to lead list." # ) # return response - # return redirect("lead_list", dealer_slug=dealer_slug) + # return redirect("lead_list", dealer_slug=dealer_slug) msg = f""" السلام عليكم {lead.full_name}, @@ -7306,7 +7141,9 @@ class OpportunityCreateView( dealer=dealer, status="available", marked_price__gt=0 ) if self.request.is_dealer: - form.fields["lead"].queryset = models.Lead.objects.filter(dealer=dealer) + form.fields["lead"].queryset = models.Lead.objects.filter( + dealer=dealer + ) elif self.request.is_staff: form.fields["lead"].queryset = models.Lead.objects.filter( dealer=dealer, staff=self.request.staff @@ -7354,12 +7191,13 @@ class OpportunityUpdateView( success_message = _("Opportunity updated successfully.") permission_required = ["inventory.change_opportunity"] + def get_form(self, form_class=None): form = super().get_form(form_class) dealer = get_object_or_404(models.Dealer, slug=self.kwargs.get("dealer_slug")) staff = getattr(self.request.user, "staff", None) form.fields["car"].queryset = models.Car.objects.filter( - dealer=dealer, status="available", marked_price__gt=0 + dealer=dealer, status="available",marked_price__gt=0 ) form.fields["lead"].queryset = models.Lead.objects.filter( dealer=dealer, staff=staff @@ -7375,7 +7213,6 @@ class OpportunityUpdateView( }, ) - class OpportunityStageUpdateView( LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, UpdateView ): @@ -7406,6 +7243,7 @@ class OpportunityStageUpdateView( success_message = _("Opportunity Stage updated successfully.") permission_required = ["inventory.change_opportunity"] + def get_success_url(self): return reverse_lazy( "opportunity_detail", @@ -7528,9 +7366,9 @@ class OpportunityListView(LoginRequiredMixin, PermissionRequiredMixin, ListView) queryset = models.Opportunity.objects.filter(dealer=dealer) elif self.request.is_staff: staff = self.request.staff - queryset = models.Opportunity.objects.filter( - dealer=dealer, lead__staff=staff - ) + queryset = models.Opportunity.objects.filter(dealer=dealer, lead__staff=staff) + + # Stage filter stage = self.request.GET.get("stage") @@ -7547,7 +7385,7 @@ class OpportunityListView(LoginRequiredMixin, PermissionRequiredMixin, ListView) elif sort == "closing": queryset = queryset.order_by("expected_close_date") - # Search filter + # Search filter search = self.request.GET.get("q") if search: queryset = queryset.filter( @@ -7802,17 +7640,16 @@ class ItemServiceListView(LoginRequiredMixin, PermissionRequiredMixin, ListView) query = self.request.GET.get("q") qs = models.AdditionalServices.objects.filter(dealer=dealer).all() if query: - qs = qs.filter( - Q(name__icontains=query) - | Q(id__icontains=query) - | Q(uom__icontains=query) - ) + qs = qs.filter(Q(name__icontains=query)| + Q(id__icontains=query)| + Q(uom__icontains=query) + ) return qs -class ItemExpenseCreateView( - LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView -): + + +class ItemExpenseCreateView(LoginRequiredMixin, PermissionRequiredMixin,SuccessMessageMixin, CreateView): """ Represents a view for creating item expense entries. @@ -7903,6 +7740,9 @@ class ItemExpenseUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateV ) + + + class ItemExpenseListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): """ Handles the display of a list of item expenses. @@ -7969,10 +7809,8 @@ class BillListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): qs = dealer.entity.get_bills() query = self.request.GET.get("q") if query: - qs = qs.filter( - Q(bill_number__icontains=query) - | Q(vendor__vendor_name__icontains=query) - ) + qs = qs.filter(Q(bill_number__icontains=query)| + Q(vendor__vendor_name__icontains=query)) return qs def get_context_data(self, **kwargs): @@ -7981,9 +7819,7 @@ class BillListView(LoginRequiredMixin, PermissionRequiredMixin, ListView): return context -class BillModelCreateView( - LoginRequiredMixin, PermissionRequiredMixin, SuccessMessageMixin, CreateView -): +class BillModelCreateView(LoginRequiredMixin, PermissionRequiredMixin,SuccessMessageMixin, CreateView): template_name = "bill/bill_create.html" PAGE_TITLE = _("Create Bill") permission_required = "django_ledger.add_billmodel" @@ -9321,6 +9157,7 @@ def schedule_cancel(request, dealer_slug, pk): @login_required @permission_required("inventory.change_dealer", raise_exception=True) def assign_car_makes(request, dealer_slug): + """ Assigns car makes to a dealer. @@ -9448,7 +9285,7 @@ class LedgerModelDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailV permission_required = "django_ledger.view_ledgermodel" -class LedgerModelCreateView(LedgerModelCreateViewBase, SuccessMessageMixin): +class LedgerModelCreateView(LedgerModelCreateViewBase,SuccessMessageMixin): """ Handles the creation of LedgerModel entities. @@ -9512,17 +9349,16 @@ class LedgerModelModelActionView(LedgerModelModelActionViewBase): ) + @login_required @permission_required("django_ledger.delete_ledgermodel", raise_exception=True) -def LedgerModelDeleteView(request, dealer_slug, entity_slug, ledger_pk): +def LedgerModelDeleteView(request, dealer_slug,entity_slug,ledger_pk): ledger = LedgerModel.objects.filter(pk=ledger_pk).first() if request.method == "POST": ledger.delete() messages.success(request, _("Ledger deleted successfully")) return redirect("ledger_list", dealer_slug=dealer_slug, entity_slug=entity_slug) - return render(request, "ledger/ledger/ledger_delete.html", {"ledger_model": ledger}) - - + return render(request,"ledger/ledger/ledger_delete.html",{"ledger_model":ledger}) # class LedgerModelDeleteView(DeleteView, SuccessMessageMixin): # """ # Handles the deletion of a Ledger model instance. @@ -9647,7 +9483,7 @@ class JournalEntryCreateView( @login_required @permission_required("django_ledger.delete_journalentrymodel", raise_exception=True) -def JournalEntryDeleteView(request, dealer_slug, pk): +def JournalEntryDeleteView(request,dealer_slug, pk): """ Handles the deletion of a specific journal entry. This view facilitates the deletion of a journal entry identified by its primary key (pk). If the @@ -9668,10 +9504,10 @@ def JournalEntryDeleteView(request, dealer_slug, pk): ledger = journal_entry.ledger if not journal_entry.can_delete(): messages.error(request, _("Journal Entry cannot be deleted")) - return redirect("journalentry_list", dealer_slug=dealer_slug, pk=ledger.pk) + return redirect("journalentry_list",dealer_slug=dealer_slug, pk=ledger.pk) journal_entry.delete() messages.success(request, "Journal Entry deleted") - return redirect("journalentry_list", dealer_slug=dealer_slug, pk=ledger.pk) + return redirect("journalentry_list",dealer_slug=dealer_slug, pk=ledger.pk) return render( request, "ledger/journal_entry/journal_entry_delete.html", @@ -9860,16 +9696,15 @@ def ledger_unpost_all_journals(request, dealer_slug, entity_slug, pk): @login_required @permission_required("inventory.change_dealer", raise_exception=True) def pricing_page(request, dealer_slug): - dealer = get_object_or_404(models.Dealer, slug=dealer_slug) + dealer=get_object_or_404(models.Dealer, slug=dealer_slug) if not dealer.active_plan: - plan_list = PlanPricing.objects.all() - form = forms.PaymentPlanForm() - return render( - request, "pricing_page.html", {"plan_list": plan_list, "form": form} - ) + plan_list = PlanPricing.objects.all() + form = forms.PaymentPlanForm() + return render(request, "pricing_page.html", {"plan_list": plan_list, "form": form}) else: - messages.info(request, _("You already have an plan!!")) - return redirect("home", dealer_slug=dealer_slug) + messages.info(request,_("You already have an plan!!")) + return redirect('home',dealer_slug=dealer_slug) + @login_required @@ -9909,47 +9744,41 @@ def payment_callback(request, dealer_slug): payment_id = request.GET.get("id") history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first() payment_status = request.GET.get("status") - logger.info( - f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}" - ) + logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}") order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW print(order) if payment_status == "paid": - logger.info( - f"Payment successful for transaction ID {payment_id}. Processing order completion." - ) + logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.") billing_info, created = BillingInfo.objects.get_or_create( user=dealer.user, defaults={ - "tax_number": dealer.vrn, - "name": dealer.arabic_name, - "street": dealer.address, - "zipcode": dealer.entity.zip_code or " ", - "city": dealer.entity.city or " ", - "country": dealer.entity.country or " ", - }, + 'tax_number': dealer.vrn, + 'name': dealer.arabic_name, + 'street': dealer.address, + 'zipcode': dealer.entity.zip_code or " ", + 'city': dealer.entity.city or " ", + 'country': dealer.entity.country or " ", + } ) if created: logger.info(f"Created new billing info for user {dealer.user}.") else: logger.debug(f"Billing info already exists for user {dealer.user}.") - if not hasattr(order.user, "userplan"): + if not hasattr(order.user, 'userplan'): UserPlan.objects.create( user=order.user, plan=order.plan, - expire=datetime.now().date() - + timedelta(days=order.get_plan_pricing().pricing.period), - ) - logger.info( - f"Created new UserPlan for user {order.user} with plan {order.plan}." + expire=datetime.now().date() + timedelta(days=order.get_plan_pricing().pricing.period) ) + logger.info(f"Created new UserPlan for user {order.user} with plan {order.plan}.") else: logger.info(f"UserPlan already exists for user {order.user}.") try: + # if order.user.userplan: # user = order.user # pricing = order.get_plan_pricing().pricing @@ -9964,36 +9793,28 @@ def payment_callback(request, dealer_slug): order.complete_order() history.status = "paid" history.save() - logger.info( - f"Order {order.id} for user {order.user} completed successfully. Payment history updated." - ) + logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.") invoice = order.get_invoices().first() return render( - request, "payment_success.html", {"order": order, "invoice": invoice} + request, + "payment_success.html", + {"order": order, "invoice": invoice} ) except Exception as e: - logger.exception( - f"Error completing order {order.id} for user {order.user}: {e}" - ) + logger.exception(f"Error completing order {order.id} for user {order.user}: {e}") logger.error(f"Plan activation failed: {str(e)}") history.status = "failed" history.save() - return render( - request, "payment_failed.html", {"message": "Plan activation error"} - ) + return render(request, "payment_failed.html", {"message": "Plan activation error"}) elif payment_status == "failed": - logger.warning( - f"Payment failed for transaction ID {payment_id}. Message: {message}" - ) + logger.warning(f"Payment failed for transaction ID {payment_id}. Message: {message}") history.status = "failed" history.save() return render(request, "payment_failed.html", {"message": message}) return render(request, "payment_failed.html", {"message": "Unknown payment status"}) - - # def payment_callback(request, dealer_slug): # message = request.GET.get("message") # dealer = get_object_or_404(models.Dealer, slug=dealer_slug) @@ -10214,6 +10035,7 @@ def add_task(request, dealer_slug, content_type, slug): return redirect(f"{content_type}_detail", dealer_slug=dealer_slug, slug=slug) + @login_required @permission_required("inventory.change_tasks", raise_exception=True) def update_task(request, dealer_slug, pk): @@ -10327,12 +10149,10 @@ def management_view(request, dealer_slug): def user_management(request, dealer_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) context = { - "customers": models.Customer.objects.filter(active=False, dealer=dealer), - "organizations": models.Organization.objects.filter( - active=False, dealer=dealer - ), - "vendors": models.Vendor.objects.filter(active=False, dealer=dealer), - "staff": models.Staff.objects.filter(active=False, dealer=dealer), + "customers": models.Customer.objects.filter(active=False,dealer=dealer), + "organizations": models.Organization.objects.filter(active=False,dealer=dealer), + "vendors": models.Vendor.objects.filter(active=False,dealer=dealer), + "staff": models.Staff.objects.filter(active=False,dealer=dealer), } return render(request, "admin_management/user_management.html", context) @@ -10772,13 +10592,7 @@ class PurchaseOrderDetailView(LoginRequiredMixin, PermissionRequiredMixin, Detai title = f"Purchase Order {po_model.po_number}" context["page_title"] = title context["header_title"] = title - context["po_ready_to_fulfill"] = all( - [ - item - for item in po_model.get_itemtxs_data()[0] - if item.po_item_status == "received" - ] - ) + context["po_ready_to_fulfill"] = all([item for item in po_model.get_itemtxs_data()[0] if item.po_item_status == 'received']) po_model: PurchaseOrderModel = self.object po_items_qs, item_data = po_model.get_itemtxs_data( queryset=po_model.itemtransactionmodel_set.all().select_related( @@ -10806,18 +10620,14 @@ class PurchaseOrderListView(LoginRequiredMixin, PermissionRequiredMixin, ListVie query = self.request.GET.get("q") qs = self.model.objects.filter(entity=dealer.entity) if query: - qs = qs.filter( - Q(po_number__icontains=query) - | Q(po_status__icontains=query) - | Q(po_title__icontains=query) - ) + qs=qs.filter(Q(po_number__icontains=query)|Q(po_status__icontains=query)|Q(po_title__icontains=query)) return qs return qs def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) dealer = get_user_type(self.request) - vendors = models.Vendor.objects.filter(dealer=dealer) + vendors=models.Vendor.objects.filter(dealer=dealer) context = super().get_context_data(**kwargs) context["entity_slug"] = dealer.entity.slug context["vendors"] = vendors @@ -10956,9 +10766,7 @@ def upload_cars(request, dealer_slug, pk=None): response = redirect("upload_cars", dealer_slug=dealer_slug, pk=pk) if po_item.status == "uploaded": - messages.add_message( - request, messages.SUCCESS, "Item uploaded Sucessfully." - ) + messages.add_message(request, messages.SUCCESS, "Item uploaded Sucessfully.") return redirect( "view_items_inventory", dealer_slug=dealer_slug, @@ -11088,12 +10896,12 @@ def upload_cars(request, dealer_slug, pk=None): cost_price=po_item.item.unit_cost, ) # if po_item: #TODO:update - # models.CarFinance.objects.create( - # car=car, - # cost_price=po_item.item.unit_cost, - # marked_price=0, - # selling_price=0, - # ) + # models.CarFinance.objects.create( + # car=car, + # cost_price=po_item.item.unit_cost, + # marked_price=0, + # selling_price=0, + # ) car.add_colors(exterior=exterior, interior=interior) cars_created += 1 logger.debug( @@ -11178,72 +10986,64 @@ class InventoryListView(InventoryListViewBase): template_name = "inventory/list.html" permission_required = ["django_ledger.view_purchaseordermodel"] - @login_required -def purchase_report_view(request, dealer_slug): +def purchase_report_view(request,dealer_slug): pos = request.entity.get_purchase_orders() data = [] - total_po_amount = 0 - total_po_cars = 0 + total_po_amount=0 + total_po_cars=0 for po in pos: - items = [ - {"total": x.total_amount, "q": x.quantity} - for x in po.get_itemtxs_data()[0].all() - ] + items = [{"total":x.total_amount,"q":x.quantity} for x in po.get_itemtxs_data()[0].all()] - po_amount = 0 - po_quantity = 0 + po_amount=0 + po_quantity=0 for item in items: - po_amount += item["total"] - po_quantity += item["q"] + po_amount+=item["total"] + po_quantity+=item["q"] - total_po_amount += po_amount - total_po_cars += po_quantity - bills = po.get_po_bill_queryset() - vendors = set([bill.vendor.vendor_name for bill in bills]) + total_po_amount+=po_amount + total_po_cars+=po_quantity + bills=po.get_po_bill_queryset() + vendors=set([bill.vendor.vendor_name for bill in bills]) vendors_str = ", ".join(sorted(list(vendors))) if vendors else "N/A" - data.append( - { - "po_number": po.po_number, - "po_created": po.created, - "po_status": po.po_status, - "po_fulfilled_date": po.date_fulfilled, - "po_amount": po_amount, - "po_quantity": po_quantity, - "vendors_str": vendors_str, - } - ) + data.append({"po_number":po.po_number,"po_created":po.created,"po_status":po.po_status,"po_fulfilled_date":po.date_fulfilled,"po_amount":po_amount, + "po_quantity":po_quantity,"vendors_str":vendors_str}) current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S") - context = { - "dealer": request.entity.name, - "time": current_time, - "data": data, - "total_po_amount": total_po_amount, - "total_po_cars": total_po_cars, - "current_time": current_time, + context={ + "dealer":request.entity.name, + "time":current_time, + "data":data, + "total_po_amount":total_po_amount, + "total_po_cars":total_po_cars, + "current_time":current_time + } - return render(request, "ledger/reports/purchase_report.html", context) -def purchase_report_csv_export(request, dealer_slug): - response = HttpResponse(content_type="text/csv") + return render(request,'ledger/reports/purchase_report.html',context) + + +def purchase_report_csv_export(request,dealer_slug): + response = HttpResponse(content_type='text/csv') + current_time = timezone.now().strftime("%Y-%m-%d_%H%M%S") filename = f"purchase_report_{dealer_slug}_{current_time}.csv" - response["Content-Disposition"] = f'attachment; filename="{filename}"' + response['Content-Disposition'] = f'attachment; filename="{filename}"' writer = csv.writer(response) + header = [ - "PO Number", - "Created Date", - "Status", - "Fulfilled Date", - "PO Amount", - "PO Quantity", - "Vendors", + 'PO Number', + 'Created Date', + 'Status', + 'Fulfilled Date', + 'PO Amount', + 'PO Quantity', + 'Vendors' ] writer.writerow(header) pos = request.entity.get_purchase_orders() @@ -11251,47 +11051,45 @@ def purchase_report_csv_export(request, dealer_slug): for po in pos: po_amount = 0 po_quantity = 0 - items = [ - {"total": x.total_amount, "q": x.quantity} - for x in po.get_itemtxs_data()[0].all() - ] + items = [{"total":x.total_amount,"q":x.quantity} for x in po.get_itemtxs_data()[0].all()] for item in items: po_amount += item["total"] po_quantity += item["q"] bills = po.get_po_bill_queryset() - vendors = set([bill.vendor.vendor_name for bill in bills]) + vendors = set([bill.vendor.vendor_name for bill in bills ]) vendors_str = ", ".join(sorted(list(vendors))) if vendors else "N/A" - writer.writerow( - [ - po.po_number, - po.created.strftime("%Y-%m-%d %H:%M:%S") if po.created else "", - po.get_po_status_display(), - po.date_fulfilled.strftime("%Y-%m-%d") if po.date_fulfilled else "", - f"{po_amount:.2f}", - po_quantity, - vendors_str, - ] - ) + + writer.writerow([ + po.po_number, + po.created.strftime("%Y-%m-%d %H:%M:%S") if po.created else '', + po.get_po_status_display(), + po.date_fulfilled.strftime("%Y-%m-%d") if po.date_fulfilled else '', + f"{po_amount:.2f}", + po_quantity, + vendors_str + ]) return response + @login_required def car_sale_report_view(request, dealer_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - vat = models.VatRate.objects.filter(dealer=dealer, is_active=True).first() - VAT_RATE = vat.rate + vat = models.VatRate.objects.filter(dealer=dealer,is_active=True).first() + VAT_RATE=vat.rate + + cars_sold = models.Car.objects.filter(dealer=dealer, status='sold') - cars_sold = models.Car.objects.filter(dealer=dealer, status="sold") # Get filter parameters from the request - selected_make = request.GET.get("make") - selected_model = request.GET.get("model") - selected_serie = request.GET.get("serie") - selected_year = request.GET.get("year") - selected_stock_type = request.GET.get("stock_type") + selected_make = request.GET.get('make') + selected_model = request.GET.get('model') + selected_serie = request.GET.get('serie') + selected_year = request.GET.get('year') + selected_stock_type=request.GET.get('stock_type') # Apply filters to the queryset if selected_make: @@ -11305,124 +11103,89 @@ def car_sale_report_view(request, dealer_slug): if selected_stock_type: cars_sold = cars_sold.filter(stock_type=selected_stock_type) + # # Calculate summary data for the filtered results - total_cars_sold = cars_sold.count() - total_revenue_from_cars = ( - cars_sold.aggregate(total=Sum(F("marked_price") - F("discount_amount")))[ - "total" - ] - or 0 - ) + total_cars_sold=cars_sold.count() + total_revenue_from_cars = cars_sold.aggregate( + total=Sum(F('marked_price') - F('discount_amount')) + )['total'] or 0 - total_vat_on_cars = ( - cars_sold.annotate( - final_price=F("marked_price") - F("discount_amount") - ).aggregate(total=Sum(F("final_price") * VAT_RATE))["total"] - or 0 - ) + total_vat_on_cars=cars_sold.annotate( + final_price=F('marked_price') - F('discount_amount')).aggregate( + total=Sum(F('final_price') * VAT_RATE))['total'] or 0 - total_revenue_from_additonals = sum( - [car.get_additional_services()["total"] for car in cars_sold] - ) - total_vat_from_additonals = sum( - [car.get_additional_services()["services_vat"] for car in cars_sold] - ) - total_vat_collected = total_vat_on_cars + total_vat_from_additonals - total_revenue_collected = total_revenue_from_cars + total_revenue_from_additonals + total_revenue_from_additonals=sum([car.get_additional_services()['total'] for car in cars_sold]) + total_vat_from_additonals=sum([car.get_additional_services()['services_vat'] for car in cars_sold]) + total_vat_collected = total_vat_on_cars+total_vat_from_additonals + total_revenue_collected=total_revenue_from_cars+total_revenue_from_additonals total_discount = sum([car.discount for car in cars_sold]) current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S") # Get distinct values for filter dropdowns - base_sold_cars_queryset = models.Car.objects.filter(dealer=dealer, status="sold") - makes = base_sold_cars_queryset.values_list( - "id_car_make__name", flat=True - ).distinct() - models_qs = base_sold_cars_queryset.values_list( - "id_car_model__name", flat=True - ).distinct() + base_sold_cars_queryset = models.Car.objects.filter(dealer=dealer, status='sold') + makes =base_sold_cars_queryset.values_list('id_car_make__name', flat=True).distinct() + models_qs =base_sold_cars_queryset.values_list('id_car_model__name', flat=True).distinct() - series = base_sold_cars_queryset.values_list( - "id_car_serie__name", flat=True - ).distinct() - stock_types = base_sold_cars_queryset.values_list( - "stock_type", flat=True - ).distinct() - years = ( - base_sold_cars_queryset.values_list("year", flat=True) - .distinct() - .order_by("-year") - ) + series =base_sold_cars_queryset.values_list('id_car_serie__name', flat=True).distinct() + stock_types=base_sold_cars_queryset.values_list('stock_type', flat=True).distinct() + years = base_sold_cars_queryset.values_list('year', flat=True).distinct().order_by('-year') context = { - "cars_sold": cars_sold, - "total_cars_sold": total_cars_sold, - "current_time": current_time, - "dealer": dealer, - "total_revenue_from_cars": total_revenue_from_cars, - "total_revenue_from_additonals": total_revenue_from_additonals, - "total_revenue_collected": total_revenue_collected, - "total_vat_on_cars": total_vat_on_cars, - "total_vat_from_additonals": total_vat_from_additonals, - "total_vat_collected": total_vat_collected, - "total_discount": total_discount, - "makes": makes, - "models": models_qs, - "series": series, - "years": years, - "stock_types": stock_types, - "selected_make": selected_make, - "selected_model": selected_model, - "selected_serie": selected_serie, - "selected_year": selected_year, - "selected_stock_type": selected_stock_type, + 'cars_sold': cars_sold, + 'total_cars_sold':total_cars_sold, + 'current_time': current_time, + 'dealer': dealer, + 'total_revenue_from_cars': total_revenue_from_cars, + 'total_revenue_from_additonals':total_revenue_from_additonals, + 'total_revenue_collected': total_revenue_collected, + 'total_vat_on_cars':total_vat_on_cars, + 'total_vat_from_additonals':total_vat_from_additonals, + 'total_vat_collected':total_vat_collected, + 'total_discount': total_discount, + 'makes': makes, + 'models': models_qs, + 'series': series, + 'years': years, + 'stock_types':stock_types, + 'selected_make': selected_make, + 'selected_model': selected_model, + 'selected_serie': selected_serie, + 'selected_year': selected_year, + 'selected_stock_type':selected_stock_type, } - return render(request, "ledger/reports/car_sale_report.html", context) + return render(request, 'ledger/reports/car_sale_report.html', context) @login_required def car_sale_report_csv_export(request, dealer_slug): - response = HttpResponse(content_type="text/csv") + response = HttpResponse(content_type='text/csv') current_time = timezone.now().strftime("%Y-%m-%d_%H-%M-%S") filename = f"sales_report_{dealer_slug}_{current_time}.csv" - response["Content-Disposition"] = f'attachment; filename="{filename}"' + response['Content-Disposition'] = f'attachment; filename="{filename}"' writer = csv.writer(response) # Define the CSV header based on your HTML table headers header = [ - "VIN", - "Make", - "Model", - "Year", - "Serie", - "Trim", - "Mileage", - "Stock Type", - "Created Date", - "Sold Date", - "Cost Price", - "Marked Price", - "Discount Amount", - "Selling Price", - "VAT on Car", - "Services Price", - "VAT on Services", - "Final Total", - "Invoice Number", + 'VIN', 'Make', 'Model', 'Year', 'Serie', 'Trim', 'Mileage', + 'Stock Type', 'Created Date', 'Sold Date', 'Cost Price', + 'Marked Price', 'Discount Amount', 'Selling Price', + 'VAT on Car', 'Services Price', 'VAT on Services', 'Final Total', + 'Invoice Number' ] writer.writerow(header) dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - cars_sold = models.Car.objects.filter(dealer=dealer, status="sold") + cars_sold = models.Car.objects.filter(dealer=dealer, status='sold') # Apply filters from the request, just like in your HTML view - selected_make = request.GET.get("make") - selected_model = request.GET.get("model") - selected_serie = request.GET.get("serie") - selected_year = request.GET.get("year") - selected_stock_type = request.GET.get("stock_type") + selected_make = request.GET.get('make') + selected_model = request.GET.get('model') + selected_serie = request.GET.get('serie') + selected_year = request.GET.get('year') + selected_stock_type = request.GET.get('stock_type') if selected_make: cars_sold = cars_sold.filter(id_car_make__name=selected_make) @@ -11439,8 +11202,8 @@ def car_sale_report_csv_export(request, dealer_slug): for car in cars_sold: # Fetching data for the additional services additional_services = car.get_additional_services() - services_total_price = additional_services["total"] - services_vat_amount = additional_services["services_vat"] + services_total_price = additional_services['total'] + services_vat_amount = additional_services['services_vat'] # Checking for the invoice number to avoid errors on cars without one invoice_number = None @@ -11449,29 +11212,27 @@ def car_sale_report_csv_export(request, dealer_slug): invoice_number = car.invoice.invoice_number sold_date = car.invoice.date_paid - writer.writerow( - [ - car.vin, - car.id_car_make.name, - car.id_car_model.name, - car.year, - car.id_car_serie.name, - car.id_car_trim.name, - car.mileage if car.mileage else "0", - car.stock_type, - car.created_at.strftime("%Y-%m-%d %H:%M:%S") if car.created_at else "", - sold_date.strftime("%Y-%m-%d %H:%M:%S") if sold_date else "", - car.cost_price, - car.marked_price, - car.discount, # Ensure this property returns a number - car.final_price, # Selling Price without VAT - car.vat_amount, # VAT on the car - services_total_price, # Total services without VAT - services_vat_amount, # VAT on services - car.final_price_plus_services_plus_vat, - invoice_number, - ] - ) + writer.writerow([ + car.vin, + car.id_car_make.name, + car.id_car_model.name, + car.year, + car.id_car_serie.name, + car.id_car_trim.name, + car.mileage if car.mileage else '0', + car.stock_type, + car.created_at.strftime("%Y-%m-%d %H:%M:%S") if car.created_at else '', + sold_date.strftime("%Y-%m-%d %H:%M:%S") if sold_date else '', + car.cost_price, + car.marked_price, + car.discount, # Ensure this property returns a number + car.final_price, # Selling Price without VAT + car.vat_amount, # VAT on the car + services_total_price, # Total services without VAT + services_vat_amount, # VAT on services + car.final_price_plus_services_plus_vat, + invoice_number, + ]) return response @@ -11482,109 +11243,94 @@ def staff_password_reset_view(request, dealer_slug, user_pk): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) staff = models.Staff.objects.filter(dealer=dealer, pk=user_pk).first() - if request.method == "POST": + if request.method == 'POST': form = forms.CustomSetPasswordForm(staff.user, request.POST) if form.is_valid(): form.save() - messages.success( - request, - _("Your password has been set. You may go ahead and log in now."), - ) - return redirect("user_detail", dealer_slug=dealer_slug, slug=staff.slug) + messages.success(request, _('Your password has been set. You may go ahead and log in now.')) + return redirect('user_detail',dealer_slug=dealer_slug,slug=staff.slug) else: - messages.error(request, _("Invalid password. Please try again.")) + messages.error(request, _('Invalid password. Please try again.')) form = forms.CustomSetPasswordForm(staff.user) - return render(request, "users/user_password_reset.html", {"form": form}) - + return render(request, 'users/user_password_reset.html', {'form': form}) class RecallListView(ListView): model = models.Recall - template_name = "recalls/recall_list.html" - context_object_name = "recalls" + template_name = 'recalls/recall_list.html' + context_object_name = 'recalls' paginate_by = 20 def get_queryset(self): - queryset = ( - super() - .get_queryset() - .annotate( - dealer_count=Count("notifications", distinct=True), - car_count=Count("notifications__cars_affected", distinct=True), - ) + queryset = super().get_queryset().annotate( + dealer_count=Count('notifications', distinct=True), + car_count=Count('notifications__cars_affected', distinct=True) ) - return queryset.select_related("make", "model", "serie", "trim") + return queryset.select_related('make', 'model', 'serie', 'trim') class RecallDetailView(DetailView): model = models.Recall - template_name = "recalls/recall_detail.html" - context_object_name = "recall" + template_name = 'recalls/recall_detail.html' + context_object_name = 'recall' def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - context["notifications"] = self.object.notifications.select_related("dealer") + context['notifications'] = self.object.notifications.select_related('dealer') return context def RecallFilterView(request): - context = {"make_data": models.CarMake.objects.all()} + context = {'make_data': models.CarMake.objects.all()} if request.method == "POST": - make = request.POST.get("make") - model = request.POST.get("model") - serie = request.POST.get("serie") - trim = request.POST.get("trim") - year = request.POST.get("year") - url = reverse("recall_create") + make = request.POST.get('make') + model = request.POST.get('model') + serie = request.POST.get('serie') + trim = request.POST.get('trim') + year = request.POST.get('year') + url = reverse('recall_create') url += f"?make={make}&model={model}&serie={serie}&trim={trim}&year={year}" - cars = models.Car.objects.filter( - id_car_make=make, - id_car_model=model, - id_car_serie=serie, - id_car_trim=trim, - year=year, - ) - context["url"] = url - context["cars"] = cars - return render(request, "recalls/recall_filter.html", context) - + cars = models.Car.objects.filter(id_car_make=make,id_car_model=model,id_car_serie=serie,id_car_trim=trim,year=year) + context['url'] = url + context['cars'] = cars + return render(request,'recalls/recall_filter.html',context) class RecallCreateView(FormView): - template_name = "recalls/recall_create.html" + template_name = 'recalls/recall_create.html' form_class = forms.RecallCreateForm - success_url = reverse_lazy("recall_success") + success_url = reverse_lazy('recall_success') def get_form(self, form_class=None): form = super().get_form(form_class) - make = self.request.GET.get("make") - model = self.request.GET.get("model") - serie = self.request.GET.get("serie") - trim = self.request.GET.get("trim") - year = self.request.GET.get("year") + make = self.request.GET.get('make') + model = self.request.GET.get('model') + serie = self.request.GET.get('serie') + trim = self.request.GET.get('trim') + year = self.request.GET.get('year') if make: qs = models.CarMake.objects.filter(pk=make) - form.fields["make"].queryset = qs - form.initial["make"] = qs.first() + form.fields['make'].queryset = qs + form.initial['make'] = qs.first() if model: qs = models.CarModel.objects.filter(pk=model) - form.fields["model"].queryset = qs - form.initial["model"] = qs.first() + form.fields['model'].queryset = qs + form.initial['model'] = qs.first() if serie: qs = models.CarSerie.objects.filter(pk=serie) - form.fields["serie"].queryset = qs - form.initial["serie"] = qs.first() + form.fields['serie'].queryset = qs + form.initial['serie'] = qs.first() if trim: qs = models.CarTrim.objects.filter(pk=trim) - form.fields["trim"].queryset = qs - form.initial["trim"] = qs.first() + form.fields['trim'].queryset = qs + form.initial['trim'] = qs.first() if year: - form.fields["year_from"].initial = year - form.fields["year_to"].initial = year + form.fields['year_from'].initial = year + form.fields['year_to'].initial = year return form def get_initial(self): initial = super().get_initial() - if self.request.method == "GET": + if self.request.method == 'GET': initial.update(self.request.GET.dict()) return initial @@ -11620,156 +11366,137 @@ class RecallCreateView(FormView): for dealer in dealers: dealer_cars = cars.filter(dealer=dealer) notification = models.RecallNotification.objects.create( - recall=recall, dealer=dealer + recall=recall, + dealer=dealer ) notification.cars_affected.set(dealer_cars) # Send email self.send_notification_email(dealer, recall, dealer_cars) - messages.success( - self.request, _("Recall created and notifications sent successfully") - ) + messages.success(self.request, _("Recall created and notifications sent successfully")) return super().form_valid(form) def send_notification_email(self, dealer, recall, cars): subject = f"Recall Notification: {recall.title}" - message = render_to_string( - "recalls/email/recall_notification.txt", - { - "dealer": dealer, - "recall": recall, - "cars": cars, - }, - ) + message = render_to_string('recalls/email/recall_notification.txt', { + 'dealer': dealer, + 'recall': recall, + 'cars': cars, + }) send_email( subject, message, - "noreply@yourdomain.com", + 'noreply@yourdomain.com', [dealer.user.email], ) - class RecallSuccessView(TemplateView): - template_name = "recalls/recall_success.html" + template_name = 'recalls/recall_success.html' @login_required -def schedule_calendar(request, dealer_slug): +def schedule_calendar(request,dealer_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - user_schedules = models.Schedule.objects.filter( - dealer=dealer, scheduled_by=request.user - ).order_by("scheduled_at") - upcoming_schedules = user_schedules.filter( - scheduled_at__gte=timezone.now() - ).order_by("scheduled_at") - context = {"schedules": user_schedules, "upcoming_schedules": upcoming_schedules} - return render(request, "schedule_calendar.html", context) + user_schedules = models.Schedule.objects.filter(dealer=dealer,scheduled_by=request.user).order_by('scheduled_at') + upcoming_schedules = user_schedules.filter(scheduled_at__gte=timezone.now()).order_by('scheduled_at') + context = { + 'schedules': user_schedules, + 'upcoming_schedules':upcoming_schedules + } + return render(request, 'schedule_calendar.html', context) # Support @login_required def help_center(request): - return render(request, "support/help_center.html") - + return render(request, 'support/help_center.html') @login_required -@permission_required("inventory.add_ticket") -def create_ticket(request, dealer_slug): +@permission_required('inventory.add_ticket') +def create_ticket(request,dealer_slug): if not request.is_dealer: - return redirect("home") + return redirect('home') dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - if request.method == "POST": + if request.method == 'POST': form = forms.TicketForm(request.POST) if form.is_valid(): instance = form.save(commit=False) instance.dealer = dealer instance.save() - messages.success( - request, "Your support ticket has been submitted successfully!" - ) - return redirect("ticket_list", dealer_slug=dealer.slug) + messages.success(request, 'Your support ticket has been submitted successfully!') + return redirect('ticket_list',dealer_slug=dealer.slug) else: form = forms.TicketForm() - return render(request, "support/create_ticket.html", {"form": form}) - + return render(request, 'support/create_ticket.html', {'form': form}) @login_required -@permission_required("inventory.view_ticket") -def ticket_list(request, dealer_slug): - dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - tickets = models.Ticket.objects.filter(dealer=dealer).order_by("-created_at") - query = request.GET.get("q") +@permission_required('inventory.view_ticket') +def ticket_list(request,dealer_slug): + dealer= get_object_or_404(models.Dealer, slug=dealer_slug) + tickets = models.Ticket.objects.filter(dealer=dealer).order_by('-created_at') + query=request.GET.get('q') if query: - tickets = tickets.filter(Q(id__icontains=query) | Q(subject__icontains=query)) - - return render(request, "support/ticket_list.html", {"tickets": tickets}) - + tickets=tickets.filter(Q(id__icontains=query)| Q(subject__icontains=query)) + + return render(request, 'support/ticket_list.html', {'tickets': tickets}) @login_required -@permission_required("inventory.change_ticket") -def ticket_detail(request, dealer_slug, ticket_id): +@permission_required('inventory.change_ticket') +def ticket_detail(request, dealer_slug,ticket_id): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) - ticket = models.Ticket.objects.get(dealer=dealer, id=ticket_id) - return render(request, "support/ticket_detail.html", {"ticket": ticket}) - + ticket = models.Ticket.objects.get(dealer=dealer,id=ticket_id) + return render(request, 'support/ticket_detail.html', {'ticket': ticket}) @login_required -@permission_required("inventory.change_ticket") +@permission_required('inventory.change_ticket') def ticket_mark_resolved(request, ticket_id): ticket = models.Ticket.objects.get(id=ticket_id) - ticket.status = "resolved" + ticket.status = 'resolved' ticket.save() - messages.success(request, "Ticket marked as resolved successfully!") - subject = "Ticket Resolved" + messages.success(request, 'Ticket marked as resolved successfully!') + subject = 'Ticket Resolved' message = f"Your support ticket has been resolved. Please check the details below:\n\nTicket ID: {ticket.id}\nSubject: {ticket.subject}\nDescription: {ticket.description}" - send_email(settings.SUPPORT_EMAIL, ticket.dealer.user.email, subject, message) - return render(request, "support/ticket_detail.html", {"ticket": ticket}) - + send_email( + settings.SUPPORT_EMAIL, + ticket.dealer.user.email, + subject, + message + ) + return render(request, 'support/ticket_detail.html', {'ticket': ticket}) @login_required -@permission_required("inventory.change_ticket") +@permission_required('inventory.change_ticket') def ticket_update(request, ticket_id): ticket = models.Ticket.objects.get(id=ticket_id) - if request.method == "POST": + if request.method == 'POST': form = forms.TicketResolutionForm(request.POST, instance=ticket) if form.is_valid(): form.save() - messages.success( - request, f"Ticket has been marked as {ticket.get_status_display()}." - ) - return redirect( - "ticket_detail", dealer_slug=ticket.dealer.slug, ticket_id=ticket.id - ) + messages.success(request, f'Ticket has been marked as {ticket.get_status_display()}.') + return redirect('ticket_detail',dealer_slug=ticket.dealer.slug, ticket_id=ticket.id) else: form = forms.TicketResolutionForm(instance=ticket) - return render( - request, "support/ticket_update.html", {"ticket": ticket, "form": form} - ) + return render(request, 'support/ticket_update.html', { + 'ticket': ticket, + 'form': form + }) class ChartOfAccountModelListView(ChartOfAccountModelListViewBase): - template_name = "chart_of_accounts/coa_list.html" - permission_required = "django_ledger.view_chartofaccountmodel" - - + template_name = 'chart_of_accounts/coa_list.html' + permission_required = 'django_ledger.view_chartofaccountmodel' class ChartOfAccountModelCreateView(ChartOfAccountModelCreateViewBase): - template_name = "chart_of_accounts/coa_create.html" - permission_required = "django_ledger.add_chartofaccountmodel" - - + template_name = 'chart_of_accounts/coa_create.html' + permission_required = 'django_ledger.add_chartofaccountmodel' class ChartOfAccountModelListView(ChartOfAccountModelListViewBase): - template_name = "chart_of_accounts/coa_list.html" - permission_required = "django_ledger.view_chartofaccountmodel" - - + template_name = 'chart_of_accounts/coa_list.html' + permission_required = 'django_ledger.view_chartofaccountmodel' class ChartOfAccountModelUpdateView(ChartOfAccountModelUpdateViewBase): - template_name = "chart_of_accounts/coa_update.html" - permission_required = "django_ledger.change_chartofaccountmodel" - - + template_name = 'chart_of_accounts/coa_update.html' + permission_required = 'django_ledger.change_chartofaccountmodel' class CharOfAccountModelActionView(CharOfAccountModelActionViewBase): - permission_required = "django_ledger.change_chartofaccountmodel" + permission_required = 'django_ledger.change_chartofaccountmodel' diff --git a/templates/base.html b/templates/base.html index 7c8b42c8..40098199 100644 --- a/templates/base.html +++ b/templates/base.html @@ -88,7 +88,7 @@
{% trans "Monthly Revenue & Profit" %}
- +
diff --git a/templates/dashboards/general_dashboard.html b/templates/dashboards/general_dashboard.html index bf681015..8f5b2b0a 100644 --- a/templates/dashboards/general_dashboard.html +++ b/templates/dashboards/general_dashboard.html @@ -8,13 +8,13 @@

- {% if request.is_dealer %} + {% if request.is_dealer or request.is_manager%} {% trans "Business Health Dashboard" %} - {% elif request.is_manger %} - {% trans "Manager Dashboard" %} - {% elif request.is_inventory %} + {% endif %} + {% if request.is_inventory and not request.is_manager and not request.is_dealer %} {% trans "Inventory Dashboard" %} - {% else %} + {% endif %} + {% if request.is_accountant and not request.is_manager and not request.is_dealer %} {% trans "Accountant Dashboard" %} {% endif %} diff --git a/templates/header.html b/templates/header.html index d54a6df6..4f340f25 100644 --- a/templates/header.html +++ b/templates/header.html @@ -439,12 +439,14 @@

- + {% if request.is_dealer%} + {% endif %} {% if request.is_staff %}
{% endif %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/items/expenses/expenses_list.html b/templates/items/expenses/expenses_list.html index 7512ca8b..1e14c954 100644 --- a/templates/items/expenses/expenses_list.html +++ b/templates/items/expenses/expenses_list.html @@ -5,6 +5,7 @@ {% endblock title %} {% block content %} {% if expenses or request.GET.q %} +

From f670015e39ac6eb6a8c07f54f7d372d5c1f59790 Mon Sep 17 00:00:00 2001 From: Faheedkhan Date: Thu, 28 Aug 2025 13:46:43 +0300 Subject: [PATCH 4/6] dashboard fixed --- inventory/urls.py | 4 +-- inventory/views.py | 35 ++++++++++++--------- templates/dashboards/general_dashboard.html | 10 +++--- templates/header.html | 27 +++++++--------- templates/index.html | 8 +++-- 5 files changed, 45 insertions(+), 39 deletions(-) diff --git a/inventory/urls.py b/inventory/urls.py index 028098d7..c7e8dabc 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -10,8 +10,8 @@ urlpatterns = [ # main URLs path("", views.WelcomeView, name="welcome"), path("signup/", views.dealer_signup, name="account_signup"), - path("", views.HomeView.as_view(), name="home"), - path("/", views.HomeView.as_view(), name="home"), + path("", views.HomeView, name="home"), + path("/", views.HomeView, name="home"), # Tasks path("legal/", views.terms_and_privacy, name="terms_and_privacy"), # path('tasks//detail/', views.task_detail, name='task_detail'), diff --git a/inventory/views.py b/inventory/views.py index 90d0efe2..322f3b6d 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -362,24 +362,31 @@ def dealer_signup(request): "account/signup-wizard.html", ) -class HomeView(LoginRequiredMixin, TemplateView): - """ - HomeView class responsible for rendering the home page. +# class HomeView(LoginRequiredMixin, TemplateView): +# """ +# HomeView class responsible for rendering the home page. - This class ensures that only authenticated users can access the home page. - Unauthenticated users are redirected to the welcome page. It is built on top of - Django's TemplateView and includes additional functionality by inheriting from - LoginRequiredMixin. The purpose of this class is to control the accessibility - of the main index page of the application and manage context data for frontend - rendering. +# This class ensures that only authenticated users can access the home page. +# Unauthenticated users are redirected to the welcome page. It is built on top of +# Django's TemplateView and includes additional functionality by inheriting from +# LoginRequiredMixin. The purpose of this class is to control the accessibility +# of the main index page of the application and manage context data for frontend +# rendering. - :ivar template_name: The path to the template used for rendering the view's - output. - :type template_name: str - """ +# :ivar template_name: The path to the template used for rendering the view's +# output. +# :type template_name: str +# """ - template_name = "index.html" +# template_name = "index.html" +@login_required +def HomeView(request,dealer_slug=None): + dealer_slug=request.dealer.slug + if request.is_sales and not request.is_manager and not request.is_dealer: + return redirect('sales_dashboard', dealer_slug=dealer_slug) + else: + return redirect('general_dashboard',dealer_slug=dealer_slug) class TestView(TemplateView): """ diff --git a/templates/dashboards/general_dashboard.html b/templates/dashboards/general_dashboard.html index 8f5b2b0a..bf681015 100644 --- a/templates/dashboards/general_dashboard.html +++ b/templates/dashboards/general_dashboard.html @@ -8,13 +8,13 @@

- {% if request.is_dealer or request.is_manager%} + {% if request.is_dealer %} {% trans "Business Health Dashboard" %} - {% endif %} - {% if request.is_inventory and not request.is_manager and not request.is_dealer %} + {% elif request.is_manger %} + {% trans "Manager Dashboard" %} + {% elif request.is_inventory %} {% trans "Inventory Dashboard" %} - {% endif %} - {% if request.is_accountant and not request.is_manager and not request.is_dealer %} + {% else %} {% trans "Accountant Dashboard" %} {% endif %} diff --git a/templates/header.html b/templates/header.html index 4f340f25..cda3de9c 100644 --- a/templates/header.html +++ b/templates/header.html @@ -11,6 +11,7 @@ hx-swap="outerHTML" hx-select-oob="#toast-container" hx-indicator="#spinner"> +

- +
{% endif %}