diff --git a/inventory/models.py b/inventory/models.py index ab589f82..8a725729 100644 --- a/inventory/models.py +++ b/inventory/models.py @@ -583,6 +583,7 @@ class AdditionalServices(models.Model, LocalizedNameMixin): "price_": str(self.price_), "taxable": self.taxable, "uom": self.uom, + "service_tax":str(self.service_tax) } @property @@ -593,6 +594,13 @@ class AdditionalServices(models.Model, LocalizedNameMixin): if self.taxable else self.price ) + + @property + def service_tax(self): + vat = VatRate.objects.filter(dealer=self.dealer, is_active=True).first() + return ( + Decimal(self.price * vat.rate) + ) class Meta: verbose_name = _("Additional Services") @@ -865,19 +873,64 @@ class Car(Base): # @property def get_additional_services_amount(self): + return sum([Decimal(x.price) for x in self.additional_services.all()]) + @property + def get_additional_services_amount_(self): return sum([Decimal(x.price_) for x in self.additional_services.all()]) + @property + def get_additional_services_vat(self): + vat = VatRate.objects.filter(dealer=self.dealer,is_active=True).first() + return sum([Decimal((x.price)*(vat.rate)) for x in self.additional_services.all()]) def get_additional_services(self): - return {"services": [x for x in self.additional_services.all()],"total":self.get_additional_services_amount} + vat = VatRate.objects.filter(dealer=self.dealer,is_active=True).first() + return {"services": [[x,(x.price)*(vat.rate)] for x in self.additional_services.all()], + "total_":self.get_additional_services_amount_, + "total":self.get_additional_services_amount, + "services_vat":self.get_additional_services_vat} @property + def final_price(self): + return Decimal(self.marked_price -self.discount) + @property def vat_amount(self): vat = VatRate.objects.filter(dealer=self.dealer,is_active=True).first() - return Decimal(self.marked_price) * (vat.rate / 100) - + return Decimal(self.final_price) * (vat.rate) + @property - def total_vat(self): - return Decimal(self.marked_price) + Decimal(self.vat_amount) + def total_services_and_car_vat(self): + return self.vat_amount+self.get_additional_services()['services_vat'] + + @property + def final_price_plus_vat(self): + return Decimal(self.final_price) + Decimal(self.vat_amount) + + @property + def final_price_plus_services_plus_vat(self): + return Decimal(self.final_price_plus_vat) + Decimal(self.get_additional_services()['total_']) #total services with vat and car_sell price with vat + # to be used after invoice is created + @property + def invoice(self): + return self.item_model.invoicemodel_set.first() or None + @property + def estimate(self): + return getattr(self.invoice,'ce_model',None) + @property + def discount(self): + if not self.estimate: + return 0 + try: + instance = ExtraInfo.objects.get( + dealer=self.dealer, + content_type=ContentType.objects.get_for_model(EstimateModel), + object_id=self.estimate.pk, + ) + return Decimal(instance.data.get('discount',0)) + except ExtraInfo.DoesNotExist: + return Decimal(0) + + + # def get_discount_amount(self,estimate,user): # try: # instance = models.ExtraInfo.objects.get( diff --git a/inventory/urls.py b/inventory/urls.py index bbfc9968..6124a486 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,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"), @@ -772,6 +775,7 @@ urlpatterns = [ views.EstimateDetailView.as_view(), name="estimate_detail", ), + path('/sales/estimates/print//', views.EstimatePrintView.as_view(), name='estimate_print'), path( "/sales/estimates/create/", views.create_estimate, diff --git a/inventory/utils.py b/inventory/utils.py index e21b9274..8bf69b86 100644 --- a/inventory/utils.py +++ b/inventory/utils.py @@ -1288,18 +1288,24 @@ def get_finance_data(estimate,dealer): ) discount = extra_info.data.get("discount", 0) discount = Decimal(discount) - vat_amount = car.marked_price * vat.rate + additional_services = car.get_additional_services() - + discounted_price=(Decimal(car.marked_price) - discount) + vat_amount = discounted_price * vat.rate + total_services_vat=sum([ x[1] for x in additional_services.get("services")]) + total_vat=vat_amount+total_services_vat return { "car": car, - "discounted_price": (Decimal(car.marked_price) - discount) or 0, + "discounted_price": discounted_price or 0, "price_before_discount": car.marked_price, "vat_amount": vat_amount, "vat_rate": vat.rate, "discount_amount": discount, "additional_services": additional_services, - "grand_total": (car.marked_price - discount) + vat_amount + additional_services.get("total") + "final_price": discounted_price+ vat_amount, + "total_services_vat":total_services_vat, + "total_vat":total_vat, + "grand_total": discounted_price + vat_amount + additional_services.get("total") } diff --git a/inventory/views.py b/inventory/views.py index 2155decc..7fe42acd 100644 --- a/inventory/views.py +++ b/inventory/views.py @@ -17,7 +17,7 @@ from random import randint from decimal import Decimal from io import TextIOWrapper from django.apps import apps -from datetime import datetime, timedelta +from datetime import datetime, timedelta,date from calendar import month_name from pyzbar.pyzbar import decode from urllib.parse import urlparse, urlunparse @@ -396,7 +396,164 @@ 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 + + + +def DealerDashboard(request): + dealer = request.dealer + cars_sold = models.Car.objects.filter(dealer=dealer, status='sold') + + today_local = timezone.localdate() + start_date_str = request.GET.get('start_date') + end_date_str = request.GET.get('end_date') + + if not start_date_str: + start_date = today_local - timedelta(days=30) + else: + start_date = timezone.datetime.strptime(start_date_str, '%Y-%m-%d').date() + + if not end_date_str: + end_date = today_local + else: + end_date = timezone.datetime.strptime(end_date_str, '%Y-%m-%d').date() + + + # The database query will automatically be handled in a timezone-aware manner + cars_sold_filtered = cars_sold.filter( + sold_date__date__gte=start_date, + sold_date__date__lte=end_date + ) + print(cars_sold_filtered) + # # Calculate summary data for the filtered results + total_cars_sold=cars_sold_filtered.count() + print(total_cars_sold) + total_revenue_from_cars = sum([ car.final_price for car in cars_sold_filtered]) + total_cost_of_cars_sold=sum([car.cost_price for car in cars_sold_filtered]) + 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]) + 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]) + + context = { + 'start_date': start_date, + 'end_date': end_date, + 'cars_sold': cars_sold_filtered, + 'total_cars_sold':total_cars_sold, + 'total_cost_of_cars_sold':total_cost_of_cars_sold, + 'total_revenue_from_cars':total_revenue_from_cars, + 'net_profit_from_cars':net_profit_from_cars, + 'total_vat_collected_from_cars': total_vat_collected_from_cars, + 'total_discount_on_cars':total_discount, + 'total_revenue_from_services':total_revenue_from_services, + 'total_vat_collected_from_services': total_vat_collected_from_services, + 'total_revenue_generated': total_revenue_generated, + 'total_vat_collected':total_vat_collected, + + } + + return render(request, 'dashboards/dealer_dashboard.html', context) + + + class ManagerDashboard(LoginRequiredMixin, TemplateView): """ @@ -412,7 +569,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: @@ -495,6 +652,9 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView): return context + + + class SalesDashboard(LoginRequiredMixin, TemplateView): """ SalesDashboard class provides a view for the sales dashboard. @@ -513,7 +673,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) @@ -4598,7 +4903,7 @@ def create_estimate(request, dealer_slug, slug=None): "quantity": 1, "unit_cost": round(float(i.marked_price)), "unit_revenue": round(float(i.marked_price)), - "total_amount": round(float(i.total_vat)),# TODO : check later + "total_amount": round(float(i.final_price_plus_vat)),# TODO : check later } ) @@ -4619,7 +4924,7 @@ def create_estimate(request, dealer_slug, slug=None): # "unit_cost": instance.cost_price, # "unit_revenue": instance.marked_price, # "quantity": Decimal(quantities), - # "total_amount": instance.total_vat * int(quantities),# TODO : check later + # "total_amount": instance.final_price_plus_vat * int(quantities),# TODO : check later # } # } @@ -4777,6 +5082,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView # calculator = CarFinanceCalculator(estimate) # finance_data = calculator.get_finance_data() finance_data = get_finance_data(estimate,dealer) + invoice_obj = InvoiceModel.objects.all().filter(ce_model=estimate).first() kwargs["data"] = finance_data @@ -4785,7 +5091,7 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView car = estimate.get_itemtxs_data()[0].first().item_model.car selected_items = car.additional_services.filter(dealer=dealer) form = forms.AdditionalFinancesForm() - form.fields["additional_finances"].queryset = form.fields["additional_finances"].queryset.filter(dealer=dealer) # TODO : check later + form.fields["additional_finances"].queryset = form.fields["additional_finances"].queryset.filter(dealer=dealer) # form.initial["additional_finances"] = selected_items kwargs["additionals_form"] = form except Exception as e: @@ -4793,6 +5099,16 @@ class EstimateDetailView(LoginRequiredMixin, PermissionRequiredMixin, DetailView return super().get_context_data(**kwargs) +class EstimatePrintView(EstimateDetailView): + """ + A view to render a printer-friendly version of the estimate. + It reuses the data-fetching logic from EstimateDetailView but + uses a dedicated, stripped-down print template. + """ + template_name = "sales/estimates/estimate_preview.html" + + + @login_required @permission_required("inventory.add_saleorder", raise_exception=True) def create_sale_order(request, dealer_slug, pk): @@ -4844,7 +5160,8 @@ def create_sale_order(request, dealer_slug, pk): f"KeyError: 'car_info' or 'status' key missing when attempting to update status to 'sold' for item.item_model PK: {getattr(item.item_model, 'pk', 'N/A')}." ) pass - + item.item_model.car.sold_date=timezone.now() # to be checked added by faheed + item.item_model.car.save()# to be checked added byfaheed item.item_model.car.mark_as_sold() messages.success(request, "Sale Order created successfully") @@ -10491,7 +10808,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, @@ -10810,12 +11127,14 @@ def purchase_report_csv_export(request,dealer_slug): def car_sale_report_view(request, dealer_slug): dealer = get_object_or_404(models.Dealer, slug=dealer_slug) 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') # Apply filters to the queryset if selected_make: @@ -10826,78 +11145,115 @@ def car_sale_report_view(request, dealer_slug): cars_sold = cars_sold.filter(id_car_serie__name=selected_serie) if selected_year: cars_sold = cars_sold.filter(year=selected_year) + if selected_stock_type: + cars_sold = cars_sold.filter(stock_type=selected_stock_type) # Get distinct values for filter dropdowns makes = models.Car.objects.filter(dealer=dealer, status='sold').values_list('id_car_make__name', flat=True).distinct() models_qs = models.Car.objects.filter(dealer=dealer, status='sold').values_list('id_car_model__name', flat=True).distinct() - series = models.Car.objects.filter(dealer=dealer, status='sold').values_list( - 'id_car_serie__name', flat=True).distinct() + + series = models.Car.objects.filter(dealer=dealer, status='sold').values_list('id_car_serie__name', flat=True).distinct() + stock_types=models.Car.objects.filter(dealer=dealer, status='sold').values_list('stock_type', flat=True).distinct() years = models.Car.objects.filter(dealer=dealer, status='sold').values_list('year', flat=True).distinct().order_by('-year') + + # # Calculate summary data for the filtered results + total_cars_sold=cars_sold.count() + total_revenue_from_cars = sum([ car.final_price for car in cars_sold]) + total_vat_on_cars=sum([car.vat_amount for car in cars_sold]) - total_revenue = cars_sold.aggregate(total_revenue=Sum('finances__marked_price'))['total_revenue'] or 0 - # total_vat = cars_sold.aggregate(total_vat=Sum('finances__vat_amount'))['total_vat'] or 0 - total_discount = cars_sold.aggregate(total_discount=Sum('finances__discount_amount'))['total_discount'] 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]) current_time = timezone.now().strftime("%Y-%m-%d %H:%M:%S") context = { 'cars_sold': cars_sold, + 'total_cars_sold':total_cars_sold, 'current_time': current_time, 'dealer': dealer, - 'total_revenue': total_revenue, - # 'total_vat': total_vat, + 'total_revenue_from_cars': total_revenue_from_cars, + 'total_revenue_from_additonals':total_revenue_from_additonals, + 'total_revenue_collected': total_revenue_collected, + 'total_vat_on_cars':total_vat_on_cars, + 'total_vat_from_additonals':total_vat_from_additonals, + 'total_vat_collected':total_vat_collected, 'total_discount': total_discount, 'makes': makes, 'models': models_qs, 'series': series, 'years': years, + 'stock_types':stock_types, 'selected_make': selected_make, 'selected_model': selected_model, 'selected_serie': selected_serie, 'selected_year': selected_year, + 'selected_stock_type':selected_stock_type, } return render(request, 'ledger/reports/car_sale_report.html', context) -def car_sale_report_csv_export(request,dealer_slug): - +@login_required +def car_sale_report_csv_export(request, dealer_slug): 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"sales_report_{dealer_slug}_{current_time}.csv" response['Content-Disposition'] = f'attachment; filename="{filename}"' writer = csv.writer(response) - header=[ - 'Make', - 'VIN', - 'Model', - 'Year', - 'Serie', - 'Trim', - 'Mileage', - 'Stock Type', - 'Created Date', - 'Sold Date', - 'Cost Price', - 'Marked Price', - 'Discount Amount', - 'Selling Price', - 'Tax Amount', - 'Invoice Number', + # Define the CSV header based on your HTML table headers + header = [ + 'VIN', 'Make', 'Model', 'Year', 'Serie', 'Trim', 'Mileage', + 'Stock Type', 'Created Date', 'Sold Date', 'Cost Price', + 'Marked Price', 'Discount Amount', 'Selling Price', + 'VAT on Car', 'Services Price', 'VAT on Services', 'Final Total', + 'Invoice Number' ] writer.writerow(header) - dealer=get_object_or_404(models.Dealer,slug=dealer_slug) - cars_sold=models.Car.objects.filter(dealer=dealer,status='sold') + dealer = get_object_or_404(models.Dealer, slug=dealer_slug) + cars_sold = models.Car.objects.filter(dealer=dealer, status='sold') + + # Apply filters from the request, just like in your HTML view + selected_make = request.GET.get('make') + selected_model = request.GET.get('model') + selected_serie = request.GET.get('serie') + selected_year = request.GET.get('year') + selected_stock_type = request.GET.get('stock_type') + + if selected_make: + cars_sold = cars_sold.filter(id_car_make__name=selected_make) + if selected_model: + cars_sold = cars_sold.filter(id_car_model__name=selected_model) + if selected_serie: + cars_sold = cars_sold.filter(id_car_serie__name=selected_serie) + if selected_year: + cars_sold = cars_sold.filter(year=selected_year) + if selected_stock_type: + cars_sold = cars_sold.filter(stock_type=selected_stock_type) + + # Write the data for the filtered cars for car in cars_sold: - writer.writerow([ + # 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: + invoice_number = car.invoice.invoice_number + sold_date = car.invoice.date_paid + + writer.writerow([ car.vin, car.id_car_make.name, car.id_car_model.name, @@ -10907,13 +11263,16 @@ def car_sale_report_csv_export(request,dealer_slug): car.mileage if car.mileage else '0', car.stock_type, car.created_at.strftime("%Y-%m-%d %H:%M:%S") if car.created_at else '', - car.sold_date.strftime("%Y-%m-%d %H:%M:%S") if car.sold_date else '', + sold_date.strftime("%Y-%m-%d %H:%M:%S") if sold_date else '', car.cost_price, car.marked_price, - car.discount_amount, - car.selling_price, - car.vat_amount, # TODO : check later - car.item_model.invoicemodel_set.first().invoice_number + car.discount, # Ensure this property returns a number + car.final_price, # Selling Price without VAT + car.vat_amount, # VAT on the car + services_total_price, # Total services without VAT + services_vat_amount, # VAT on services + car.final_price_plus_services_plus_vat, + invoice_number, ]) return response 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 + +
+
+
+
+
+
+
+

