trace for the my way of writing the dashboard logic
This commit is contained in:
parent
0dcc7d56a3
commit
d0ac8eba9a
@ -602,7 +602,7 @@ def update_car_status_on_reservation_create(sender, instance, created, **kwargs)
|
|||||||
"""
|
"""
|
||||||
Signal handler to update the status of a car upon the creation of a car reservation.
|
Signal handler to update the status of a car upon the creation of a car reservation.
|
||||||
This function is triggered when a new instance of a CarReservation is created and saved
|
This function is triggered when a new instance of a CarReservation is created and saved
|
||||||
to the database. It modifies the status of the associated car to reflect the RESERVED status.
|
to the database. It modifies the status of the associated car to reflect the D status.
|
||||||
|
|
||||||
:param sender: The model class that sends the signal (CarReservation).
|
:param sender: The model class that sends the signal (CarReservation).
|
||||||
:param instance: The specific instance of the CarReservation that triggered the signal.
|
:param instance: The specific instance of the CarReservation that triggered the signal.
|
||||||
|
|||||||
@ -495,14 +495,22 @@ class TestView(TemplateView):
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
from django.db.models import Sum, F, Count
|
||||||
|
from django.db.models.functions import ExtractMonth
|
||||||
|
from django.utils import timezone
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
def DealerDashboard(request):
|
def DealerDashboard(request):
|
||||||
dealer = request.dealer
|
dealer = request.dealer
|
||||||
|
|
||||||
|
# This part is correct as is, it filters cars by dealer and sold status
|
||||||
cars_sold = models.Car.objects.filter(dealer=dealer, status='sold')
|
cars_sold = models.Car.objects.filter(dealer=dealer, status='sold')
|
||||||
|
|
||||||
today_local = timezone.localdate()
|
today_local = timezone.localdate()
|
||||||
start_date_str = request.GET.get('start_date')
|
start_date_str = request.GET.get('start_date')
|
||||||
end_date_str = request.GET.get('end_date')
|
end_date_str = request.GET.get('end_date')
|
||||||
|
|
||||||
|
# This part needs to be made timezone-aware and robust
|
||||||
if not start_date_str:
|
if not start_date_str:
|
||||||
start_date = today_local - timedelta(days=30)
|
start_date = today_local - timedelta(days=30)
|
||||||
else:
|
else:
|
||||||
@ -513,41 +521,143 @@ def DealerDashboard(request):
|
|||||||
else:
|
else:
|
||||||
end_date = timezone.datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
end_date = timezone.datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
||||||
|
|
||||||
|
# Existing inventory calculations
|
||||||
|
active_cars = models.Car.objects.filter(status__in=['available', 'reserved', 'damaged'])
|
||||||
|
total_cars_in_inventory = active_cars.count()
|
||||||
|
new_cars_qs= active_cars.filter(stock_type='new')
|
||||||
|
used_cars_qs=active_cars.filter(stock_type='old')
|
||||||
|
total_new_cars_in_inventory = new_cars_qs.count()
|
||||||
|
total_used_cars_in_inventory = used_cars_qs.count()
|
||||||
|
total_inventory_value = sum([car.cost_price for car in active_cars])
|
||||||
|
new_car_value=sum([car.cost_price for car in new_cars_qs])
|
||||||
|
used_car_value=sum([car.cost_price for car in used_cars_qs])
|
||||||
|
# Define the aging threshold (e.g., 90 days)
|
||||||
|
aging_threshold_days = 60
|
||||||
|
# Efficiently query the database for aging cars
|
||||||
|
aging_inventory = active_cars.filter(created_at__lte=today_local - timedelta(days=aging_threshold_days))
|
||||||
|
# You can get the count for display on the dashboard
|
||||||
|
aging_inventory_count = aging_inventory.count()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# The database query will automatically be handled in a timezone-aware manner
|
# The database query will automatically be handled in a timezone-aware manner
|
||||||
cars_sold_filtered = cars_sold.filter(
|
cars_sold_filtered = cars_sold.filter(
|
||||||
sold_date__date__gte=start_date,
|
sold_date__date__gte=start_date,
|
||||||
sold_date__date__lte=end_date
|
sold_date__date__lte=end_date
|
||||||
)
|
)
|
||||||
print(cars_sold_filtered)
|
|
||||||
# # Calculate summary data for the filtered results
|
# Calculate summary data for the filtered results
|
||||||
total_cars_sold=cars_sold_filtered.count()
|
total_cars_sold = cars_sold_filtered.count()
|
||||||
print(total_cars_sold)
|
new_cars_sold=cars_sold_filtered.filter(stock_type='new')
|
||||||
total_revenue_from_cars = sum([ car.final_price for car in cars_sold_filtered])
|
used_cars_sold=cars_sold_filtered.filter(stock_type='used')
|
||||||
total_cost_of_cars_sold=sum([car.cost_price for car in cars_sold_filtered])
|
total_new_cars_sold=new_cars_sold.count()
|
||||||
net_profit_from_cars=total_revenue_from_cars-total_cost_of_cars_sold
|
total_used_cars_sold=used_cars_sold.count()
|
||||||
total_vat_collected_from_cars=sum([car.vat_amount for car in cars_sold_filtered])
|
|
||||||
total_revenue_from_services=sum([car.get_additional_services()['total'] for car in cars_sold_filtered])
|
# Calculations for all cars
|
||||||
total_vat_collected_from_services=sum([car.get_additional_services()['services_vat'] for car in cars_sold_filtered])
|
total_cost_of_cars_sold = sum([car.cost_price for car in cars_sold_filtered])
|
||||||
total_vat_collected = total_vat_collected_from_cars+total_vat_collected_from_services
|
total_revenue_from_cars = sum([car.final_price for car in cars_sold_filtered])
|
||||||
total_revenue_generated=total_revenue_from_cars+total_revenue_from_services
|
net_profit_from_cars = total_revenue_from_cars - total_cost_of_cars_sold
|
||||||
|
total_vat_collected_from_cars = sum([car.vat_amount for car in cars_sold_filtered])
|
||||||
|
|
||||||
|
# Calculations specifically for used cars
|
||||||
|
total_cost_of_used_cars_sold = sum([car.cost_price for car in used_cars_sold])
|
||||||
|
total_revenue_from_used_cars = sum([car.final_price for car in used_cars_sold])
|
||||||
|
net_profit_from_used_cars = total_revenue_from_used_cars - total_cost_of_used_cars_sold
|
||||||
|
total_vat_collected_from_used_cars = sum([car.vat_amount for car in used_cars_sold])
|
||||||
|
|
||||||
|
# Calculations specifically for new cars
|
||||||
|
total_cost_of_new_cars_sold = sum([car.cost_price for car in new_cars_sold])
|
||||||
|
total_revenue_from_new_cars = sum([car.final_price for car in new_cars_sold])
|
||||||
|
net_profit_from_new_cars = total_revenue_from_new_cars - total_cost_of_new_cars_sold
|
||||||
|
total_vat_collected_from_new_cars = sum([car.vat_amount for car in new_cars_sold])
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
total_discount = sum([car.discount for car in cars_sold_filtered])
|
total_discount = sum([car.discount for car in cars_sold_filtered])
|
||||||
|
|
||||||
|
# Filter for expenses
|
||||||
|
expenses = ItemModel.objects.filter(entity__admin__dealer=dealer, item_role='expense')
|
||||||
|
total_expenses = sum([expense.default_amount for expense in expenses])
|
||||||
|
|
||||||
|
gross_profit = net_profit_from_cars - total_expenses
|
||||||
|
|
||||||
|
# ----------------------------------------------------
|
||||||
|
# NEW LOGIC FOR CHARTS - Aggregating data by month
|
||||||
|
# ----------------------------------------------------
|
||||||
|
|
||||||
|
# Group cars sold by month and aggregate totals
|
||||||
|
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')), # Corrected for marked price and discount_amount
|
||||||
|
total_profit=Sum(F('marked_price') - F('discount_amount') - F('cost_price')) # Corrected profit calculation
|
||||||
|
).order_by('month')
|
||||||
|
|
||||||
|
# Initialize lists for chart data
|
||||||
|
monthly_cars_sold = [0] * 12
|
||||||
|
monthly_revenue = [0] * 12
|
||||||
|
monthly_net_profit = [0] * 12
|
||||||
|
|
||||||
|
# Populate the lists from the queryset results
|
||||||
|
for data in monthly_sales_data:
|
||||||
|
month_index = data['month'] - 1 # Months are 1-12, so we need to adjust to 0-11
|
||||||
|
monthly_cars_sold[month_index] = data['total_cars']
|
||||||
|
monthly_revenue[month_index] = data['total_revenue']
|
||||||
|
monthly_net_profit[month_index] = data['total_profit']
|
||||||
|
# the monthly revenue is of this form: [0, 0, 0, 0, 0, 0, 0, Decimal('21000'), 0, 0, 0, 0] so javascript cannot recognise Decimale('21000') as no.
|
||||||
|
|
||||||
|
|
||||||
|
# NEW: Convert the lists to JSON strings
|
||||||
|
monthly_cars_sold_json = json.dumps(monthly_cars_sold)
|
||||||
|
monthly_revenue_json = json.dumps([float(x) for x in monthly_revenue]) # Convert Decimal to float
|
||||||
|
monthly_net_profit_json = json.dumps([float(x) for x in monthly_net_profit])
|
||||||
|
|
||||||
|
# Update the context dictionary with the new monthly data lists
|
||||||
context = {
|
context = {
|
||||||
'start_date': start_date,
|
'start_date': start_date,
|
||||||
'end_date': end_date,
|
'end_date': end_date,
|
||||||
'cars_sold': cars_sold_filtered,
|
'cars_sold': cars_sold_filtered,
|
||||||
'total_cars_sold':total_cars_sold,
|
'total_cars_sold': total_cars_sold,
|
||||||
'total_cost_of_cars_sold':total_cost_of_cars_sold,
|
'total_cost_of_cars_sold': total_cost_of_cars_sold,
|
||||||
'total_revenue_from_cars':total_revenue_from_cars,
|
'total_revenue_from_cars': total_revenue_from_cars,
|
||||||
'net_profit_from_cars':net_profit_from_cars,
|
'net_profit_from_cars': net_profit_from_cars,
|
||||||
'total_vat_collected_from_cars': total_vat_collected_from_cars,
|
'total_vat_collected_from_cars': total_vat_collected_from_cars,
|
||||||
'total_discount_on_cars':total_discount,
|
'total_discount_on_cars': total_discount,
|
||||||
'total_revenue_from_services':total_revenue_from_services,
|
'total_revenue_from_services': total_revenue_from_services,
|
||||||
'total_vat_collected_from_services': total_vat_collected_from_services,
|
'total_vat_collected_from_services': total_vat_collected_from_services,
|
||||||
'total_revenue_generated': total_revenue_generated,
|
'total_revenue_generated': total_revenue_generated,
|
||||||
'total_vat_collected':total_vat_collected,
|
'total_vat_collected': total_vat_collected,
|
||||||
|
'total_expenses': total_expenses,
|
||||||
|
"gross_profit": gross_profit,
|
||||||
|
'total_cars_in_inventory': total_cars_in_inventory,
|
||||||
|
'total_used_cars_in_inventory': total_used_cars_in_inventory,
|
||||||
|
'total_new_cars_in_inventory': total_new_cars_in_inventory,
|
||||||
|
'total_inventory_value': total_inventory_value,
|
||||||
|
'new_car_value':new_car_value,
|
||||||
|
'used_car_value':used_car_value,
|
||||||
|
'aging_inventory_count':aging_inventory_count,
|
||||||
|
'total_new_cars_sold':total_new_cars_sold,
|
||||||
|
'total_used_cars_sold':total_used_cars_sold,
|
||||||
|
'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_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,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# New variables for charts
|
||||||
|
'monthly_cars_sold_json': monthly_cars_sold_json,
|
||||||
|
'monthly_revenue_json': monthly_revenue_json,
|
||||||
|
'monthly_net_profit_json': monthly_net_profit_json,
|
||||||
}
|
}
|
||||||
|
|
||||||
return render(request, 'dashboards/dealer_dashboard.html', context)
|
return render(request, 'dashboards/dealer_dashboard.html', context)
|
||||||
@ -580,76 +690,6 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
|
|||||||
# )
|
# )
|
||||||
# return super().dispatch(request, *args, **kwargs)
|
# 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 = 0 # models.CarFinance.objects.filter(car__dealer=dealer).aggregate( #TODO:update_finance
|
|
||||||
# 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
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -152,8 +152,8 @@
|
|||||||
<div class="card h-100 shadow-sm border-0">
|
<div class="card h-100 shadow-sm border-0">
|
||||||
<div class="card-body d-flex flex-column justify-content-between p-4">
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-uppercase text-muted fw-bold small mb-1">Total Expense</p>
|
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total Expense" %}</p>
|
||||||
<h4 class="fw-bolder text-danger mb-3">$1.25M</h4>
|
<h4 class="fw-bolder text-danger mb-3">{{total_expenses|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge bg-danger-subtle text-danger fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
<span class="badge bg-danger-subtle text-danger fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
-2% from last month
|
-2% from last month
|
||||||
@ -165,8 +165,8 @@
|
|||||||
<div class="card h-100 shadow-sm border-0">
|
<div class="card h-100 shadow-sm border-0">
|
||||||
<div class="card-body d-flex flex-column justify-content-between p-4">
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-uppercase text-muted fw-bold small mb-1">Gross Profit</p>
|
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Gross Profit" %}</p>
|
||||||
<h4 class="fw-bolder text-info mb-3">$1.25M</h4>
|
<h4 class="fw-bolder text-info mb-3">{{gross_profit|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
+8% from last month
|
+8% from last month
|
||||||
@ -179,7 +179,7 @@
|
|||||||
<div class="card h-100 shadow-sm border-0">
|
<div class="card h-100 shadow-sm border-0">
|
||||||
<div class="card-body d-flex flex-column justify-content-between p-4">
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
<div>
|
<div>
|
||||||
<p class="text-uppercase text-muted fw-bold small mb-1">Total VAT Collected</p>
|
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans "Total VAT Collected" %}</p>
|
||||||
<h4 class="fw-bolder text-primary mb-3">{{total_vat_collected|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
|
<h4 class="fw-bolder text-primary mb-3">{{total_vat_collected|floatformat:2}}<span class="icon-saudi_riyal"></span></h4>
|
||||||
</div>
|
</div>
|
||||||
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
@ -201,8 +201,135 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">New Cars Sold</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ total_cars_sold }}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+5 units from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">Used Cars Sold</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ total_cars_sold }}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+5 units from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">{% trans 'Inventory Value '%}</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ total_inventory_value|floatformat:2 }}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+8% from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">Total Cars in Inventory</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ total_cars_in_inventory }}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+5 units from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">Total Old Cars in Inventory</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ total_old_cars_in_inventory}}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+5 units from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">Total New Cars in Inventory</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ total_new_cars_in_inventory }}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+5 units from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">New Cars Value</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ new_car_value }}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+8% from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">Old Cars Value</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ old_car_value }}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+8% from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||||
|
<div class="card h-100 shadow-sm border-0">
|
||||||
|
<div class="card-body d-flex flex-column justify-content-between p-4">
|
||||||
|
<div>
|
||||||
|
<p class="text-uppercase text-muted fw-bold small mb-1">Aging Inventory</p>
|
||||||
|
<h4 class="fw-bolder text-success mb-3">{{ aging_inventory_count}}</h4>
|
||||||
|
</div>
|
||||||
|
<span class="badge bg-success-subtle text-success fw-bold p-2 rounded-pill d-inline-flex align-self-start">
|
||||||
|
+5 units from last month
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!--charts-->
|
||||||
<div class="row g-4">
|
<div class="row g-4">
|
||||||
<div class="col-lg-8">
|
<div class="col-lg-8">
|
||||||
<div class="card h-100 shadow-sm border-0">
|
<div class="card h-100 shadow-sm border-0">
|
||||||
@ -225,6 +352,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||||
@ -247,7 +375,7 @@
|
|||||||
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
labels: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'Total Cars Sold',
|
label: 'Total Cars Sold',
|
||||||
data: [2, 3, 10, 4, 30, 12, 8, 9, 20, 12, 15, 35],
|
data: {{monthly_cars_sold_json|safe}},
|
||||||
backgroundColor: primaryColor,
|
backgroundColor: primaryColor,
|
||||||
borderColor: primaryColor,
|
borderColor: primaryColor,
|
||||||
borderWidth: 1
|
borderWidth: 1
|
||||||
@ -282,7 +410,7 @@
|
|||||||
datasets: [
|
datasets: [
|
||||||
{
|
{
|
||||||
label: 'Monthly Revenue',
|
label: 'Monthly Revenue',
|
||||||
data: [120000, 150000, 130000, 180000, 200000, 175000, 190000, 220000, 210000, 250000, 240000, 280000],
|
data: {{monthly_revenue_json|safe}},
|
||||||
borderColor: primaryColor,
|
borderColor: primaryColor,
|
||||||
backgroundColor: 'rgba(114, 73, 182, 0.1)', // Using primaryColor with transparency
|
backgroundColor: 'rgba(114, 73, 182, 0.1)', // Using primaryColor with transparency
|
||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
@ -293,7 +421,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'Monthly Net Profit',
|
label: 'Monthly Net Profit',
|
||||||
data: [25000, 35000, 28000, 40000, 45000, 38000, 42000, 50000, 48000, 55000, 52000, 60000],
|
data: {{monthly_net_profit_json|safe}},
|
||||||
borderColor: successColor,
|
borderColor: successColor,
|
||||||
backgroundColor: 'rgba(0, 208, 116, 0.1)', // Using successColor with transparency
|
backgroundColor: 'rgba(0, 208, 116, 0.1)', // Using successColor with transparency
|
||||||
tension: 0.4,
|
tension: 0.4,
|
||||||
@ -324,7 +452,7 @@
|
|||||||
label += ': ';
|
label += ': ';
|
||||||
}
|
}
|
||||||
if (context.parsed.y !== null) {
|
if (context.parsed.y !== null) {
|
||||||
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }).format(context.parsed.y);
|
label += new Intl.NumberFormat('en-US', { style: 'currency', currency: 'SAR' }).format(context.parsed.y);
|
||||||
}
|
}
|
||||||
return label;
|
return label;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user