Merge pull request 'new chnages to the sale and purchase report and chnages to the charts label colors' (#220) from frontend into main

Reviewed-on: #220
This commit is contained in:
ismail 2025-09-02 14:09:13 +03:00
commit f0ab5d1888
9 changed files with 324 additions and 157 deletions

View File

@ -1308,6 +1308,7 @@ urlpatterns = [
views.car_sale_report_view, views.car_sale_report_view,
name="car-sale-report", name="car-sale-report",
), ),
path('<slug:dealer_slug>/car-sale-report/get_filtered_choices/',views.get_filtered_choices,name='get_filtered_choices'),
path('car-sale-report/<slug:dealer_slug>/csv/', views.car_sale_report_csv_export, name='car-sale-report-csv-export'), path('car-sale-report/<slug:dealer_slug>/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/', views.RecallListView.as_view(), name='recall_list'),

View File

@ -11030,55 +11030,76 @@ class InventoryListView(InventoryListViewBase):
permission_required = ["django_ledger.view_purchaseordermodel"] permission_required = ["django_ledger.view_purchaseordermodel"]
@login_required @login_required
def purchase_report_view(request,dealer_slug): def purchase_report_view(request, dealer_slug):
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
pos = request.entity.get_purchase_orders() pos = request.entity.get_purchase_orders()
if start_date_str:
try:
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date()
pos = pos.filter(created__date__gte=start_date)
except (ValueError, TypeError):
pass
if end_date_str:
try:
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
pos = pos.filter(created__date__lte=end_date)
except (ValueError, TypeError):
pass
data = [] data = []
total_po_amount=0 total_po_amount = 0
total_po_cars=0 total_po_cars = 0
for po in pos: 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_amount = sum(item["total"] for item in items)
po_quantity=0 po_quantity = sum(item["q"] for item in items)
for item in items:
po_amount+=item["total"]
po_quantity+=item["q"]
total_po_amount+=po_amount total_po_amount += po_amount
total_po_cars+=po_quantity total_po_cars += po_quantity
bills=po.get_po_bill_queryset()
vendors=set([bill.vendor.vendor_name for bill in bills]) 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" 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") current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
context={ context = {
"dealer":request.entity.name, "dealer": request.entity.name,
"time":current_time, "time": current_time,
"data":data, "data": data,
"total_po_amount":total_po_amount, "total_po_amount": total_po_amount,
"total_po_cars":total_po_cars, "total_po_cars": total_po_cars,
"current_time":current_time "current_time": current_time,
"start_date": start_date_str,
"end_date": end_date_str,
} }
return render(request, 'ledger/reports/purchase_report.html', context)
return render(request,'ledger/reports/purchase_report.html',context) def purchase_report_csv_export(request, dealer_slug):
def purchase_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") current_time = timezone.now().strftime("%Y-%m-%d_%H%M%S")
filename = f"purchase_report_{dealer_slug}_{current_time}.csv" 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) writer = csv.writer(response)
header = [ header = [
'PO Number', 'PO Number',
'Created Date', 'Created Date',
@ -11089,22 +11110,38 @@ def purchase_report_csv_export(request,dealer_slug):
'Vendors' 'Vendors'
] ]
writer.writerow(header) writer.writerow(header)
pos = request.entity.get_purchase_orders() pos = request.entity.get_purchase_orders()
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
if start_date_str:
try:
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date()
pos = pos.filter(created__date__gte=start_date)
except (ValueError, TypeError):
pass
if end_date_str:
try:
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
pos = pos.filter(created__date__lte=end_date)
except (ValueError, TypeError):
pass
for po in pos: for po in pos:
po_amount = 0 po_amount = 0
po_quantity = 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: for item in items:
po_amount += item["total"] po_amount += item["total"]
po_quantity += item["q"] po_quantity += item["q"]
bills = po.get_po_bill_queryset() 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" vendors_str = ", ".join(sorted(list(vendors))) if vendors else "N/A"
writer.writerow([ writer.writerow([
po.po_number, po.po_number,
po.created.strftime("%Y-%m-%d %H:%M:%S") if po.created else '', po.created.strftime("%Y-%m-%d %H:%M:%S") if po.created else '',
@ -11117,22 +11154,22 @@ def purchase_report_csv_export(request,dealer_slug):
return response return response
@login_required @login_required
def car_sale_report_view(request, dealer_slug): def car_sale_report_view(request, dealer_slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug) dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
vat = models.VatRate.objects.filter(dealer=dealer,is_active=True).first() vat = models.VatRate.objects.filter(dealer=dealer, is_active=True).first()
VAT_RATE=vat.rate VAT_RATE = vat.rate if vat else 0
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 # Get filter parameters from the request
selected_make = request.GET.get('make') selected_make = request.GET.get('make')
selected_model = request.GET.get('model') selected_model = request.GET.get('model')
selected_serie = request.GET.get('serie') selected_serie = request.GET.get('serie')
selected_year = request.GET.get('year') selected_year = request.GET.get('year')
selected_stock_type=request.GET.get('stock_type') selected_stock_type = request.GET.get('stock_type')
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
# Apply filters to the queryset # Apply filters to the queryset
if selected_make: if selected_make:
@ -11145,62 +11182,122 @@ def car_sale_report_view(request, dealer_slug):
cars_sold = cars_sold.filter(year=selected_year) cars_sold = cars_sold.filter(year=selected_year)
if selected_stock_type: if selected_stock_type:
cars_sold = cars_sold.filter(stock_type=selected_stock_type) cars_sold = cars_sold.filter(stock_type=selected_stock_type)
# Corrected: Apply date filters using the 'sold_date' field
if start_date_str:
try:
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date()
cars_sold = cars_sold.filter(sold_date__gte=start_date)
except (ValueError, TypeError):
pass
if end_date_str:
try:
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
cars_sold = cars_sold.filter(sold_date__lte=end_date)
except (ValueError, TypeError):
pass
# Calculate summary data for the filtered results
# # Calculate summary data for the filtered results total_cars_sold = cars_sold.count()
total_cars_sold=cars_sold.count()
total_revenue_from_cars = cars_sold.aggregate( total_revenue_from_cars = cars_sold.aggregate(
total=Sum(F('marked_price') - F('discount_amount')) total=Sum(F('marked_price') - F('discount_amount'))
)['total'] or 0 )['total'] or 0
total_vat_on_cars=cars_sold.annotate( total_vat_on_cars = cars_sold.annotate(
final_price=F('marked_price') - F('discount_amount')).aggregate( final_price=F('marked_price') - F('discount_amount')).aggregate(
total=Sum(F('final_price') * VAT_RATE))['total'] or 0 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_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_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_vat_collected = total_vat_on_cars + total_vat_from_additonals
total_revenue_collected=total_revenue_from_cars+total_revenue_from_additonals total_revenue_collected = total_revenue_from_cars + total_revenue_from_additonals
total_discount = sum([car.discount for car in cars_sold])
total_discount = cars_sold.aggregate(total=Sum('discount_amount'))['total'] or 0
current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S") current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S")
# Get distinct values for filter dropdowns # Get distinct makes for the initial dropdown, other dropdowns will be populated via AJAX
base_sold_cars_queryset = models.Car.objects.filter(dealer=dealer, status='sold') 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() makes = base_sold_cars_queryset.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name')
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')
context = { context = {
'cars_sold': cars_sold, 'cars_sold': cars_sold,
'total_cars_sold':total_cars_sold, 'total_cars_sold': total_cars_sold,
'current_time': current_time, 'current_time': current_time,
'dealer': dealer, 'dealer': dealer,
'total_revenue_from_cars': total_revenue_from_cars, 'total_revenue_from_cars': total_revenue_from_cars,
'total_revenue_from_additonals':total_revenue_from_additonals, 'total_revenue_from_additonals': total_revenue_from_additonals,
'total_revenue_collected': total_revenue_collected, 'total_revenue_collected': total_revenue_collected,
'total_vat_on_cars':total_vat_on_cars, 'total_vat_on_cars': total_vat_on_cars,
'total_vat_from_additonals':total_vat_from_additonals, 'total_vat_from_additonals': total_vat_from_additonals,
'total_vat_collected':total_vat_collected, 'total_vat_collected': total_vat_collected,
'total_discount': total_discount, 'total_discount': total_discount,
'makes': makes, 'makes': makes,
'models': models_qs,
'series': series,
'years': years,
'stock_types':stock_types,
'selected_make': selected_make, 'selected_make': selected_make,
'selected_model': selected_model, 'selected_model': selected_model,
'selected_serie': selected_serie, 'selected_serie': selected_serie,
'selected_year': selected_year, 'selected_year': selected_year,
'selected_stock_type':selected_stock_type, 'selected_stock_type': selected_stock_type,
'start_date': start_date_str,
'end_date': end_date_str,
} }
return render(request, 'ledger/reports/car_sale_report.html', context) return render(request, 'ledger/reports/car_sale_report.html', context)
### 2. Updated `get_filtered_choices`
@login_required
def get_filtered_choices(request, dealer_slug):
dealer = get_object_or_404(models.Dealer, slug=dealer_slug)
# Get all filter parameters from the request
selected_make = request.GET.get('make')
selected_model = request.GET.get('model')
selected_serie = request.GET.get('serie')
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
# Start with the base queryset
queryset = models.Car.objects.filter(dealer=dealer, status='sold')
# Apply filters based on what is selected
if selected_make:
queryset = queryset.filter(id_car_make__name=selected_make)
if selected_model:
queryset = queryset.filter(id_car_model__name=selected_model)
if selected_serie:
queryset = queryset.filter(id_car_serie__name=selected_serie)
# Corrected: Apply date filters to the AJAX queryset
if start_date_str:
try:
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date()
queryset = queryset.filter(sold_date__gte=start_date)
except (ValueError, TypeError):
pass
if end_date_str:
try:
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
queryset = queryset.filter(sold_date__lte=end_date)
except (ValueError, TypeError):
pass
data = {
'models': list(queryset.values_list('id_car_model__name', flat=True).distinct().order_by('id_car_model__name')),
'series': list(queryset.values_list('id_car_serie__name', flat=True).distinct().order_by('id_car_serie__name')),
'years': list(queryset.values_list('year', flat=True).distinct().order_by('-year')),
'stock_types': list(queryset.values_list('stock_type', flat=True).distinct().order_by('stock_type'))
}
return JsonResponse(data)
### 3. Updated `car_sale_report_csv_export`
@login_required @login_required
def car_sale_report_csv_export(request, dealer_slug): def car_sale_report_csv_export(request, dealer_slug):
response = HttpResponse(content_type='text/csv') response = HttpResponse(content_type='text/csv')
@ -11229,6 +11326,8 @@ def car_sale_report_csv_export(request, dealer_slug):
selected_serie = request.GET.get('serie') selected_serie = request.GET.get('serie')
selected_year = request.GET.get('year') selected_year = request.GET.get('year')
selected_stock_type = request.GET.get('stock_type') selected_stock_type = request.GET.get('stock_type')
start_date_str = request.GET.get('start_date')
end_date_str = request.GET.get('end_date')
if selected_make: if selected_make:
cars_sold = cars_sold.filter(id_car_make__name=selected_make) cars_sold = cars_sold.filter(id_car_make__name=selected_make)
@ -11240,15 +11339,28 @@ def car_sale_report_csv_export(request, dealer_slug):
cars_sold = cars_sold.filter(year=selected_year) cars_sold = cars_sold.filter(year=selected_year)
if selected_stock_type: if selected_stock_type:
cars_sold = cars_sold.filter(stock_type=selected_stock_type) cars_sold = cars_sold.filter(stock_type=selected_stock_type)
# Corrected: Apply date filters for CSV export
if start_date_str:
try:
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date()
cars_sold = cars_sold.filter(sold_date__gte=start_date)
except (ValueError, TypeError):
pass
if end_date_str:
try:
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
cars_sold = cars_sold.filter(sold_date__lte=end_date)
except (ValueError, TypeError):
pass
# Write the data for the filtered cars # Write the data for the filtered cars
for car in cars_sold: for car in cars_sold:
# Fetching data for the additional services
additional_services = car.get_additional_services() additional_services = car.get_additional_services()
services_total_price = additional_services['total'] services_total_price = additional_services['total']
services_vat_amount = additional_services['services_vat'] services_vat_amount = additional_services['services_vat']
# Checking for the invoice number to avoid errors on cars without one
invoice_number = None invoice_number = None
sold_date = None sold_date = None
if car.invoice: if car.invoice:
@ -11268,18 +11380,17 @@ def car_sale_report_csv_export(request, dealer_slug):
sold_date.strftime("%Y-%m-%d %H:%M:%S") if sold_date else '', sold_date.strftime("%Y-%m-%d %H:%M:%S") if sold_date else '',
car.cost_price, car.cost_price,
car.marked_price, car.marked_price,
car.discount, # Ensure this property returns a number car.discount_amount,
car.final_price, # Selling Price without VAT car.final_price,
car.vat_amount, # VAT on the car car.vat_amount,
services_total_price, # Total services without VAT services_total_price,
services_vat_amount, # VAT on services services_vat_amount,
car.final_price_plus_services_plus_vat, car.final_price_plus_services_plus_vat,
invoice_number, invoice_number,
]) ])
return response return response
@login_required @login_required
# @permission_required('inventory.view_staff') # @permission_required('inventory.view_staff')
def staff_password_reset_view(request, dealer_slug, user_pk): def staff_password_reset_view(request, dealer_slug, user_pk):