Net Profit

+

$1.25M

+
+ + +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

+
+
+
+ +
+
+
+
+
Monthly Revenue & Profit
+
+
+ +
+
+
+
+
+ + +{% endblock %} + +{% block customJS %} + +{% endblock %} \ No newline at end of file diff --git a/templates/dashboards/dealer_dashboard.html b/templates/dashboards/dealer_dashboard.html new file mode 100644 index 00000000..93e2a2c0 --- /dev/null +++ b/templates/dashboards/dealer_dashboard.html @@ -0,0 +1,349 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load tenhal_tag %} +{% block title %} + {{ _("Dealership Dashboard") |capfirst }} +{% endblock title %} +{% block content%} +
+
+

Business Health Dashboard

+
+
+
+ + +
+
+ + +
+
+ +
+
+
+
+ +
+
+
+
+
+

Total Cost of Cars Sold

+

{{total_cost_of_cars_sold|floatformat:2}}

+
+ + +8% from last month + +
+
+
+
+
+
+
+

Total Revenue from cars

+

{{total_revenue_from_cars|floatformat:2}}

+
+ + +8% from last month + +
+
+
+
+
+
+
+

Net Profit From Cars

+

{{net_profit_from_cars|floatformat:2}}

+
+ + +8% from last month + +
+
+
+
+
+
+
+

