diff --git a/inventory/urls.py b/inventory/urls.py index edc968a1..99f49ec0 100644 --- a/inventory/urls.py +++ b/inventory/urls.py @@ -1308,6 +1308,7 @@ urlpatterns = [ views.car_sale_report_view, name="car-sale-report", ), + path('/car-sale-report/get_filtered_choices/',views.get_filtered_choices,name='get_filtered_choices'), 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'), diff --git a/inventory/views.py b/inventory/views.py index eefb7339..a3fb5d15 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -11030,55 +11030,76 @@ class InventoryListView(InventoryListViewBase): permission_required = ["django_ledger.view_purchaseordermodel"] @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() + + 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 = [] - 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 - for item in items: - po_amount+=item["total"] - po_quantity+=item["q"] + po_amount = sum(item["total"] for item in items) + po_quantity = sum(item["q"] for item in items) - 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, + "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') - - 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}"' writer = csv.writer(response) - header = [ 'PO Number', 'Created Date', @@ -11089,22 +11110,38 @@ def purchase_report_csv_export(request,dealer_slug): 'Vendors' ] writer.writerow(header) + 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: 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 '', @@ -11117,22 +11154,22 @@ def purchase_report_csv_export(request,dealer_slug): 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 if vat else 0 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_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 if selected_make: @@ -11145,62 +11182,122 @@ def car_sale_report_view(request, dealer_slug): cars_sold = cars_sold.filter(year=selected_year) if 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 - total_cars_sold=cars_sold.count() + # 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_vat_on_cars=cars_sold.annotate( + 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_discount = sum([car.discount 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_collected = total_vat_on_cars + total_vat_from_additonals + total_revenue_collected = total_revenue_from_cars + total_revenue_from_additonals + + total_discount = cars_sold.aggregate(total=Sum('discount_amount'))['total'] or 0 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') - 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') + makes = base_sold_cars_queryset.values_list('id_car_make__name', flat=True).distinct().order_by('id_car_make__name') context = { 'cars_sold': cars_sold, - 'total_cars_sold':total_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_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_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, + '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) +### 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 def car_sale_report_csv_export(request, dealer_slug): 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_year = request.GET.get('year') 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: 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) if 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 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'] - # Checking for the invoice number to avoid errors on cars without one invoice_number = None sold_date = None 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 '', 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.discount_amount, + car.final_price, + car.vat_amount, + services_total_price, + services_vat_amount, car.final_price_plus_services_plus_vat, invoice_number, ]) return response - @login_required # @permission_required('inventory.view_staff') def staff_password_reset_view(request, dealer_slug, user_pk): diff --git a/locale/ar/LC_MESSAGES/django.mo b/locale/ar/LC_MESSAGES/django.mo index 0adf9155..155f3b64 100644 Binary files a/locale/ar/LC_MESSAGES/django.mo and b/locale/ar/LC_MESSAGES/django.mo differ diff --git a/locale/ar/LC_MESSAGES/django.po b/locale/ar/LC_MESSAGES/django.po index 146e2478..8810f382 100644 --- a/locale/ar/LC_MESSAGES/django.po +++ b/locale/ar/LC_MESSAGES/django.po @@ -13191,20 +13191,14 @@ msgid "Total cars" msgstr "إجمالي السيارات" #: templates/dealers/dealer_detail.html:113 -#, fuzzy -#| msgid "Subscriptions" msgid "Plan & Subscription" msgstr "الاشتراكات" #: templates/dealers/dealer_detail.html:125 -#, fuzzy -#| msgid "Car Details" msgid "Company Details" msgstr "تفاصيل السيارة" #: templates/dealers/dealer_detail.html:137 -#, fuzzy -#| msgid "Car Transfer" msgid "Car Brands" msgstr "نقل السيارة" @@ -13252,8 +13246,6 @@ msgid "Upgrade Plan" msgstr "ترقية الخطة" #: templates/dealers/dealer_detail.html:212 -#, fuzzy -#| msgid "Manage Groups & Permissions" msgid "Manage Users & Cars" msgstr "إدارة المجموعات والأذونات" @@ -13269,20 +13261,14 @@ msgid "Contact support to increase your limits" msgstr "" #: templates/dealers/dealer_detail.html:258 -#, fuzzy -#| msgid "Client Information" msgid "Contact Information" msgstr "معلومات العميل" #: templates/dealers/dealer_detail.html:286 -#, fuzzy -#| msgid "User Information" msgid "VAT Information" msgstr "معلومات المستخدم" #: templates/dealers/dealer_detail.html:292 -#, fuzzy -#| msgid "Updated At" msgid "Update VAT" msgstr "تم التحديث" @@ -13291,8 +13277,6 @@ msgid "Makes you are selling" msgstr "الماركات التي تبيعها" #: templates/dealers/dealer_detail.html:321 -#, fuzzy -#| msgid "No staff member selected." msgid "No car makes selected." msgstr "لم يتم اختيار أي عضو من الفريق." diff --git a/templates/dashboards/general_dashboard.html b/templates/dashboards/general_dashboard.html index 03f2fa3a..f863b446 100644 --- a/templates/dashboards/general_dashboard.html +++ b/templates/dashboards/general_dashboard.html @@ -122,7 +122,7 @@ data: {{ monthly_cars_sold_json|safe }}, backgroundColor: primaryColor, borderColor: primaryColor, - borderWidth: 1 + borderWidth: 1, }] }, options: { @@ -193,7 +193,7 @@ plugins: { legend: { display: true, - labels: { color: '#495057', boxWidth: 20 } + labels: { boxWidth: 20 } }, tooltip: { backgroundColor: 'rgba(33, 37, 41, 0.9)', @@ -250,7 +250,7 @@ plugins: { legend: { position: 'right', - labels: { color: '#343a40', font: { size: 14 } } + labels: { font: { size: 14 } } }, tooltip: { backgroundColor: 'rgba(33, 37, 41, 0.9)', @@ -349,7 +349,7 @@ plugins: { legend: { position: 'right', - labels: { color: '#343a40', font: { size: 14 } } + labels: { font: { size: 14 } } }, tooltip: { backgroundColor: 'rgba(33, 37, 41, 0.9)', diff --git a/templates/ledger/reports/car_sale_report.html b/templates/ledger/reports/car_sale_report.html index 971a3990..3ab02e91 100644 --- a/templates/ledger/reports/car_sale_report.html +++ b/templates/ledger/reports/car_sale_report.html @@ -3,7 +3,7 @@ {% load static %} {% load tenhal_tag %} {% block title %} - {{ _("Car Sale Report") |capfirst }} + {{ _("Car Sale Report")|capfirst }} {% endblock title %} {% block content %}
@@ -47,11 +45,19 @@

