diff --git a/inventory/urls.py b/inventory/urls.py
index cca5ab0a..29affe5b 100644
--- a/inventory/urls.py
+++ b/inventory/urls.py
@@ -40,12 +40,15 @@ urlpatterns = [
views.assign_car_makes,
name="assign_car_makes",
),
- path(
- "dashboards/manager/",
- views.ManagerDashboard.as_view(),
- name="manager_dashboard",
- ),
+
+
+ #dashboards
+ path("dashboards/dealer/", views.DealerDashboard.as_view(),name="dealer_dashboard"),
+ path( "dashboards/manager/", views.ManagerDashboard.as_view(),name="manager_dashboard"),
path("dashboards/sales/", views.SalesDashboard.as_view(), name="sales_dashboard"),
+ path("dashboards/accountant/", views.AccountantDashboard.as_view(), name="accountant_dashboard"),
+ path("dashboards/inventory/", views.InventoryDashboard.as_view(), name="inventory_dashboard"),
+
path("cars/inventory/table/", views.CarListViewTable.as_view(), name="car_table"),
path("export/format/", TableExport.export, name="export"),
diff --git a/inventory/views.py b/inventory/views.py
index 212dcad3..6b36b60a 100644
--- a/inventory/views.py
+++ b/inventory/views.py
@@ -386,7 +386,101 @@ class TestView(TemplateView):
template_name = "inventory/cars_list_api.html"
+class DealerDashboard(LoginRequiredMixin, TemplateView):
+ """
+ ManagerDashboard class is a view handling the dashboard for a manager.
+ Provides functionality to manage and view various statistics and data specific
+ to the dealer associated with the authenticated manager. It uses a specific
+ template and ensures authentication before granting access. The class
+ aggregates data about cars, leads, financial statistics, and other related
+ business information for display in the manager's dashboard.
+
+ :ivar template_name: Path to the template used for rendering the manager's dashboard.
+ :type template_name: str
+ """
+
+ template_name = "dashboards/dealer_dashbaord.html"
+
+ # def dispatch(self, request, *args, **kwargs):
+ # if not request.user.is_authenticated:
+ # return redirect("welcome")
+ # if not getattr(request.user, "dealer", False):
+ # return HttpResponseForbidden(
+ # "You are not authorized to view this dashboard."
+ # )
+ # return super().dispatch(request, *args, **kwargs)
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ dealer = get_user_type(self.request)
+ entity = dealer.entity
+
+ qs = models.Car.objects.filter(dealer=dealer)
+ total_cars = qs.count()
+
+ stats = models.CarFinance.objects.filter(car__dealer=dealer).aggregate(
+ total_cost_price=Sum("cost_price"),
+ total_selling_price=Sum("selling_price"),
+ )
+ total_cost_price = stats["total_cost_price"] or 0
+ total_selling_price = stats["total_selling_price"] or 0
+ total_profit = total_selling_price - total_cost_price
+
+ new_leads = models.Lead.objects.filter(
+ dealer=dealer, status=models.Status.NEW
+ ).count()
+ pending_leads = models.Lead.objects.filter(
+ dealer=dealer, status=models.Status.CONTACTED
+ ).count()
+ canceled_leads = models.Lead.objects.filter(
+ dealer=dealer, status=models.Status.UNQUALIFIED
+ ).count()
+
+ car_status_qs = qs.values("status").annotate(count=Count("status"))
+ car_status = {status: count for status, count in car_status_qs}
+
+ car_by_make_qs = qs.values("id_car_make__name").annotate(count=Count("id"))
+ car_by_make = list(car_by_make_qs)
+
+ context["dealer"] = dealer
+ context["total_activity"] = models.UserActivityLog.objects.filter(
+ user=dealer.user
+ ).count()
+ context["total_cars"] = total_cars
+ context["total_reservations"] = models.CarReservation.objects.filter(
+ reserved_until__gte=timezone.now()
+ ).count()
+ context["total_cost_price"] = total_cost_price
+ context["total_selling_price"] = total_selling_price
+ context["total_profit"] = total_profit
+ context["new_leads"] = new_leads
+ context["pending_leads"] = pending_leads
+ context["canceled_leads"] = canceled_leads
+ context["car"] = json.dumps(car_by_make)
+
+
+ context["available_cars"] =qs.filter(status='available').count()
+ context["sold_cars"] = qs.filter(status='sold').count()
+ context["reserved_cars"] = qs.filter(status='reserved').count()
+ context["hold_cars"] =qs.filter(status='hold').count()
+ context["damaged_cars"] = qs.filter(status='damaged').count()
+ context["transfer_cars"] = qs.filter(status='transfer').count()
+ context["present_inventory_count"]=total_cars-context["sold_cars"]-context["damaged_cars"]
+ cars_sold=qs.filter(status='sold')
+ # cars_sold.aggregate(total_inventory_value=sum())
+
+
+ context["customers"] = entity.get_customers().count()
+ context["staff"] = models.Staff.objects.filter(dealer=dealer).count()
+ context["total_leads"] = models.Lead.objects.filter(dealer=dealer).count()
+ context["invoices"] = entity.get_invoices().count()
+ context["estimates"] = entity.get_estimates().count()
+ context["purchase_orders"] = entity.get_purchase_orders().count()
+
+
+
+ return context
class ManagerDashboard(LoginRequiredMixin, TemplateView):
"""
@@ -402,7 +496,7 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
:type template_name: str
"""
- template_name = "dashboards/manager.html"
+ template_name = "dashboards/manager_dashboard.html"
# def dispatch(self, request, *args, **kwargs):
# if not request.user.is_authenticated:
@@ -485,6 +579,9 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
return context
+
+
+
class SalesDashboard(LoginRequiredMixin, TemplateView):
"""
SalesDashboard class provides a view for the sales dashboard.
@@ -503,7 +600,152 @@ class SalesDashboard(LoginRequiredMixin, TemplateView):
:type template_name: str
"""
- template_name = "dashboards/sales.html"
+ template_name = "dashboards/sales_dashboard.html"
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ dealer = self.request.dealer
+ staff = getattr(self.request, "staff", None)
+ total_cars = models.Car.objects.filter(dealer=dealer).count()
+ total_reservations = models.CarReservation.objects.filter(
+ reserved_by=self.request.user, reserved_until__gte=timezone.now()
+ ).count()
+
+ available_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.AVAILABLE
+ ).count()
+ sold_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.SOLD
+ ).count()
+ reserved_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.RESERVED
+ ).count()
+ hold_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.HOLD
+ ).count()
+ damaged_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.DAMAGED
+ ).count()
+ transfer_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.TRANSFER
+ ).count()
+ reserved_percentage = reserved_cars / total_cars * 100
+ sold_percentage = sold_cars / total_cars * 100
+ qs = (
+ models.Car.objects.values("id_car_make__name")
+ .annotate(count=Count("id"))
+ .order_by("id_car_make__name")
+ )
+ car_by_make = list(qs)
+
+ context["dealer"] = dealer
+ context["staff"] = staff
+ context["total_cars"] = total_cars
+ context["total_reservations"] = total_reservations
+ context["reserved_percentage"] = reserved_percentage
+ context["sold_percentage"] = sold_percentage
+ context["available_cars"] = available_cars
+ context["sold_cars"] = sold_cars
+ context["reserved_cars"] = reserved_cars
+ context["hold_cars"] = hold_cars
+ context["damaged_cars"] = damaged_cars
+ context["transfer_cars"] = transfer_cars
+ context["car"] = json.dumps(car_by_make)
+
+ return context
+
+
+class InventoryDashboard(LoginRequiredMixin, TemplateView):
+ """
+ SalesDashboard class provides a view for the sales dashboard.
+
+ This class is responsible for generating the context data required to
+ display various statistics and information on the sales dashboard. It
+ inherits from `LoginRequiredMixin` and `TemplateView`, ensuring only
+ authenticated users can access the view. The dashboard includes data
+ such as the total number of cars, reservations, sold percentages,
+ reserved percentages, and cars categorized by various statuses. The
+ purpose of this dashboard is to provide dealership staff an overview
+ of their inventory and related sales metrics.
+
+ :ivar template_name: The name of the HTML template used for rendering
+ the sales dashboard.
+ :type template_name: str
+ """
+
+ template_name = "dashboards/inventory_dashboard.html"
+
+ def get_context_data(self, **kwargs):
+ context = super().get_context_data(**kwargs)
+ dealer = self.request.dealer
+ staff = getattr(self.request, "staff", None)
+ total_cars = models.Car.objects.filter(dealer=dealer).count()
+ total_reservations = models.CarReservation.objects.filter(
+ reserved_by=self.request.user, reserved_until__gte=timezone.now()
+ ).count()
+
+ available_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.AVAILABLE
+ ).count()
+ sold_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.SOLD
+ ).count()
+ reserved_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.RESERVED
+ ).count()
+ hold_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.HOLD
+ ).count()
+ damaged_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.DAMAGED
+ ).count()
+ transfer_cars = models.Car.objects.filter(
+ dealer=dealer, status=models.CarStatusChoices.TRANSFER
+ ).count()
+ reserved_percentage = reserved_cars / total_cars * 100
+ sold_percentage = sold_cars / total_cars * 100
+ qs = (
+ models.Car.objects.values("id_car_make__name")
+ .annotate(count=Count("id"))
+ .order_by("id_car_make__name")
+ )
+ car_by_make = list(qs)
+
+ context["dealer"] = dealer
+ context["staff"] = staff
+ context["total_cars"] = total_cars
+ context["total_reservations"] = total_reservations
+ context["reserved_percentage"] = reserved_percentage
+ context["sold_percentage"] = sold_percentage
+ context["available_cars"] = available_cars
+ context["sold_cars"] = sold_cars
+ context["reserved_cars"] = reserved_cars
+ context["hold_cars"] = hold_cars
+ context["damaged_cars"] = damaged_cars
+ context["transfer_cars"] = transfer_cars
+ context["car"] = json.dumps(car_by_make)
+
+ return context
+
+class AccountantDashboard(LoginRequiredMixin, TemplateView):
+ """
+ SalesDashboard class provides a view for the sales dashboard.
+
+ This class is responsible for generating the context data required to
+ display various statistics and information on the sales dashboard. It
+ inherits from `LoginRequiredMixin` and `TemplateView`, ensuring only
+ authenticated users can access the view. The dashboard includes data
+ such as the total number of cars, reservations, sold percentages,
+ reserved percentages, and cars categorized by various statuses. The
+ purpose of this dashboard is to provide dealership staff an overview
+ of their inventory and related sales metrics.
+
+ :ivar template_name: The name of the HTML template used for rendering
+ the sales dashboard.
+ :type template_name: str
+ """
+
+ template_name = "dashboards/accountant_dashboard.html"
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
@@ -10559,7 +10801,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.ERROR, "Item already uploaded.")
+ messages.add_message(request, messages.SUCCESS, "Item uploaded Sucessfully.")
return redirect(
"view_items_inventory",
dealer_slug=dealer_slug,
diff --git a/templates/dashboards/accountant_dashboard.html b/templates/dashboards/accountant_dashboard.html
new file mode 100644
index 00000000..0bf03d0f
--- /dev/null
+++ b/templates/dashboards/accountant_dashboard.html
@@ -0,0 +1,218 @@
+{% extends 'base.html' %}
+{% block content %}
+
+
+
+
Accountant Dashboard
+
+
+
+
+
+
+
+
+
Total Revenue
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
Gross Profit
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
Total Expense
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
Breakdown by Vehicle Type
+
+
+
+
New Cars
+
$1.25M
+
Total Revenue
+
+
+
+
+
Used Cars
+
$0.75M
+
Total Revenue
+
+
+
+
+
New Cars
+
$0.25M
+
Net Profit
+
+
+
+
+
Used Cars
+
$0.15M
+
Net Profit
+
+
+
+
+
+
+
+
+{% endblock %}
+
+{% block customJS %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/dashboards/dealer_dashbaord.html b/templates/dashboards/dealer_dashbaord.html
new file mode 100644
index 00000000..12753d7f
--- /dev/null
+++ b/templates/dashboards/dealer_dashbaord.html
@@ -0,0 +1,243 @@
+{% extends 'base.html' %}
+{% block content %}
+
+
+
+
Business Health Dashboard
+
+
+
+
+
+
+
+
+
Total Revenue
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
Gross Profit
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
Total Expense
+
$1.25M
+
+
+ -2% from last month
+
+
+
+
+
+
+
+
+
Total VAT Collected
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
Total Cars Sold
+
{{ sold_cars }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
+
+{% endblock content %}
+
+
+{% block customJS %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/dashboards/inventory_dashboard.html b/templates/dashboards/inventory_dashboard.html
new file mode 100644
index 00000000..e5a8cac9
--- /dev/null
+++ b/templates/dashboards/inventory_dashboard.html
@@ -0,0 +1,300 @@
+{% extends 'base.html' %}
+{% block content %}
+
+
+
+
Inventory Dashboard
+
+
+
+
+
+
+
+
+
Total Cars in Inventory
+
{{ total_cars_in_inventory }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
New Cars in Inventory
+
{{ new_cars_in_inventory }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
Used Cars in Inventory
+
{{ old_cars_in_inventory }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
Inventory Value
+
$5.8M
+
+
+ -2% from last month
+
+
+
+
+
+
+
+
+
+
New Car Value
+
$3.2M
+
Total new cars value
+
+
+
+
+
Used Car Value
+
$2.6M
+
Total used cars value
+
+
+
+
+
Average Time on Lot
+
10 days
+
Average for all vehicles
+
+
+
+
+
Aging Inventory
+
12 cars
+
+ -4 cars from last month
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock content %}
+
+{% block customJS %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/dashboards/manager.html b/templates/dashboards/manager.html
deleted file mode 100644
index 962f274b..00000000
--- a/templates/dashboards/manager.html
+++ /dev/null
@@ -1,641 +0,0 @@
-{% extends 'base.html' %}
-{% block content %}
-
-
-
Dashboard Overview
-
-
-
-
-
-
-
-
-
-
-
Total Revenue
-
$1.25M
-
+8% from last month
-
-
-
-
-
-
-
Net Profit
-
$1.25M
-
+8% from last month
-
-
-
-
-
-
-
Gross Profit
-
$1.25M
-
+8% from last month
-
-
-
-
-
-
-
Total Expense
-
$1.25M
-
+8% from last month
-
-
-
-
-
-
-
Total VAT collected
-
$1.25M
-
+8% from last month
-
-
-
-
-
-
-
Cars Sold
-
{{sold_cars}}
-
+5 units from last month
-
-
-
-
-
-
-
Average Time on Lot
-
10 days
-
-
-
-
-
-
-
Inventory Value
-
$5.8M
-
-2% from last month
-
-
-
-
-
-
-
Aging Inventory
-
12
-
-4 cars from last month
-
-
-
-
-
-
-
-
-{% endblock content %}
-
-
-{% block customJS%}
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/templates/dashboards/manager_dashboard.html b/templates/dashboards/manager_dashboard.html
new file mode 100644
index 00000000..0d6c30ad
--- /dev/null
+++ b/templates/dashboards/manager_dashboard.html
@@ -0,0 +1,367 @@
+{% extends 'base.html' %}
+{% block content %}
+
+
+
+
+
+
+
+
+
+
Total Revenue
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
Total Expense
+
$1.25M
+
+
+ +3% from last month
+
+
+
+
+
+
+
+
+
Total Cars Sold
+
{{ sold_cars }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
Avg. Time on Lot
+
10 days
+
+
+ +2 days from last month
+
+
+
+
+
+
+
+
+
Inventory Value
+
$5.8M
+
+
+ -2% from last month
+
+
+
+
+
+
+
+
+
Aging Inventory
+
12 units
+
+
+ -4 cars from last month
+
+
+
+
+
+
+
+
+
Gross Profit
+
$1.25M
+
+
+ +8% from last month
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock content %}
+
+
+{% block customJS %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/dashboards/sales_dashboard.html b/templates/dashboards/sales_dashboard.html
new file mode 100644
index 00000000..211fa9e7
--- /dev/null
+++ b/templates/dashboards/sales_dashboard.html
@@ -0,0 +1,356 @@
+{% extends 'base.html' %}
+{% block content %}
+
+
+
+
Sales & Leads Dashboard
+
+
+
+
+
+
+
+
+
Total Cars Sold
+
{{ sold_cars }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
Used Cars Sold
+
{{ sold_used_cars }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
New Cars Sold
+
{{ sold_new_cars }}
+
+
+ +5 units from last month
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+{% endblock content %}
+
+
+{% block customJS%}
+
+{% endblock %}
\ No newline at end of file
diff --git a/templates/index.html b/templates/index.html
index b91b35d6..c12821e9 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -3,10 +3,16 @@
{% block content %}
{% if request.user.is_authenticated %}
-
-
- {{ _("Select All") }}
-
+
{% for car in cars %}
-
+
@@ -322,68 +311,5 @@
{% endblock %}
- {% block customJS %}
-
- {% endblock customJS %}
+
+
\ No newline at end of file
diff --git a/templates/purchase_orders/car_inventory_item_form.html b/templates/purchase_orders/car_inventory_item_form.html
index 74a5e4bb..727fd542 100644
--- a/templates/purchase_orders/car_inventory_item_form.html
+++ b/templates/purchase_orders/car_inventory_item_form.html
@@ -36,13 +36,13 @@
}
.color-radio:checked + .color-display {
- border: 2px solid #0d6efd;
- box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.25);
+ border: 3px solid rgb(44, 229, 44);
+ box-shadow: 0 0 10px rgba(44, 123, 229, 0.5);
}
.color-radio:focus + .color-display {
- border-color: #86b7fe;
- box-shadow: 0 0 0 3px rgba(13, 110, 253, 0.25);
+ border: 3px solid rgb(44, 229, 44);
+ box-shadow: 0 0 10px rgba(44, 123, 229, 0.5);
}
.color-display {
diff --git a/templates/sales/estimates/estimate_detail.html b/templates/sales/estimates/estimate_detail.html
index 439a2f42..8b9a1f8f 100644
--- a/templates/sales/estimates/estimate_detail.html
+++ b/templates/sales/estimates/estimate_detail.html
@@ -112,7 +112,7 @@
onclick="setFormAction('review')"
data-bs-toggle="modal"
data-bs-target="#confirmModal">
-
{% trans 'Mark As Review' %}
+
{% trans 'Mark As Review' %}
{% endif %}
{% elif estimate.status == 'in_review' %}
@@ -122,45 +122,46 @@
class="btn btn-phoenix-secondary"
data-bs-toggle="modal"
data-bs-target="#confirmModal">
-
{% trans 'Mark As Approved' %}
+
{% trans 'Mark As Approved' %}
{% endif %}
{% if estimate.can_approve and not request.is_manager %}
{% endif %}
{% elif estimate.status == 'approved' %}
{% if perms.django_ledger.change_estimatemodel %}
{% trans 'Send Quotation' %}
+ class="btn btn-phoenix-primary me-2">
{% trans 'Send Quotation' %}
{% endif %}
{% if estimate.sale_orders.first %}
{% if perms.django_ledger.add_invoicemodel %}
{% trans 'Create Invoice' %}
+ class="btn btn-phoenix-primary">
{% trans 'Create Invoice' %}
{% endif %}
{% if perms.inventory.view_saleorder %}
{{ _("Preview Sale Order") }}
+ class="btn btn-phoenix-primary">
{{ _("Preview Sale Order") }}
{% endif %}
{% else %}
{% if perms.inventory.add_saleorder %}
{% trans 'Create Sale Order' %}
+ class="btn btn-phoenix-primary">
{% trans 'Create Sale Order' %}
{% endif %}
{% endif %}
{% elif estimate.status == 'completed' %}
{% if perms.inventory.view_saleorder %}
{{ _("Preview Sale Order") }}
+ class="btn btn-phoenix-primary">
{{ _("Preview Sale Order") }}
{% endif %}
{% if perms.django_ledger.view_invoicemodel %}
- {{ _("View Invoice") }}
+ type="button">
+
{{ _("View Invoice") }}
+
{% endif %}
{% endif %}
{% if estimate.can_cancel %}
@@ -168,7 +169,7 @@
{% endif %}
{% endif %}
@@ -178,31 +179,31 @@
- {% trans 'Quotation Number' %}:
+ {% trans 'Quotation Number' %}:
{{ estimate.estimate_number }}
- {% trans 'Quotation Date' %}:
+ {% trans 'Quotation Date' %}:
{{ estimate.created }}
- {% trans 'Customer' %}:
+ {% trans 'Customer' %}:
{{ estimate.customer.customer_name }}
- {% trans 'Email' %}:
+ {% trans 'Email' %}:
{{ estimate.customer.email }}
- {% trans "Quotation Status" %}:
+ {% trans "Quotation Status" %}:
{% if estimate.status == 'draft' %}
diff --git a/templates/users/user_detail.html b/templates/users/user_detail.html
index a1a980e8..9344e151 100644
--- a/templates/users/user_detail.html
+++ b/templates/users/user_detail.html
@@ -2,108 +2,107 @@
{% load static %}
{% load i18n %}
{% block title %}
- {{ _("View Staff") }}
+ {{ user_.name }}
{% endblock title %}
{% block content %}
-