Total Discount on cars

+

{{total_discount_on_cars|floatformat:2}}

+
+ + +8% from last month + +
+
+
+
+
+
+
+

Total VAT collected from cars

+

{{total_vat_collected_from_cars|floatformat:2}}

+
+ + +8% from last month + +
+
+
+
+
+
+
+

Total Revenue from Services

+

{{total_revenue_from_services|floatformat:2}}

+
+ + +8% from last month + +
+
+
+ +
+
+
+
+

Total VAT collected from Services

+

{{total_vat_collected_from_services|floatformat:2}}

+
+ + +8% from last month + +
+
+
+
+
+
+
+

{% trans "Total Revenue Generated" %}

+

{{total_revenue_generated|floatformat:2}}

+
+ + +8% from last month + +
+
+
+ +
+
+
+
+

{% trans "Total VAT Collected" %}

+

{{total_vat_collected|floatformat:2}}

+
+ + +8% from last month + +
+
+
+ +
+
+
+
+

Total Expense

+

$1.25M

+
+ + -2% from last month + +
+
+
+
+
+
+
+

Gross Profit

+

$1.25M

+
+ + +8% from last month + +
+
+
+ +
+
+
+
+

Total VAT Collected

+

{{total_vat_collected|floatformat:2}}

+
+ + +8% from last month + +
+
+
+
+
+
+
+