{% trans 'Report Date' %}: {{ current_time }}

-
+

{% trans 'Filters' %}

-
+ +
+ + +
+
+ + +
- {% for model in models %} - - {% endfor %} + {% if selected_model %} + + {% endif %}
@@ -111,27 +109,8 @@
-
- - {% 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 %} - +
+

{% trans 'Report Summary' %}

@@ -166,7 +145,7 @@ {% trans 'Total Revenue' %}

- {{ total_revenue_collected |floatformat:2 }} + {{ total_revenue_collected|floatformat:2 }}

@@ -302,14 +281,82 @@ {{ car.final_price_plus_services_plus_vat|floatformat:2 }} - {{ car.invoice.invoice_number }} - - - {% endfor %} - - - - - - - {% endblock %} + {{ car.invoice.invoice_number }} + + + {% endfor %} + + + + + + + + + + +{% endblock %} \ No newline at end of file diff --git a/templates/ledger/reports/purchase_report.html b/templates/ledger/reports/purchase_report.html index d6f10e42..6aecdab6 100644 --- a/templates/ledger/reports/purchase_report.html +++ b/templates/ledger/reports/purchase_report.html @@ -2,7 +2,7 @@ {% load i18n %} {% load static %} {% block title %} - {{ _("Car Purchase Report") |capfirst }} + {{ _("Car Purchase Report")|capfirst }} {% endblock title %} {% block content %}