Binary file not shown.

View File

@ -13191,20 +13191,14 @@ msgid "Total cars"
msgstr "إجمالي السيارات" msgstr "إجمالي السيارات"
#: templates/dealers/dealer_detail.html:113 #: templates/dealers/dealer_detail.html:113
#, fuzzy
#| msgid "Subscriptions"
msgid "Plan & Subscription" msgid "Plan & Subscription"
msgstr "الاشتراكات" msgstr "الاشتراكات"
#: templates/dealers/dealer_detail.html:125 #: templates/dealers/dealer_detail.html:125
#, fuzzy
#| msgid "Car Details"
msgid "Company Details" msgid "Company Details"
msgstr "تفاصيل السيارة" msgstr "تفاصيل السيارة"
#: templates/dealers/dealer_detail.html:137 #: templates/dealers/dealer_detail.html:137
#, fuzzy
#| msgid "Car Transfer"
msgid "Car Brands" msgid "Car Brands"
msgstr "نقل السيارة" msgstr "نقل السيارة"
@ -13252,8 +13246,6 @@ msgid "Upgrade Plan"
msgstr "ترقية الخطة" msgstr "ترقية الخطة"
#: templates/dealers/dealer_detail.html:212 #: templates/dealers/dealer_detail.html:212
#, fuzzy
#| msgid "Manage Groups & Permissions"
msgid "Manage Users & Cars" msgid "Manage Users & Cars"
msgstr "إدارة المجموعات والأذونات" msgstr "إدارة المجموعات والأذونات"
@ -13269,20 +13261,14 @@ msgid "Contact support to increase your limits"
msgstr "" msgstr ""
#: templates/dealers/dealer_detail.html:258 #: templates/dealers/dealer_detail.html:258
#, fuzzy
#| msgid "Client Information"
msgid "Contact Information" msgid "Contact Information"
msgstr "معلومات العميل" msgstr "معلومات العميل"
#: templates/dealers/dealer_detail.html:286 #: templates/dealers/dealer_detail.html:286
#, fuzzy
#| msgid "User Information"
msgid "VAT Information" msgid "VAT Information"
msgstr "معلومات المستخدم" msgstr "معلومات المستخدم"
#: templates/dealers/dealer_detail.html:292 #: templates/dealers/dealer_detail.html:292
#, fuzzy
#| msgid "Updated At"
msgid "Update VAT" msgid "Update VAT"
msgstr "تم التحديث" msgstr "تم التحديث"
@ -13291,8 +13277,6 @@ msgid "Makes you are selling"
msgstr "الماركات التي تبيعها" msgstr "الماركات التي تبيعها"
#: templates/dealers/dealer_detail.html:321 #: templates/dealers/dealer_detail.html:321
#, fuzzy
#| msgid "No staff member selected."
msgid "No car makes selected." msgid "No car makes selected."
msgstr "لم يتم اختيار أي عضو من الفريق." msgstr "لم يتم اختيار أي عضو من الفريق."