Total Cars Sold

+

{{ total_cars_sold }}

+
+ + +5 units from last month + +
+
+
+
+ +
+
+
+
+
Monthly Revenue & Profit
+
+
+ +
+
+
+
+
+
+
Monthly Cars Sold
+
+
+ +
+
+
+
+
+ + +{% 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..d18771f7 --- /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 + +
+
+
+ + +
+
+
+
+
Inventory by Make
+
+
+ +
+
+
+ +
+
+
+
Car Models by Make in Inventory
+
+
+
+ + +
+
+
+ +
+
+
+ +
+ + +
+ + +{% 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 -
-
-
-
- -
-
-
-
Monthly Revenue & Profit
-
- -
-
-
-
-
-
Monthly Cars Sold
-
- -
-
-
-
-
-
Inventory By Make
-
- -
-
-
-
-
-
Sales By Make
-
- -
-
-
- -
-
-
Salesperson Performance
-
- -
-
-
-
-
-
Top 5 Lead Sources
-
- -
-
-
-
-
-
Appointments by Weekday
-
- -
-
-
- -
-
-
Lead Conversion Funnel
-
- -
-
-
-
-
- -{% 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 %} + +
+
+

Manager Dashboard

+ +
+ +
+
+
+
+
+

Total Revenue

+

$1.25M

+
+ + +8% from last month + +
+
+
+
+
+
+
+

Net Profit

+

$1.25M

+
+ + +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 + +
+
+
+
+ +
+
+
+
+
Monthly Revenue & Profit
+
+
+ +
+
+
+
+
+
+
Monthly Cars Sold
+
+
+ +
+
+
+
+ +
+
+
+
+
Sales by Make
+
+
+ +
+
+
+
+
+
+
Top Salesperson Performance
+
+
+ +
+
+
+
+ +
+ +{% 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 + +
+
+
+
+ +
+
+
+
+
Monthly Cars Sold
+
+
+ +
+
+
+
+
+
+
Sales by Make
+
+
+ +
+
+
+
+ +
+
+
+
+
Top 5 Lead Sources
+
+
+ +
+
+
+
+
+
+
Appointments by Weekday
+
+
+ +
+
+
+
+ +
+
+
+
+
Lead Conversion Funnel
+
+
+ +
+
+
+
+
+ + +{% 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/ledger/reports/car_sale_report.html b/templates/ledger/reports/car_sale_report.html index 011bc426..498589a6 100644 --- a/templates/ledger/reports/car_sale_report.html +++ b/templates/ledger/reports/car_sale_report.html @@ -51,7 +51,7 @@

{% trans 'Filters' %}

-
+
-
+
-
+
-
+
+ + +
+
+ + {% 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' %}

-
{% trans 'Total Revenue' %}
-

{{ total_revenue|floatformat:2 }}

+
{% trans 'Total Revenue from Cars' %}
+

{{ total_revenue_from_cars|floatformat:2 }}

-
{% trans 'Total VAT Amount' %}
-

{{ 10000|floatformat:2 }}

+
{% trans 'Total Revenue from Services' %}
+

{{ total_revenue_from_additonals|floatformat:2 }}

+
+
+
+
{% trans 'Total Revenue' %}
+

{{total_revenue_collected |floatformat:2 }}

+
+
+
+
+
+
+
{% trans 'Total VAT from Cars' %}
+

{{ total_vat_on_cars|floatformat:2 }}

+
+
+
+
+
+
+
{% trans 'Total VAT from Services' %}
+

{{ total_vat_from_additonals|floatformat:2 }}

+
+
+
+
+
+
+
{% trans 'Total VAT' %}
+

{{ total_vat_collected|floatformat:2 }}

+
+
+
+
{% trans 'Total Discount Amount' %}
-

{{ total_discount|floatformat:2 }}

+

{{ total_discount|floatformat:2 }}

@@ -124,13 +186,10 @@
{% trans 'Total Cars Sold' %}
-

{{ cars_sold|length }}

+

{{ total_cars_sold }}

- - -
@@ -142,7 +201,7 @@ {% trans 'Download as CSV' %}
-
+
@@ -160,7 +219,10 @@ - + + + + @@ -178,12 +240,15 @@ - - - # TODO : check later - - # TODO : check later - + + + + + + + + + {% endfor %} 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/purchase_orders/includes/po_item_formset.html b/templates/purchase_orders/includes/po_item_formset.html index 31005851..7cc612ce 100644 --- a/templates/purchase_orders/includes/po_item_formset.html +++ b/templates/purchase_orders/includes/po_item_formset.html @@ -17,8 +17,7 @@
{% trans 'Marked Price' %} {% trans 'Discount Amount' %} {% trans 'Selling Price' %}{% trans 'Tax Amount' %}{% trans 'VAT on Car' %}{% trans 'Services Price' %}{% trans 'VAT on Services' %}{% trans 'Final Total' %} {% trans 'Invoice Number' %}
{{ car.stock_type|capfirst }} {{ car.created_at|date }} {{ car.invoice.date_paid|date|default_if_none:"-" }}{{ car.cost_price }} {{ car.marked_price }} {{ car.total_discount }} {{ car.selling_price }} {{ car.vat_amount }} {{ car.invoice.invoice_number }}{{ car.cost_price }}{{ car.marked_price }} {{ car.discount}} {{ car.final_price}} {{ car.vat_amount|floatformat:2 }} {{ car.get_additional_services.total|floatformat:2}} {{ car.get_additional_services.services_vat|floatformat:2}}{{ car.final_price_plus_services_plus_vat|floatformat:2 }}{{ car.invoice.invoice_number }}
- + + + + + + + + + +
+ {% trans 'Item' %} {% if po_model.is_draft %} + {% endif %} {% if estimate.can_approve and not request.is_manager %} {% trans "Additional Services" %} {% for service in data.additional_services.services %} - + {{ service.name }} - {{ service.price_|floatformat }} + + {{ service.0.name }} - {{ service.0.price_|floatformat }}
{% endfor %} {% if estimate.is_draft %} diff --git a/templates/sales/estimates/estimate_preview.html b/templates/sales/estimates/estimate_preview.html index 810bf665..528d16bc 100644 --- a/templates/sales/estimates/estimate_preview.html +++ b/templates/sales/estimates/estimate_preview.html @@ -1,11 +1,10 @@ {% load i18n static custom_filters num2words_tags %} - + - Invoice - + Quotation - - @@ -92,267 +101,262 @@
-
-
-
- Quotation / عرض سعر -
-
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- {% if dealer.logo %} - Dealer Logo - {% endif %} -
-
- Customer Name - - {{ dealer.arabic_name }} -
- {{ dealer.name }} -
- {{ dealer.arabic_name }} -
- Address - {{ dealer.address }} - العنوان -
- Phone - {{ dealer.phone_number }} - جوال -
- VAT Number - {{ dealer.vrn }} - الرقم الضريبي -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Quotation Number - {{ estimate.estimate_number }} - رقم عرض السعر -
- Date - {{ estimate.date_approved| date:"Y/m/d" }} - التاريخ -
- Customer - {{ estimate.customer.customer_name }} - العميل -
- VAT ID - {{ estimate.customer.vrn|default:"-" }} - الرقم الضريبي -
- Email - {{ estimate.customer.email |default:"N/A" }} - البريد الإلكتروني -
- Terms - {{ estimate.get_terms_display }} - طريقة الدفع -
-
-
- Car Details - تفاصيل السيارة -
-
- - + +
+
+
+
+ Quotation / عرض سعر +
+
+
+
+ +
+ +
- - - - - - - - + + + - - - {% for item in data.cars %} - - - - - - - - - {% endfor %} - - - - + + + - -
- Make / الصانع - - Model / الموديل - - Trim / الفئة - - Year / السنة - - VIN / الهيكل - - Quantity / الكمية - - Unit Price / سعر الوحدة - - Including VAT / شامل الضريبة - + Dealership Name + + {{ request.dealer.name }} + + اسم الوكالة +
{{ item.make }} - {{ item.model }} - {{ item.trim }}{{ item.year }}{{ item.vin }}{{ item.quantity|floatformat:-1 }}{{ item.unit_price|floatformat:2 }}{{ item.total_vat|floatformat:2 }}
{{ data.quantity|floatformat:-1 }}{{ data.total_price_before_discount|floatformat:2 }}{{ data.grand_total|floatformat:2 }} + Dealership Address + {{ request.dealer.address }} + العنوان +
-
-
- Additional Services - الخدمات الإضافية -
- {% if data.additionals %} +
+ Phone + {{ request.dealer.phone_number }} + جوال +
+ VAT Number + {{ request.dealer.vrn }} + الرقم الضريبي +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Quotation Number + {{ estimate.estimate_number }} + رقم عرض السعر +
+ Date + {{ estimate.date_approved| date:"Y/m/d" }} + التاريخ +
+ Customer Name + {{ estimate.customer.customer_name }} + العميل +
+ Email + {{ estimate.customer.email |default:"N/A" }} + البريد الإلكتروني +
+ Terms + {{ estimate.get_terms_display }} + طريقة الدفع +
+
+
+ Car Details + تفاصيل السيارة +
- - - + + + + + + + + + + - {% for item in data.additionals %} - - - - - - {% endfor %} + + + + + + + + + + + + +
Type / النوعPrice / القيمة - Including VAT / شامل الضريبة + + Make / الصانع + + Model / الموديل + + Series / السلسلة + + Trim / الفئة + + Year / السنة + + VIN / رقم الهيكل + + Quantity / الكمية + + Unit Price / سعر الوحدة + + Discount / الخصم + + VAT / الضريبة + + Total / الإجمالي
{{ item.name }}{{ item.price|floatformat }}{{ item.price_|floatformat }}
{{ data.car.id_car_make.name }}{{ data.car.id_car_model.name }}{{ data.car.id_car_serie.name }}{{ data.car.id_car_trim.name }}{{ data.car.year }}{{ data.car.vin }}1{{ data.car.marked_price |floatformat:2 }}{{  data.discount_amount |floatformat:2 }}{{ data.vat_amount|floatformat:2 }}{{ data.final_price|floatformat:2 }}
- {% endif %} -
-
- - - - - - - - - - - - - - -
- VAT - - {{ data.total_vat_amount|floatformat }} - - ضريبة القيمة المضافة -
- Total - - {{ data.grand_total|floatformat }}  - - الإجمالي -
- كتابةً: {{ data.grand_total|num_to_words }}  -
+
+ Additional Services + الخدمات الإضافية +
+ {% if data.additional_services %} +
+ + + + + + + + + + + {% for service in data.additional_services.services %} + + + + + + + {% endfor %} + +
Type / النوعPrice / القيمةVAT / ضريبة الخدمة + Total / الإجمالي +
{{ service.0.name }}{{ service.0.price|floatformat }}{{ service.1|floatformat }}{{ service.0.price_|floatformat }}
+
+ {% endif %} +
+
+ + + + + + + + + + + + + + +
+ Total VAT + + {{ data.total_vat|floatformat }} + + إجمالي ضريبة القيمة المضافة +
+ Grand Total + + {{ data.grand_total|floatformat }}  + + الإجمالي +
+ كتابةً: {{ data.grand_total|num_to_words }}  +
+
-
-
-
-
-
-
+
+ - + \ No newline at end of file diff --git a/templates/sales/invoices/invoice_detail.html b/templates/sales/invoices/invoice_detail.html index 3052060e..51ed30c0 100644 --- a/templates/sales/invoices/invoice_detail.html +++ b/templates/sales/invoices/invoice_detail.html @@ -358,7 +358,7 @@ {% trans "Additional Services" %} {% for service in data.additional_services.services %} - + {{ service.name }} - {{ service.price_|floatformat }} + + {{ service.0.name }} - {{ service.0.price_|floatformat }}
{% endfor %} diff --git a/templates/sales/invoices/invoice_preview.html b/templates/sales/invoices/invoice_preview.html index 939eb82f..77474f4f 100644 --- a/templates/sales/invoices/invoice_preview.html +++ b/templates/sales/invoices/invoice_preview.html @@ -1,11 +1,10 @@ {% load i18n static custom_filters num2words_tags %} - + Invoice - - - @@ -97,271 +101,274 @@
+
-
-
Tax Invoice  /  فاتورة ضريبية
-
-
-
-
- QR Code -
+
+
+
+ Invoice / فاتورة +
-
- -
- Car Details - تفاصيل السيارة -
-
- - - - - - - - - - - - - - - {% for item in data.cars %} - - - - - - - - - {% endfor %} - - - - - - - -
- Make / الصانع - - Model / الموديل - - Trim / الفئة - - Year / السنة - - VIN / الهيكل - - Quantity / الكمية - - Unit Price / سعر الوحدة - - Including VAT / شامل الضريبة -
{{ item.make }} - {{ item.model }} - {{ item.trim }}{{ item.year }}{{ item.vin }}{{ item.quantity|floatformat }}{{ item.unit_price|floatformat }}{{ item.total_vat|floatformat }}
{{ data.quantity|floatformat }}{{ data.total_price_before_discount|floatformat }}{{ data.grand_total|floatformat }}
-
-
- Additional Services - الخدمات الإضافية -
- {% if data.additionals %}
- - - + + + + + + + + + + - {% for item in data.additionals %} - - - - - - {% endfor %} + + + + + + + + + + + + + +
Type / النوعPrice / القيمة - Including VAT / شامل الضريبة + + Make / الصانع + + Model / الموديل + + Series / السلسلة + + Trim / الفئة + + Year / السنة + + VIN / رقم الهيكل + + Quantity / الكمية + + Unit Price / سعر الوحدة + + Discount / الخصم + + VAT / الضريبة + + Total / الإجمالي
{{ item.name }}{{ item.price|floatformat }}{{ item.price_|floatformat }}
{{ data.car.id_car_make.name }}{{ data.car.id_car_model.name }}{{ data.car.id_car_serie.name }}{{ data.car.id_car_trim.name }}{{ data.car.year }}{{ data.car.vin }}1{{ data.car.marked_price |floatformat:2 }}{{ data.discount_amount |floatformat:2 }}{{ data.vat_amount|floatformat:2 }}{{ data.final_price|floatformat:2 }}
- {% endif %} -
-
- - - - - - - - - - - - - - -
- VAT - - {{ data.total_vat_amount|floatformat }} - - ضريبة القيمة المضافة -
- Total - - {{ data.grand_total|floatformat }}  - - الإجمالي -
- كتابةً: {{ data.grand_total|num_to_words }}  -
+
+ Additional Services + الخدمات الإضافية +
+ {% if data.additional_services %} +
+ + + + + + + + + + + {% for service in data.additional_services.services %} + + + + + + + {% endfor %} + +
Type / النوعPrice / السعرService VAT / ضريبة الخدمة + Total / الإجمالي +
{{ service.0.name }}{{ service.0.price|floatformat }}{{ service.1|floatformat }}{{ service.0.price_|floatformat }}
+
+ {% endif %} +
+
+ + + + + + + + + + + + + + +
+ Total VAT + + {{ data.total_vat|floatformat }} + + إجمالي ضريبة القيمة المضافة +
+ Grand Total + + {{ data.grand_total|floatformat }}  + + الإجمالي الكلي +
+ كتابةً: {{ data.grand_total|num_to_words }}  +
+
-
-
-
-
-
-
+
+ - + \ No newline at end of file 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 %} -
-
-
-

- {{ _("User Details") }} -

+
+
+
+ + {% trans "Back to Staffs" %} + +

{% trans "Staff Profile" %}

-
-
-
- {% if user_.logo %} - User Image - {% else %} - Default User Image - {% endif %} -
-
-

- {{ _("Name") }}: {{ user_.name }} -

-

- {{ _("Arabic Name") }}: {{ user_.arabic_name }} -

-

- {{ _("Email") }}: {{ user_.email }} -

-
-
-

- {{ _("Phone Number") }}: {{ user_.phone_number }} -

-
- {{ _("Roles") }}: - {% for group in user_.groups %} - {{ group.name }} - {% if not forloop.last %}&{% endif %} - {% endfor %} +
+ + {% trans "Edit" %} + + +
+
+ +
+
+
+
+
+ {% if user_.logo %} + {{ user_.name }} + {% else %} +
+ {{ user_.name|first|upper }} +
+ {% endif %} +
+

{{ user_.name }}

+

{{ user_.email }}

+
-
-
-

Groups

- Manage Groups - - - - - - - - {% for group in user_.groups %} - - - - {% empty %} - - - - {% endfor %} - -
{% trans 'name'|capfirst %}
{{ group }}
{% trans "No Group" %}
-
-
{% include 'modal/delete_modal.html' %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/templates/users/user_group_form.html b/templates/users/user_group_form.html index b60c5ad5..91c71bfd 100644 --- a/templates/users/user_group_form.html +++ b/templates/users/user_group_form.html @@ -5,32 +5,37 @@ {% trans "Group" %} {% endblock title %} {% block content %} -
-
-
-
-

{{ _("Manage Groups") }}

+ +
+ +
+
+
+

+ {{ _("Manage Groups") }} +

+
+
+
+ {% csrf_token %} + {{ redirect_field }} + {{ form|crispy }} + {% for error in form.errors %}
{{ error }}
{% endfor %} +
+
+ + {% trans "Cancel" %} +
+
+ +
-
-
-
-
- {% csrf_token %} - {{ redirect_field }} - {{ form|crispy }} - {% for error in form.errors %} -
{{ error }}
- {% endfor %} -
- {% trans "Cancel" %} - -
-
-
-
-
+ + {% endblock %} + + + + + \ No newline at end of file diff --git a/templates/users/user_list.html b/templates/users/user_list.html index 28a7e1d9..4b810bc8 100644 --- a/templates/users/user_list.html +++ b/templates/users/user_list.html @@ -1,94 +1,100 @@ {% extends "base.html" %} {% load i18n %} {% load render_table from django_tables2 %} + {% block title %} {% trans "Staffs" %} {% endblock title %} + {% block content %} -{%if users or request.GET.q%} -
-
-
-
-
- {% if request.user.userplan %} - {% trans "Add New Staff" %} - {% trans "Manage Groups & Permissions" %} - {% else %} + {%if users or request.GET.q%} +
+
+

{% trans "Staffs" %}

+ {% if request.user.userplan %} + + {% endif %} +
- {% url "pricing_page" request.dealer.slug as pricing_page_url %} - {% include "message-illustration.html" with value1="No Active Plan, please create your subscription plan." value2="Manage Subscription" url=pricing_page_url %} - - {% endif %} -
+
+
+
+ + + + + + + + + + + + {% for user in users %} + + + + + + + + {% endfor %} + +
{% trans 'name'|capfirst %}{% trans 'email'|capfirst %}{% trans 'phone number'|capfirst %}{% trans 'role'|capfirst %}{% trans 'actions'|capfirst %}
+
+
+ {% if user.logo %} + {{ user.name }}'s logo + {% else %} +
+ {{ user.name|first|upper }} +
+ {% endif %} +
+ + {{ user.name }} + +
+
{{ user.email }}{{ user.phone_number }} + {% for group in user.groups %} + {{ group.name|title }} + {% endfor %} + + + {% trans 'View' %} + +
-
- - - - - - - - - - - - {% for user in users %} - - - - - - - - {% endfor %} - -
{% trans 'name'|capfirst %}{% trans 'email'|capfirst %}{% trans 'phone number'|capfirst %}{% trans 'role'|capfirst %}{% trans 'actions'|capfirst %}
-
-
- {% if user.logo %} - Logo - {% endif %} -
- {{ user.arabic_name }} -
-
{{ user.email }}{{ user.phone_number }} - {% for group in user.groups %} - {% trans group.name|title %} - {% endfor %} - - - - {% trans 'view'|capfirst %} - -
-
-
- {% if is_paginated %} + + {% if is_paginated %} +
{% include 'partials/pagination.html' %} - {% endif %} -
+
+ {% endif %}
-
{% else %} {% if request.user.userplan %} - {% url "user_create" request.dealer.slug as create_staff_url %} + {% url "user_create" request.dealer.slug as create_staff_url %} {% include "empty-illustration-page.html" with value="staff" url=create_staff_url %} - {% else %} {% url "pricing_page" request.dealer.slug as pricing_page_url %} {% include "message-illustration.html" with value1=_("No active plan, Please create a subscription plan.") value2=_("Buy Plan") url=pricing_page_url %} {% endif %} {% endif %} -{% endblock %} + +{% endblock %} \ No newline at end of file