View File

@ -122,7 +122,7 @@
data: {{ monthly_cars_sold_json|safe }}, data: {{ monthly_cars_sold_json|safe }},
backgroundColor: primaryColor, backgroundColor: primaryColor,
borderColor: primaryColor, borderColor: primaryColor,
borderWidth: 1 borderWidth: 1,
}] }]
}, },
options: { options: {
@ -193,7 +193,7 @@
plugins: { plugins: {
legend: { legend: {
display: true, display: true,
labels: { color: '#495057', boxWidth: 20 } labels: { boxWidth: 20 }
}, },
tooltip: { tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)', backgroundColor: 'rgba(33, 37, 41, 0.9)',
@ -250,7 +250,7 @@
plugins: { plugins: {
legend: { legend: {
position: 'right', position: 'right',
labels: { color: '#343a40', font: { size: 14 } } labels: { font: { size: 14 } }
}, },
tooltip: { tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)', backgroundColor: 'rgba(33, 37, 41, 0.9)',
@ -349,7 +349,7 @@
plugins: { plugins: {
legend: { legend: {
position: 'right', position: 'right',
labels: { color: '#343a40', font: { size: 14 } } labels: { font: { size: 14 } }
}, },
tooltip: { tooltip: {
backgroundColor: 'rgba(33, 37, 41, 0.9)', backgroundColor: 'rgba(33, 37, 41, 0.9)',

View File

@ -3,7 +3,7 @@
{% load static %} {% load static %}
{% load tenhal_tag %} {% load tenhal_tag %}
{% block title %} {% block title %}
{{ _("Car Sale Report") |capfirst }} {{ _("Car Sale Report")|capfirst }}
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<style> <style>
@ -15,7 +15,7 @@
} }
.summary-card:hover { .summary-card:hover {
transform: translateY(-5px); transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0,0,0,0.1); box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
} }
.summary-card .card-body { .summary-card .card-body {
display: flex; display: flex;
@ -32,9 +32,7 @@
.summary-card .card-text { .summary-card .card-text {
font-size: 2.25rem; font-size: 2.25rem;
font-weight: 700; font-weight: 700;
} }
</style> </style>
<div class="container-fluid report-container"> <div class="container-fluid report-container">
<header class="report-header text-center"> <header class="report-header text-center">
@ -47,11 +45,19 @@
<p class="text-muted">{% trans 'Report Date' %}: {{ current_time }}</p> <p class="text-muted">{% trans 'Report Date' %}: {{ current_time }}</p>
</header> </header>
<main> <main>
<section id="filters" class="mb-5 p-4 rounded border border-primary"> <section id="filters" class="mb-5 p-4 rounded border border-primary">
<h2 class="section-heading mb-4"> <h2 class="section-heading mb-4">
{% trans 'Filters' %} <i class="fas fa-sliders-h ms-2"></i> {% trans 'Filters' %} <i class="fas fa-sliders-h ms-2"></i>
</h2> </h2>
<form method="GET" class="row g-3 align-items-end"> <form method="GET" class="row g-3 align-items-end" data-filter-url="{% url 'get_filtered_choices' dealer_slug=dealer.slug %}">
<div class="col-md-6">
<label for="start_date" class="form-label">{% trans 'Start Date' %}</label>
<input type="date" class="form-control" id="start_date" name="start_date" value="{{ start_date }}">
</div>
<div class="col-md-6">
<label for="end_date" class="form-label">{% trans 'End Date' %}</label>
<input type="date" class="form-control" id="end_date" name="end_date" value="{{ end_date }}">
</div>
<div class="col-md-2"> <div class="col-md-2">
<label for="make-select" class="form-label">{% trans 'Make' %}</label> <label for="make-select" class="form-label">{% trans 'Make' %}</label>
<select id="make-select" name="make" class="form-select"> <select id="make-select" name="make" class="form-select">
@ -65,44 +71,36 @@
<label for="model-select" class="form-label">{% trans 'Model' %}</label> <label for="model-select" class="form-label">{% trans 'Model' %}</label>
<select id="model-select" name="model" class="form-select"> <select id="model-select" name="model" class="form-select">
<option value="">{% trans 'All Models' %}</option> <option value="">{% trans 'All Models' %}</option>
{% for model in models %} {% if selected_model %}
<option value="{{ model }}" <option value="{{ selected_model }}" selected>{{ selected_model }}</option>
{% if model == selected_model %}selected{% endif %}>{{ model }}</option> {% endif %}
{% endfor %}
</select> </select>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<label for="serie-select" class="form-label">{% trans 'Serie' %}</label> <label for="serie-select" class="form-label">{% trans 'Serie' %}</label>
<select id="serie-select" name="serie" class="form-select"> <select id="serie-select" name="serie" class="form-select">
<option value="">{% trans 'All Series' %}</option> <option value="">{% trans 'All Series' %}</option>
{% for serie in series %} {% if selected_serie %}
<option value="{{ serie }}" <option value="{{ selected_serie }}" selected>{{ selected_serie }}</option>
{% if serie == selected_serie %}selected{% endif %}>{{ serie }}</option> {% endif %}
{% endfor %}
</select> </select>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<label for="year-select" class="form-label">{% trans 'Year' %}</label> <label for="year-select" class="form-label">{% trans 'Year' %}</label>
<select id="year-select" name="year" class="form-select"> <select id="year-select" name="year" class="form-select">
<option value="">{% trans 'All Years' %}</option> <option value="">{% trans 'All Years' %}</option>
{% for year in years %} {% if selected_year %}
<option value="{{ year }}" <option value="{{ selected_year }}" selected>{{ selected_year }}</option>
{% if year|stringformat:"s" == selected_year %}selected{% endif %}> {% endif %}
{{ year }}
</option>
{% endfor %}
</select> </select>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
<label for="stock_type-select" class="form-label">{% trans 'Stock Types' %}</label> <label for="stock_type-select" class="form-label">{% trans 'Stock Types' %}</label>
<select id="stock_type-select" name="stock_type" class="form-select"> <select id="stock_type-select" name="stock_type" class="form-select">
<option value="">{% trans 'Stock Types' %}</option> <option value="">{% trans 'All Stock Types' %}</option>
{% for stock_type in stock_types %} {% if selected_stock_type %}
<option value="{{ stock_type }}" <option value="{{ selected_stock_type }}" selected>{{ selected_stock_type }}</option>
{% if stock_type == selected_stock_type %}selected{% endif %}> {% endif %}
{{ stock_type }}
</option>
{% endfor %}
</select> </select>
</div> </div>
<div class="col-md-2"> <div class="col-md-2">
@ -111,27 +109,8 @@
</button> </button>
</div> </div>
</form> </form>
</section> </section>
<!---->
{% comment %} 'cars_sold': 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,
'selected_make': selected_make,
'selected_model': selected_model,
'selected_serie': selected_serie,
'selected_year': selected_year, {% endcomment %}
<!---->
<section id="summary" class="mb-5"> <section id="summary" class="mb-5">
<h2 class="section-heading mb-4 border-start border-5 border-primary p-2">{% trans 'Report Summary' %}</h2> <h2 class="section-heading mb-4 border-start border-5 border-primary p-2">{% trans 'Report Summary' %}</h2>
<div class="row g-4"> <div class="row g-4">
@ -166,7 +145,7 @@
<span>{% trans 'Total Revenue' %}<span class="icon-saudi_riyal"></span></span> <span>{% trans 'Total Revenue' %}<span class="icon-saudi_riyal"></span></span>
</h5> </h5>
<p class="card-text"> <p class="card-text">
<span>{{ total_revenue_collected |floatformat:2 }} <span class="icon-saudi_riyal"></span></span> <span>{{ total_revenue_collected|floatformat:2 }} <span class="icon-saudi_riyal"></span></span>
</p> </p>
</div> </div>
</div> </div>
@ -302,14 +281,82 @@
<span>{{ car.final_price_plus_services_plus_vat|floatformat:2 }}<span class="icon-saudi_riyal"></span></span> <span>{{ car.final_price_plus_services_plus_vat|floatformat:2 }}<span class="icon-saudi_riyal"></span></span>
</td> </td>
<td class="fs-9"> <td class="fs-9">
<span>{{ car.invoice.invoice_number }} <span>{{ car.invoice.invoice_number }}</span>
</td> </td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</div> </div>
</section> </section>
</main> </main>
</div> </div>
{% endblock %}
<script>
document.addEventListener('DOMContentLoaded', () => {
const makeSelect = document.getElementById('make-select');
const modelSelect = document.getElementById('model-select');
const serieSelect = document.getElementById('serie-select');
const yearSelect = document.getElementById('year-select');
const stockTypeSelect = document.getElementById('stock_type-select');
const startDateInput = document.getElementById('start_date');
const endDateInput = document.getElementById('end_date');
const form = document.querySelector('form');
const baseUrl = form.dataset.filterUrl;
function updateOptions(selectElement, options, currentValue) {
const name = selectElement.name.replace(/_/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
selectElement.innerHTML = `<option value="">All ${name}s</option>`;
options.forEach(option => {
const newOption = document.createElement('option');
newOption.value = option;
newOption.textContent = option;
if (String(option) === String(currentValue)) {
newOption.selected = true;
}
selectElement.appendChild(newOption);
});
}
function fetchAndUpdateDropdowns() {
const make = makeSelect.value;
const model = modelSelect.value;
const serie = serieSelect.value;
const start_date = startDateInput.value;
const end_date = endDateInput.value;
// Corrected: Include start_date and end_date in the AJAX URL
const url = `${baseUrl}?make=${make}&model=${model}&serie=${serie}&start_date=${start_date}&end_date=${end_date}`;
fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(data => {
updateOptions(modelSelect, data.models, model);
updateOptions(serieSelect, data.series, serie);
updateOptions(yearSelect, data.years, yearSelect.value);
updateOptions(stockTypeSelect, data.stock_types, stockTypeSelect.value);
})
.catch(error => console.error('Error fetching filtered choices:', error));
}
// Event listeners for all filter elements
makeSelect.addEventListener('change', fetchAndUpdateDropdowns);
modelSelect.addEventListener('change', fetchAndUpdateDropdowns);
serieSelect.addEventListener('change', fetchAndUpdateDropdowns);
yearSelect.addEventListener('change', fetchAndUpdateDropdowns);
stockTypeSelect.addEventListener('change', fetchAndUpdateDropdowns);
startDateInput.addEventListener('change', fetchAndUpdateDropdowns);
endDateInput.addEventListener('change', fetchAndUpdateDropdowns);
// Initial call to populate other dropdowns on page load
fetchAndUpdateDropdowns();
});
</script>
{% endblock %}

View File

@ -2,7 +2,7 @@
{% load i18n %} {% load i18n %}
{% load static %} {% load static %}
{% block title %} {% block title %}
{{ _("Car Purchase Report") |capfirst }} {{ _("Car Purchase Report")|capfirst }}
{% endblock title %} {% endblock title %}
{% block content %} {% block content %}
<style> <style>
@ -45,6 +45,26 @@
</p> </p>
<p class="text-muted">{% trans "Report Date" %}: {{ current_time }}</p> <p class="text-muted">{% trans "Report Date" %}: {{ current_time }}</p>
</header> </header>
<section id="filters" class="mb-5 p-4 rounded border border-primary">
<h2 class="section-heading mb-4">
{% trans 'Filters' %} <i class="fas fa-sliders-h ms-2"></i>
</h2>
<form method="GET" class="row g-3 align-items-end">
<div class="col-md-3">
<label for="start_date" class="form-label">{% trans 'Start Date' %}</label>
<input type="date" class="form-control" id="start_date" name="start_date" value="{{ start_date|default_if_none:'' }}">
</div>
<div class="col-md-3">
<label for="end_date" class="form-label">{% trans 'End Date' %}</label>
<input type="date" class="form-control" id="end_date" name="end_date" value="{{ end_date|default_if_none:'' }}">
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-filter me-2"></i>{% trans 'Filter' %}
</button>
</div>
</form>
</section>
<main> <main>
<section id="summary" class="mb-5"> <section id="summary" class="mb-5">
<h2 class="section-heading mb-4 border-start border-3 border-primary p-2">{% trans 'Report Summary' %}</h2> <h2 class="section-heading mb-4 border-start border-3 border-primary p-2">{% trans 'Report Summary' %}</h2>
@ -84,7 +104,7 @@
<section id="purchase-details" class="mb-3"> <section id="purchase-details" class="mb-3">
<h2 class="section-heading border-start border-3 border-primary p-2">{% trans 'Detailed Purchase List' %}</h2> <h2 class="section-heading border-start border-3 border-primary p-2">{% trans 'Detailed Purchase List' %}</h2>
<div class="d-flex justify-content-end mb-3 d-print-none"> <div class="d-flex justify-content-end mb-3 d-print-none">
<a href="{% url 'purchase-report-csv-export' request.dealer.slug %}" <a href="{% url 'purchase-report-csv-export' request.dealer.slug %}?start_date={{ start_date|default_if_none:'' }}&end_date={{ end_date|default_if_none:'' }}"
class="btn btn-phoenix-primary"> class="btn btn-phoenix-primary">
<i class="bi bi-download me-2"></i>{% trans 'Download as CSV' %} <i class="bi bi-download me-2"></i>{% trans 'Download as CSV' %}
</a> </a>
@ -126,4 +146,4 @@
</section> </section>
</main> </main>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -122,7 +122,7 @@
<div class="row d-flex justify-content-center align-items-center mt-5 mb-3 ms-6 ps-3"> <div class="row d-flex justify-content-center align-items-center mt-5 mb-3 ms-6 ps-3">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
{% if not items.car %} {% if not items %}
{% url "car_add" request.dealer.slug as create_car_url %} {% url "car_add" request.dealer.slug as create_car_url %}
{% include "message-illustration.html" with value1="Please add at least one car or complete the car info before creating a quotation." value2="Add car" message_image="images/logos/no-content-new.jpg" url=create_car_url %} {% include "message-illustration.html" with value1="Please add at least one car or complete the car info before creating a quotation." value2="Add car" message_image="images/logos/no-content-new.jpg" url=create_car_url %}
{% endif %} {% endif %}

View File

@ -128,7 +128,9 @@
<h2 class="mb-7">{{ _("Pricing") }}</h2> <h2 class="mb-7">{{ _("Pricing") }}</h2>
<div class="row g-3 mb-7 mb-lg-11"> <div class="row g-3 mb-7 mb-lg-11">
{% for plan in plan_list %} {% for plan in plan_list %}
<div class="col-lg-3">
<div class="col-lg-4" onclick="window.location='{% url "account_signup" %}';">
<input type="radio" <input type="radio"
class="btn-check" class="btn-check"
name="selected_plan" name="selected_plan"
@ -156,7 +158,9 @@
</div> </div>
</div> </div>
</label> </label>
</div> </div>
{% endfor %} {% endfor %}
</div> </div>
</div> </div>