update
@ -65,6 +65,7 @@ admin.site.register(models.Lead)
|
||||
admin.site.register(models.Activity)
|
||||
admin.site.register(models.Schedule)
|
||||
admin.site.register(models.Notes)
|
||||
admin.site.register(models.UserActivityLog)
|
||||
# admin.site.register(appointment_models.Client)
|
||||
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormB
|
||||
|
||||
from .models import (
|
||||
Dealer,
|
||||
DealersMake,
|
||||
# Branch,
|
||||
Vendor,
|
||||
Schedule,
|
||||
@ -187,6 +188,7 @@ class CarForm(
|
||||
"vendor",
|
||||
]
|
||||
widgets = {
|
||||
"id_car_make": forms.Select(attrs={"class": "form-select form-select-sm"}),
|
||||
"receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
|
||||
"remarks": forms.Textarea(attrs={"rows": 2}),
|
||||
}
|
||||
@ -930,4 +932,24 @@ class DealerSettingsForm(forms.ModelForm):
|
||||
fields = "__all__"
|
||||
|
||||
class LeadTransferForm(forms.Form):
|
||||
transfer_to = forms.ModelChoiceField(label="to",queryset=Staff.objects.all())
|
||||
transfer_to = forms.ModelChoiceField(label="to",queryset=Staff.objects.all())
|
||||
|
||||
|
||||
class DealersMakeForm(forms.Form):
|
||||
car_makes = forms.ModelMultipleChoiceField(
|
||||
queryset=CarMake.objects.filter(is_sa_import=True),
|
||||
widget=forms.CheckboxSelectMultiple(attrs={"class": "car-makes-grid"}),
|
||||
required=True,
|
||||
label=_("Select Car Makes")
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.dealer = kwargs.pop("dealer", None) # Pass dealer instance
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def save(self):
|
||||
if self.dealer:
|
||||
DealersMake.objects.filter(dealer=self.dealer).delete()
|
||||
|
||||
for car_make in self.cleaned_data["car_makes"]:
|
||||
DealersMake.objects.create(dealer=self.dealer, car_make=car_make)
|
||||
|
||||
26
inventory/migrations/0054_dealersmake.py
Normal file
@ -0,0 +1,26 @@
|
||||
# Generated by Django 5.1.6 on 2025-03-03 16:59
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('inventory', '0053_lead_crn_lead_vrn'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='DealersMake',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('added_at', models.DateTimeField(auto_now_add=True)),
|
||||
('car_make', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='car_dealers', to='inventory.carmake')),
|
||||
('dealer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='dealer_makes', to='inventory.dealer')),
|
||||
],
|
||||
options={
|
||||
'unique_together': {('dealer', 'car_make')},
|
||||
},
|
||||
),
|
||||
]
|
||||
@ -53,7 +53,8 @@ class DealerUserManager(UserManager):
|
||||
return user
|
||||
|
||||
|
||||
|
||||
# class DealerMakes(models.Model):
|
||||
# car_make = models.ManyToManyField(CarMake, verbose_name=_("Car Make"), related_name="dealers")
|
||||
|
||||
|
||||
class StaffUserManager(UserManager):
|
||||
@ -924,6 +925,18 @@ class Dealer(models.Model, LocalizedNameMixin):
|
||||
# return self.parent_dealer if self.parent_dealer else self
|
||||
|
||||
|
||||
class DealersMake(models.Model):
|
||||
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="dealer_makes")
|
||||
car_make = models.ForeignKey(CarMake, on_delete=models.CASCADE, related_name="car_dealers")
|
||||
added_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
class Meta:
|
||||
unique_together = ("dealer", "car_make") # Prevents duplicate entries
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.dealer.name} - {self.car_make.name}"
|
||||
|
||||
|
||||
##############################
|
||||
# Additional staff types for later
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import json
|
||||
|
||||
from django_ledger.models import EntityModel
|
||||
|
||||
from inventory.utils import get_jwt_token
|
||||
from inventory.utils import get_jwt_token, get_user_type
|
||||
from pyvin import VIN
|
||||
from django.conf import settings
|
||||
from openai import OpenAI
|
||||
@ -43,8 +43,8 @@ def decodevin(vin):
|
||||
return result
|
||||
elif result:=elm(vin):
|
||||
return result
|
||||
elif result:=decode_vin_haikalna(vin):
|
||||
return result
|
||||
# elif result:=decode_vin_haikalna(vin):
|
||||
# return result
|
||||
else:
|
||||
return None
|
||||
|
||||
@ -85,12 +85,3 @@ def elm(vin):
|
||||
}
|
||||
print(data)
|
||||
return data if all([x for x in data.values()]) else None
|
||||
|
||||
|
||||
def get_ledger_data(request):
|
||||
data = {}
|
||||
entity = EntityModel.objects.filter(name=request.user.dealer.name).first()
|
||||
data['bills'] = entity.get_bills()
|
||||
data['invoices'] = entity.get_invoices()
|
||||
data['income'] = entity.get_income_statement()
|
||||
return data
|
||||
@ -184,7 +184,7 @@ def income_statement_table(context, io_model, from_date=None, to_date=None):
|
||||
'tx_digest': io_digest.get_io_data()
|
||||
}
|
||||
|
||||
@register.inclusion_tag('django_ledger/financial_statements/tags/cash_flow_statement.html', takes_context=True)
|
||||
@register.inclusion_tag('ledger/reports/tags/cash_flow_statement.html', takes_context=True)
|
||||
def cash_flow_statement(context, io_model):
|
||||
user_model = context['user']
|
||||
entity_slug = context['view'].kwargs.get('entity_slug')
|
||||
|
||||
@ -46,6 +46,7 @@ urlpatterns = [
|
||||
# Dashboards
|
||||
# path("user/<int:pk>/settings/", views.UserSettingsView.as_view(), name="user_settings"),
|
||||
path("dealer/<int:pk>/settings/", views.DealerSettingsView, name="dealer_settings"),
|
||||
path("dealer/assign-car-makes/", views.assign_car_makes, name="assign_car_makes"),
|
||||
path("dashboards/manager/", views.ManagerDashboard.as_view(), name="manager_dashboard"),
|
||||
path("dashboards/sales/", views.SalesDashboard.as_view(), name="sales_dashboard"),
|
||||
path("test/", views.TestView.as_view(), name="test"),
|
||||
|
||||
@ -846,4 +846,5 @@ def get_local_name(self):
|
||||
"""
|
||||
if get_language() == 'ar':
|
||||
return getattr(self, 'arabic_name', None)
|
||||
return getattr(self, 'name', None)
|
||||
return getattr(self, 'name', None)
|
||||
|
||||
|
||||
@ -262,57 +262,57 @@ class HomeView(LoginRequiredMixin,TemplateView):
|
||||
return redirect("welcome")
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
dealer = get_user_type(self.request)
|
||||
|
||||
try:
|
||||
# Fetch car-related statistics
|
||||
total_cars = models.Car.objects.filter(dealer=dealer).count()
|
||||
total_reservations = models.CarReservation.objects.filter(
|
||||
reserved_until__gte=timezone.now()
|
||||
).count()
|
||||
cars_in_house = models.CarLocation.objects.filter(
|
||||
owner=dealer,
|
||||
).count()
|
||||
cars_outside = total_cars - cars_in_house
|
||||
|
||||
# Fetch financial statistics
|
||||
stats = models.CarFinance.objects.aggregate(
|
||||
total_cost_price=Sum("cost_price"),
|
||||
total_selling_price=Sum("selling_price"),
|
||||
)
|
||||
total_cost_price = stats.get("total_cost_price", 0) or 0
|
||||
total_selling_price = stats.get("total_selling_price", 0) or 0
|
||||
total_profit = total_selling_price - total_cost_price
|
||||
|
||||
# Prepare context data
|
||||
context.update({
|
||||
"dealer": dealer,
|
||||
"total_cars": total_cars,
|
||||
"cars_in_house": cars_in_house,
|
||||
"cars_outside": cars_outside,
|
||||
"total_reservations": total_reservations,
|
||||
"total_cost_price": total_cost_price,
|
||||
"total_selling_price": total_selling_price,
|
||||
"total_profit": total_profit,
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
# Log the error (you can use Django's logging framework)
|
||||
print(f"Error fetching data: {e}")
|
||||
# Provide default values in case of an error
|
||||
context.update({
|
||||
"dealer": dealer,
|
||||
"total_cars": 0,
|
||||
"cars_in_house": 0,
|
||||
"cars_outside": 0,
|
||||
"total_reservations": 0,
|
||||
"total_cost_price": 0,
|
||||
"total_selling_price": 0,
|
||||
"total_profit": 0,
|
||||
})
|
||||
return context
|
||||
# def get_context_data(self, **kwargs):
|
||||
# context = super().get_context_data(**kwargs)
|
||||
# dealer = get_user_type(self.request)
|
||||
#
|
||||
# try:
|
||||
# # Fetch car-related statistics
|
||||
# total_cars = models.Car.objects.filter(dealer=dealer).count()
|
||||
# total_reservations = models.CarReservation.objects.filter(
|
||||
# reserved_until__gte=timezone.now()
|
||||
# ).count()
|
||||
# cars_in_house = models.CarLocation.objects.filter(
|
||||
# owner=dealer,
|
||||
# ).count()
|
||||
# cars_outside = total_cars - cars_in_house
|
||||
#
|
||||
# # Fetch financial statistics
|
||||
# stats = models.CarFinance.objects.aggregate(
|
||||
# total_cost_price=Sum("cost_price"),
|
||||
# total_selling_price=Sum("selling_price"),
|
||||
# )
|
||||
# total_cost_price = stats.get("total_cost_price", 0) or 0
|
||||
# total_selling_price = stats.get("total_selling_price", 0) or 0
|
||||
# total_profit = total_selling_price - total_cost_price
|
||||
#
|
||||
# # Prepare context data
|
||||
# context.update({
|
||||
# "dealer": dealer,
|
||||
# "total_cars": total_cars,
|
||||
# "cars_in_house": cars_in_house,
|
||||
# "cars_outside": cars_outside,
|
||||
# "total_reservations": total_reservations,
|
||||
# "total_cost_price": total_cost_price,
|
||||
# "total_selling_price": total_selling_price,
|
||||
# "total_profit": total_profit,
|
||||
# })
|
||||
#
|
||||
# except Exception as e:
|
||||
# # Log the error (you can use Django's logging framework)
|
||||
# print(f"Error fetching data: {e}")
|
||||
# # Provide default values in case of an error
|
||||
# context.update({
|
||||
# "dealer": dealer,
|
||||
# "total_cars": 0,
|
||||
# "cars_in_house": 0,
|
||||
# "cars_outside": 0,
|
||||
# "total_reservations": 0,
|
||||
# "total_cost_price": 0,
|
||||
# "total_selling_price": 0,
|
||||
# "total_profit": 0,
|
||||
# })
|
||||
# return context
|
||||
|
||||
class TestView(TemplateView):
|
||||
template_name = "inventory/cars_list_api.html"
|
||||
@ -321,15 +321,14 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
|
||||
template_name = "dashboards/manager.html"
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if (
|
||||
not request.user.is_authenticated
|
||||
):
|
||||
if not request.user.is_authenticated:
|
||||
return redirect("welcome")
|
||||
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
|
||||
total_cars = models.Car.objects.filter(dealer=dealer).count()
|
||||
total_reservations = models.CarReservation.objects.filter(
|
||||
reserved_until__gte=timezone.now()
|
||||
@ -355,6 +354,13 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
|
||||
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)
|
||||
total_activity = models.UserActivityLog.objects.filter(user=dealer.user).count()
|
||||
staff = models.Staff.objects.filter(dealer=dealer).count()
|
||||
total_leads = models.Lead.objects.filter(dealer=dealer).count()
|
||||
invoices = entity.get_invoices().count()
|
||||
customers = entity.get_customers().count()
|
||||
purchase_orders = entity.get_purchase_orders().count()
|
||||
estimates = entity.get_estimates().count()
|
||||
|
||||
context["dealer"] = dealer
|
||||
context["total_cars"] = total_cars
|
||||
@ -374,6 +380,13 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
|
||||
context['damaged_cars'] = damaged_cars
|
||||
context['transfer_cars'] = transfer_cars
|
||||
context['car'] = json.dumps(car_by_make)
|
||||
context['customers'] = customers
|
||||
context['staff'] = staff
|
||||
context['total_leads'] = total_leads
|
||||
context['invoices'] = invoices
|
||||
context['estimates'] = estimates
|
||||
context['purchase_orders'] = purchase_orders
|
||||
|
||||
|
||||
return context
|
||||
|
||||
@ -425,6 +438,11 @@ class SalesDashboard(LoginRequiredMixin, TemplateView):
|
||||
context['damaged_cars'] = damaged_cars
|
||||
context['transfer_cars'] = transfer_cars
|
||||
context['car'] = json.dumps(car_by_make)
|
||||
# context['customers'] = customers
|
||||
# context['staff'] = staff
|
||||
# context['total_leads'] = total_leads
|
||||
# context['invoices'] = invoices
|
||||
|
||||
|
||||
return context
|
||||
|
||||
@ -1211,13 +1229,20 @@ class DealerDetailView(LoginRequiredMixin, DetailView):
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
dealer = self.object
|
||||
car_makes = models.CarMake.objects.filter(car_dealers__dealer=dealer)
|
||||
# Fetch current staff count from the annotated queryset
|
||||
staff_count = dealer.staff_count
|
||||
cars_count = models.Car.objects.filter(dealer=dealer).count()
|
||||
# Get the quota value dynamically
|
||||
quota_dict = get_user_quota(dealer.user)
|
||||
allowed_users = quota_dict.get("Users", None) # Fetch quota or default to None
|
||||
|
||||
allowed_users = quota_dict.get("Users", None)
|
||||
allowed_cars = quota_dict.get("Cars", None)
|
||||
context["car_makes"] = car_makes
|
||||
context["staff_count"] = staff_count
|
||||
context["cars_count"] = cars_count
|
||||
context["allowed_users"] = allowed_users
|
||||
context["allowed_cars"] = allowed_cars
|
||||
context["quota_display"] = f"{staff_count}/{allowed_users}" if allowed_users is not None else "N/A"
|
||||
|
||||
return context
|
||||
@ -2320,6 +2345,7 @@ class EstimatePreviewView(LoginRequiredMixin,PermissionRequiredMixin,DetailView)
|
||||
permission_required = ['django_ledger.view_estimatemodel']
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
dealer = get_user_type(self.request)
|
||||
estimate = kwargs.get("object")
|
||||
if estimate.get_itemtxs_data():
|
||||
data = get_financial_values(estimate)
|
||||
@ -2329,6 +2355,7 @@ class EstimatePreviewView(LoginRequiredMixin,PermissionRequiredMixin,DetailView)
|
||||
kwargs["discount_amount"] = data["discount_amount"]
|
||||
kwargs["vat"] = data["vat"]
|
||||
kwargs["additional_services"] = data["additional_services"]
|
||||
kwargs["dealer"] = dealer
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
@ -3557,53 +3584,50 @@ class OrderListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
|
||||
@login_required
|
||||
@permission_required("django_ledger.view_estimate", raise_exception=True)
|
||||
def send_email_view(request, pk):
|
||||
dealer = get_user_type(request)
|
||||
estimate = get_object_or_404(EstimateModel, pk=pk)
|
||||
if request.method == "POST":
|
||||
if not estimate.get_itemtxs_data()[0]:
|
||||
messages.error(request, _("Estimate has no items"))
|
||||
return redirect("estimate_detail", pk=estimate.pk)
|
||||
|
||||
send_email(
|
||||
"manager@tenhal.com",
|
||||
request.POST.get("to"),
|
||||
request.POST.get("subject"),
|
||||
request.POST.get("message"),
|
||||
)
|
||||
estimate.mark_as_review()
|
||||
messages.success(request, _("Email sent successfully!"))
|
||||
if not estimate.get_itemtxs_data()[0]:
|
||||
messages.error(request, _("Quotation has no items"))
|
||||
return redirect("estimate_detail", pk=estimate.pk)
|
||||
link = reverse_lazy("estimate_preview", kwargs={"pk": estimate.pk})
|
||||
|
||||
link = request.build_absolute_uri(reverse_lazy("estimate_preview", kwargs={"pk": estimate.pk}))
|
||||
|
||||
msg = f"""
|
||||
السلام عليكم
|
||||
Dear {estimate.customer.customer_name},
|
||||
|
||||
أود أن أشارككم تقدير المشروع الذي ناقشناه. يرجى العثور على الوثيقة التفصيلية للمقترح المرفقة.
|
||||
أود أن أشارككم عرض السعر.
|
||||
|
||||
I hope this email finds you well. I wanted to share with you the estimate for the project we discussed. Please find the detailed estimate document attached.
|
||||
I wanted to share with you the quotation.
|
||||
|
||||
يرجى مراجعة المقترح وإعلامي إذا كانت لديك أي أسئلة أو مخاوف. إذا كانت كل شيء يبدو جيدًا، يمكننا المضي قدمًا في المشروع.
|
||||
يرجى مراجعة عرض السعر وإعلامي إذا كانت لديك أي استفسارات أو ملاحظات. إذا كان كل شيء على ما يرام، يمكننا المتابعة في الإجراءات.
|
||||
|
||||
Please review the estimate and let me know if you have any questions or concerns. If everything looks good, we can proceed with the project.
|
||||
Please review the quotation and let me know if you have any questions or concerns. If everything looks good, we can proceed with the process.
|
||||
|
||||
Estimate Link:
|
||||
رابط عرض السعر:
|
||||
{link}
|
||||
|
||||
شكراً لاهتمامكم بهذا الأمر.
|
||||
Thank you for your attention to this matter.
|
||||
|
||||
تحياتي,
|
||||
Best regards,
|
||||
[Your Name]
|
||||
[Your Position]
|
||||
[Your Company]
|
||||
[Your Contact Information]
|
||||
{dealer.get_local_name}
|
||||
{dealer.phone_number}
|
||||
هيكل | Haikal
|
||||
"""
|
||||
return render(
|
||||
request,
|
||||
"sales/estimates/estimate_send.html",
|
||||
{"estimate": estimate, "message": msg},
|
||||
|
||||
send_email(
|
||||
settings.DEFAULT_FROM_EMAIL,
|
||||
estimate.customer.email,
|
||||
_("Quotation"),
|
||||
msg,
|
||||
)
|
||||
|
||||
estimate.mark_as_review()
|
||||
messages.success(request, _("Email sent successfully!"))
|
||||
|
||||
return redirect("estimate_detail", pk=estimate.pk)
|
||||
|
||||
|
||||
# errors
|
||||
def custom_page_not_found_view(request, exception=None):
|
||||
@ -3993,4 +4017,20 @@ def schedule_cancel(request,pk):
|
||||
schedule.save()
|
||||
response = HttpResponse()
|
||||
response.status_code = 200
|
||||
return response
|
||||
return response
|
||||
|
||||
|
||||
@login_required
|
||||
def assign_car_makes(request):
|
||||
dealer = get_user_type(request)
|
||||
if request.method == "POST":
|
||||
form = forms.DealersMakeForm(request.POST, dealer=dealer)
|
||||
if form.is_valid():
|
||||
form.save()
|
||||
return redirect("dealer_detail", pk=dealer.pk)
|
||||
else:
|
||||
# Pre-fill the form with existing selections
|
||||
existing_car_makes = models.DealersMake.objects.filter(dealer=dealer).values_list("car_make", flat=True)
|
||||
form = forms.DealersMakeForm(initial={"car_makes": existing_car_makes}, dealer=dealer)
|
||||
|
||||
return render(request, "dealers/assign_car_makes.html", {"form": form})
|
||||
BIN
static/.DS_Store
vendored
BIN
static/assets/fonts/SaudiRiyalFont.ttf
Normal file
@ -1,4 +1,13 @@
|
||||
@font-face {
|
||||
font-family: 'SaudiRiyalFont';
|
||||
src: url('/static/assets/fonts/SaudiRiyalFont.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.currency {
|
||||
font-family: 'SaudiRiyalFont', sans-serif;
|
||||
}
|
||||
|
||||
.color-div {
|
||||
width: 64px;
|
||||
|
||||
@ -1259,6 +1259,7 @@ progress {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
|
||||
.display-1 {
|
||||
font-size: calc(1.6018371582rem + 4.2220458984vw);
|
||||
font-weight: 400;
|
||||
|
||||
BIN
static/images/car_make/Chevrolet_ZUJVQuH.png
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
static/images/car_make/Citroen.png
Normal file
|
After Width: | Height: | Size: 48 KiB |
BIN
static/images/car_make/Daewoo.png
Normal file
|
After Width: | Height: | Size: 46 KiB |
BIN
static/images/car_make/Daihatsu.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
static/images/car_make/Datsun.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
static/images/car_make/Dongfeng.png
Normal file
|
After Width: | Height: | Size: 109 KiB |
BIN
static/images/car_make/EXEED.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/images/car_make/Ferrari.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
static/images/car_make/Fiat.png
Normal file
|
After Width: | Height: | Size: 3.9 KiB |
BIN
static/images/car_make/Forthing.png
Normal file
|
After Width: | Height: | Size: 142 KiB |
BIN
static/images/car_make/Foton.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
static/images/car_make/GAC.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
static/images/car_make/GMC.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
static/images/car_make/GWM.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
static/images/car_make/Geely.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/car_make/Genesis.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/images/car_make/Honda.png
Normal file
|
After Width: | Height: | Size: 9.5 KiB |
BIN
static/images/car_make/Hongqi.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
static/images/car_make/Hummer.png
Normal file
|
After Width: | Height: | Size: 3.7 KiB |
BIN
static/images/car_make/Hyundai.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
static/images/car_make/Infiniti.png
Normal file
|
After Width: | Height: | Size: 9.2 KiB |
BIN
static/images/car_make/Isuzu.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
static/images/car_make/JAC.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
static/images/car_make/JMC.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
static/images/car_make/Jaguar.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
static/images/car_make/Jetour.png
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
static/images/car_make/Lamborghini.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
static/images/car_make/Land-Rover.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
BIN
static/images/car_make/Lexus.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/images/car_make/Lucid.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
static/images/car_make/LynkCo.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
BIN
static/images/car_make/MG.png
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
static/images/car_make/Maxus.png
Normal file
|
After Width: | Height: | Size: 77 KiB |
BIN
static/images/car_make/Maybach.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
static/images/car_make/Mazda.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
static/images/car_make/McLaren.png
Normal file
|
After Width: | Height: | Size: 5.6 KiB |
BIN
static/images/car_make/Mercedes-Benz.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
static/images/car_make/Mercury.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
static/images/car_make/Mitsubishi.png
Normal file
|
After Width: | Height: | Size: 24 KiB |
BIN
static/images/car_make/Opel.png
Normal file
|
After Width: | Height: | Size: 27 KiB |
BIN
static/images/car_make/Pontiac.png
Normal file
|
After Width: | Height: | Size: 30 KiB |
BIN
static/images/car_make/Porsche.png
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
static/images/car_make/Rayton-Fissore.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
static/images/car_make/Renault.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
static/images/car_make/Rolls-Royce.png
Normal file
|
After Width: | Height: | Size: 33 KiB |
BIN
static/images/car_make/Rover.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
static/images/car_make/Saab.png
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
static/images/car_make/Seat.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
static/images/car_make/Seres.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
static/images/car_make/Skoda.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
static/images/car_make/Smart.png
Normal file
|
After Width: | Height: | Size: 9.6 KiB |
BIN
static/images/car_make/Spectre.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
BIN
static/images/car_make/Ssangyong.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
static/images/car_make/Stelato.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
static/images/car_make/Subaru.png
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
static/images/car_make/Suzuki.png
Normal file
|
After Width: | Height: | Size: 21 KiB |
BIN
static/images/car_make/TATA.png
Normal file
|
After Width: | Height: | Size: 9.4 KiB |
BIN
static/images/car_make/Tank.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
BIN
static/images/car_make/Tesla.png
Normal file
|
After Width: | Height: | Size: 4.6 KiB |
BIN
static/images/car_make/Volkswagen.png
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
static/images/car_make/Volvo.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
static/images/car_make/Voyah.png
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
static/images/car_make/Zotye.png
Normal file
|
After Width: | Height: | Size: 64 KiB |
BIN
static/images/car_make/haval.png
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
static/images/car_make/mini.png
Normal file
|
After Width: | Height: | Size: 9.1 KiB |
BIN
static/images/car_make/zx.png
Normal file
|
After Width: | Height: | Size: 61 KiB |
3
static/vendors/tesseract/tesseract.min.js
vendored
Normal file
1
static/vendors/zxing/index.min.js
vendored
Normal file
@ -85,9 +85,9 @@
|
||||
<!-- Currency Field -->
|
||||
<div class="form-floating mb-3">
|
||||
<select name="currency" id="id_currency" class="form-select form-control-sm" >
|
||||
<option value="SAR">{{ CURRENCY }}</option>
|
||||
<option class="currency" value="SAR">{{ CURRENCY }}</option>
|
||||
</select>
|
||||
<label for="id_currency">{{ _("Currency") }}</label>
|
||||
<label for="id_currency"><span class="currency"> {{ CURRENCY }}</span></label>
|
||||
{% if form.currency.errors %}
|
||||
<div class="alert alert-danger mt-2">
|
||||
{{ form.currency.errors }}
|
||||
|
||||
@ -39,7 +39,7 @@
|
||||
<h3 class="fw-bolder mb-2 line-clamp-1">{{ opportunity.customer.customer_name }}</h3>
|
||||
<div class="d-flex align-items-center mb-4">
|
||||
{% if opportunity.car.finances %}
|
||||
<h5 class="mb-0 me-4">{{ opportunity.car.finances.total }} <span class="fw-light">{{ _("SAR") }}</span></h5>
|
||||
<h5 class="mb-0 me-4">{{ opportunity.car.finances.total }} <span class="fw-light"><span class="currency">{{ CURRENCY }}</span></span></h5>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="d-md-flex d-xl-block align-items-center justify-content-between mb-5">
|
||||
|
||||
@ -128,7 +128,7 @@
|
||||
|
||||
<p class="fw-semibold mb-0" >{{ _("Quotation") }}-<span class="fs-10 fw-medium">{{ estimate.estimate_number }}</span></p>
|
||||
</td>
|
||||
<td class="total align-middle text-end fw-semibold pe-7 text-body-highlight">{{ estimate.revenue_estimate|currency_format }}</td>
|
||||
<td class="total align-middle text-end fw-semibold pe-7 text-body-highlight">{{ estimate.revenue_estimate|currency_format }} <span class="currency"> {{ CURRENCY }}</span> </td>
|
||||
<td class="payment_status align-middle white-space-nowrap text-start fw-bold text-body-tertiary">
|
||||
|
||||
</td>
|
||||
@ -150,7 +150,7 @@
|
||||
|
||||
<p class="fw-semibold mb-0" >{{ _("Invoice") }}-<span class="fs-10 fw-medium">{{ invoice.invoice_number }}</span></p>
|
||||
</td>
|
||||
<td class="total align-middle text-end fw-semibold pe-7 text-body-highlight">{{ invoice.amount_paid|currency_format }}</td>
|
||||
<td class="total align-middle text-end fw-semibold pe-7 text-body-highlight">{{ invoice.amount_paid|currency_format }}<span class="currency"> {{ CURRENCY }}</span> </td>
|
||||
<td class="payment_status align-middle white-space-nowrap text-start fw-bold text-body-tertiary">
|
||||
{% if invoice.is_paid %}
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-success">
|
||||
|
||||
@ -2,13 +2,96 @@
|
||||
{% load i18n static custom_filters django_ledger%}
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="row g-2">
|
||||
<div class="row justify-content-between mb-2">
|
||||
<h3 class="fs-4 fs-md-4 fs-xl-4 fw-black mb-4">
|
||||
<span class="text-gradient-info me-3">{{ dealer.get_local_name }}</span>
|
||||
</h3>
|
||||
<span class="text-gradient-info me-3">{{ dealer.get_local_name }}</span>
|
||||
</h3>
|
||||
<p><span class="badge badge-phoenix badge-phoenix-success me-2 fs-10">
|
||||
<span class="fs-10 text-body-secondary me-1">{{ _("As of")}}</span>{% now "SHORT_DATETIME_FORMAT" %}
|
||||
</span></p>
|
||||
</div>
|
||||
<div class="row justify-content-between mb-2">
|
||||
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
|
||||
<span class="uil fs-5 lh-1 uil-users-alt text-success"></span>
|
||||
<h4 class="fs-6 pt-3">{{ staff }}</h4>
|
||||
<p class="fs-9 mb-0">{{ _("Staff")}}</p>
|
||||
</div>
|
||||
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
|
||||
<span class="uil fs-5 lh-1 uil-bolt-alt text-primary"></span>
|
||||
<a href="{% url 'lead_list' %}"><h4 class="fs-6 pt-3">{{ total_leads }}</h4></a>
|
||||
<p class="fs-9 mb-0">{{ _("Leads")}}</p>
|
||||
</div>
|
||||
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
|
||||
<span class="uil fs-5 lh-1 uil-user-plus text-warning"></span>
|
||||
<h4 class="fs-6 pt-3">{{ customers }}</h4>
|
||||
<p class="fs-9 mb-0">{{ _("Customers")}}</p>
|
||||
</div>
|
||||
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
|
||||
<span class="uil fs-5 lh-1 uil-bill text-info"></span>
|
||||
<h4 class="fs-6 pt-3">{{ invoices }}</h4>
|
||||
<p class="fs-9 mb-0">{{ _("Invoices")}}</p>
|
||||
</div>
|
||||
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
|
||||
<span class="uil fs-5 lh-1 uil-comment-alt-question text-success-dark"></span>
|
||||
<h4 class="fs-6 pt-3">{{ estimates }}</h4>
|
||||
<p class="fs-9 mb-0">{{ _("Quotations")}}</p>
|
||||
</div>
|
||||
<div class="col-6 col-md-4 col-xxl-2 text-center border-translucent border-start-xxl border-end-xxl-0 border-bottom-xxl-0 border-end border-bottom pb-4 pb-xxl-0 ">
|
||||
<span class="uil fs-5 lh-1 uil-receipt-alt text-secondary"></span>
|
||||
<h4 class="fs-6 pt-3">{{ purchase_orders }}</h4>
|
||||
<p class="fs-9 mb-0">{{ _("Purchase Orders")}}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3 pe-xxl-3 my-3">
|
||||
<div class="col-12 col-xl-6 col-xxl-12">
|
||||
<div class="row">
|
||||
<div class="col-4 col-xl-12 col-xxl-4 border-end border-end-xl-0 border-end-xxl pb-4 pt-4 pt-xl-0 pt-xxl-4 pe-4 pe-sm-5 pe-xl-0 pe-xxl-5">
|
||||
<h4 class="text-body mb-4">{% trans 'inventory'|upper %}</h4>
|
||||
<div class="d-md-flex flex-between-center">
|
||||
<div id="car-chart-by-make" class="order-sm-0 order-md-1" style="height:64px;width: 128px;"></div>
|
||||
<div class="mt-4 mt-md-0">
|
||||
<h1 class="text-body-highlight">{{ total_cars }} <span class="fs-6 text-body-highlight">{{ _("Car") }}</span></h1>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 col-xl-12 col-xxl-4 border-end border-end-xl-0 border-end-xxl py-4 ps-4 ps-sm-5 ps-xl-0 ps-xxl-5">
|
||||
<h4 class="text-body mb-4">{% trans 'inventory value'|upper %}</h4>
|
||||
<div class="d-md-flex flex-between-center">
|
||||
<div class="d-md-flex align-items-center gap-2">
|
||||
<span class="fas fa-money-check-alt fs-5 text-success-light dark__text-opacity-75"></span>
|
||||
<div class="d-flex d-md-block gap-2 align-items-center mt-1 mt-md-0">
|
||||
<p class="fs-9 mb-0 mb-md-2 text-body-tertiary text-nowrap"></p>
|
||||
<h4 class="text-body-highlight mb-0"></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mt-md-0">
|
||||
<h3 class="text-body-highlight mb-2">{{ total_selling_price|currency_format }} <span class="currency"> {{ CURRENCY }}</span></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-4 col-xl-12 col-xxl-4 border-end border-end-xl-0 border-end-xxl py-4 pe-4 pe-sm-5 pe-xl-0 pe-xxl-5">
|
||||
<h4 class="text-body mb-4">{% trans "Profits"|upper %}</h4>
|
||||
<div class="d-md-flex flex-between-center">
|
||||
<div class="d-md-flex align-items-center gap-2">
|
||||
<span class="fa-solid fa-money-bill-trend-up fs-5 text-warning-light dark__text-opacity-75" data-bs-theme="light"></span>
|
||||
<div class="d-flex d-md-block gap-2 align-items-center mt-1 mt-md-0">
|
||||
<p class="fs-9 mb-0 mb-md-2 text-body-tertiary text-nowrap"></p>
|
||||
<h4 class="text-body-highlight mb-0"></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mt-md-0">
|
||||
<h3 class="text-body-highlight mb-2">{{ total_profit|currency_format }}<span class="currency"> {{ CURRENCY }}</span></h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-12 col-lg-12">
|
||||
<div class="row">
|
||||
<div class="card mb-3">
|
||||
<div class="bg-holder" style="background-image:url({% static 'images/bg/38.png' %});background-position:left bottom;background-size:auto;"></div>
|
||||
|
||||
@ -56,194 +139,18 @@
|
||||
</div>
|
||||
<div class="col-sm-5 col-md-4 col-xxl-4 my-3 my-sm-0">
|
||||
<div class="position-relative d-flex flex-center mb-sm-4 mb-xl-0 echart-cars-by-status-container mt-sm-7 mt-lg-4 mt-xl-0">
|
||||
<div id="echart-cars-by-status" style="min-height:245px;width:100%"></div>
|
||||
<div class="position-absolute rounded-circle bg-primary-subtle top-50 start-50 translate-middle d-flex flex-center" style="height:100px; width:100px;">
|
||||
<h3 class="mb-0 text-primary-dark fw-bolder" data-label="data-label"></h3>
|
||||
</div>
|
||||
<div id="echart-cars-by-status" class="mx-auto mt-3 mt-md-0 mt-xl-3 mt-xxl-0" style="min-height:245px;width:100%"></div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-2">
|
||||
<div class="col-12 mb-8">
|
||||
<div class="mb-3">
|
||||
<h3>{{ _("New Leads and Customers")}}</h3>
|
||||
<p class="text-body-tertiary mb-0">{{ _("Payment received across all channels")}}</p>
|
||||
</div>
|
||||
<div class="row g-6">
|
||||
<div class="col-xl-6 mb-2 mb-sm-0">
|
||||
<div class="d-flex align-items-center"><span class="me-2 text-info" data-feather="users" style="min-height:24px; width:24px"></span>
|
||||
<h4 class="text-body-tertiary mb-0">{{ _("New Customers")}} :
|
||||
<span class="text-body-emphasis"> 42</span>
|
||||
</h4>
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-success d-inline-flex align-items-center ms-2">
|
||||
<span class="badge-label d-inline-block lh-base">+24.5%</span>
|
||||
<span class="ms-1 fa-solid fa-caret-up d-inline-block lh-1"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pb-0 pt-4">
|
||||
<div class="echarts-new-users" style="min-height:110px;width:100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="d-flex align-items-center"><span class="me-2 text-primary" data-feather="zap" style="height:24px; width:24px"></span>
|
||||
<h4 class="text-body-tertiary mb-0">{{ _("New Leads")}} :<span class="text-body-emphasis"> 45</span></h4>
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-success d-inline-flex align-items-center ms-2">
|
||||
<span class="badge-label d-inline-block lh-base">+30.5%</span>
|
||||
<span class="ms-1 fa-solid fa-caret-up d-inline-block lh-1"></span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="pb-0 pt-4">
|
||||
<div class="echarts-new-leads" style="min-height:110px;width:100%;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
|
||||
<div class="row g-3 pe-xxl-3">
|
||||
<div class="col-12 col-xl-6 col-xxl-12">
|
||||
<div class="row">
|
||||
<div class="col-6 col-xl-12 col-xxl-6 border-bottom border-end border-end-xl-0 border-end-xxl pb-4 pt-4 pt-xl-0 pt-xxl-4 pe-4 pe-sm-5 pe-xl-0 pe-xxl-5">
|
||||
<h4 class="text-body mb-4">{% trans 'inventory'|upper %}</h4>
|
||||
<div class="d-md-flex flex-between-center">
|
||||
<div id="car-chart-by-make" class="order-sm-0 order-md-1" style="height:64px;width: 128px;"></div>
|
||||
<div class="mt-4 mt-md-0">
|
||||
<h1 class="text-body-highlight mb-2">{{ total_cars }}</h1>
|
||||
|
||||
|
||||
<span class="badge badge-phoenix badge-phoenix-primary me-2 fs-10"> <span class="fs-10 text-body-secondary me-1">{{ _("As of")}}</span>{% now "SHORT_DATETIME_FORMAT" %}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-xl-12 col-xxl-6 border-bottom py-4 ps-4 ps-sm-5 ps-xl-0 ps-xxl-5">
|
||||
<h4 class="text-body mb-4">{% trans 'inventory value'|upper %}</h4>
|
||||
<div class="d-md-flex flex-between-center">
|
||||
<div class="d-md-flex align-items-center gap-2 order-sm-0 order-md-1 fa-2x align-items-center">
|
||||
<i class="fas fa-money-check-alt fs-4 text-success-light dark__text-opacity-75"></i>
|
||||
<div class="d-flex d-md-block gap-2 align-items-center mt-1 mt-md-0">
|
||||
<p class="fs-9 mb-0 mb-md-2 text-body-tertiary text-nowrap"></p>
|
||||
<h4 class="text-body-highlight mb-0"></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mt-md-0">
|
||||
<h3 class="text-body-highlight mb-2">{{ total_selling_price|currency_format }} {{ CURRENCY }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-xl-12 col-xxl-6 border-bottom-xl border-bottom-xxl-0 border-end border-end-xl-0 border-end-xxl py-4 pe-4 pe-sm-5 pe-xl-0 pe-xxl-5">
|
||||
<h4 class="text-body mb-4">{% trans "Profits"|upper %}</h4>
|
||||
<div class="d-md-flex flex-between-center">
|
||||
<div class="d-md-flex align-items-center gap-2 order-sm-0 order-md-1">
|
||||
<span class="fa-solid fa-money-bill-trend-up fs-4 text-warning-light dark__text-opacity-75" data-bs-theme="light"></span>
|
||||
<div class="d-flex d-md-block gap-2 align-items-center mt-1 mt-md-0">
|
||||
<p class="fs-9 mb-0 mb-md-2 text-body-tertiary text-nowrap"></p>
|
||||
<h4 class="text-body-highlight mb-0"></h4>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mt-md-0">
|
||||
<h3 class="text-body-highlight mb-2">{{ total_profit|currency_format }} {{ CURRENCY }}</h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-xl-12 col-xxl-6 py-4 ps-4 ps-sm-5 ps-xl-0 ps-xxl-5">
|
||||
<h5 class="text-body mb-4">{{ _("Canceled Invoices")}}</h5>
|
||||
<div class="d-md-flex flex-between-center">
|
||||
<div class="chart-cancel-booking order-sm-0 order-md-1" style="height:54px; width:78px"></div>
|
||||
<div class="mt-3 mt-md-0">
|
||||
<h3 class="text-body-highlight mb-2">120.00</h3>
|
||||
<span class="badge badge-phoenix badge-phoenix-danger me-2 fs-10"> <span class="fa-solid fa-plus me-1"></span>5.76%</span>
|
||||
<span class="fs-9 text-body-secondary d-block d-sm-inline mt-1">{{ _("From last month")}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-xl-6 col-xxl-12 mb-3">
|
||||
<div class="card h-100">
|
||||
<div class="card-header pb-3">
|
||||
<div class="row justify-content-between g-3">
|
||||
<div class="col-auto">
|
||||
<h3 class="text-body-highlight">{{ _("Gross Profit")}}</h3>
|
||||
<p class="mb-0">Annual income according to the board</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row align-items-center h-100 gy-5">
|
||||
<div class="col-12 col-md-auto col-xl-12 col-xxl-auto order-md-1 order-xl-0 order-xxl-1 px-md-8 px-xl-6">
|
||||
<div class="echart-gross-profit mx-auto mt-3 mt-md-0 mt-xl-3 mt-xxl-0" style="width: 250px; height: 250px"></div>
|
||||
</div>
|
||||
<div class="col-12 col-md-auto col-xl-12 col-xxl-auto flex-1 h-md-100">
|
||||
<div class="d-flex flex-column justify-content-between h-md-100 h-xl-auto h-xxl-100">
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex gap-2">
|
||||
<div class="bullet-item bg-primary-light" data-bs-theme="light"></div>
|
||||
<div>
|
||||
<h6 class="mb-0 text-body fw-semibold mb-2">Flight</h6>
|
||||
<h5 class="mb-0 text-body">$162,791,400</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 text-primary">
|
||||
<span class="fw-bold" data-feather="trending-up" style="width: 24px; height: 24px"></span>
|
||||
<p class="mb-0 fw-bold">15.50%</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex gap-2">
|
||||
<div class="bullet-item bg-info-light" data-bs-theme="light"></div>
|
||||
<div>
|
||||
<h6 class="mb-0 text-body fw-semibold mb-2">Flight (Package)</h6>
|
||||
<h5 class="mb-0 text-body">$135,659,500</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 text-danger">
|
||||
<span class="fw-bold" data-feather="trending-down" style="width: 24px; height: 24px"></span>
|
||||
<p class="mb-0 fw-bold">11.09%</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex gap-2">
|
||||
<div class="bullet-item bg-warning-light" data-bs-theme="light"></div>
|
||||
<div>
|
||||
<h6 class="mb-0 text-body fw-semibold mb-2">Hotel</h6>
|
||||
<h5 class="mb-0 text-body">$271,319,000</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 text-warning">
|
||||
<span class="fw-bold" data-feather="trending-up" style="width: 24px; height: 24px"></span>
|
||||
<p class="mb-0 fw-bold">29.98%</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="d-flex align-items-center justify-content-between">
|
||||
<div class="d-flex gap-2">
|
||||
<div class="bullet-item bg-success-light" data-bs-theme="light"></div>
|
||||
<div>
|
||||
<h6 class="mb-0 text-body fw-semibold mb-2">Hotel (Package)</h6>
|
||||
<h5 class="mb-0 text-body">$162,791,400</h5>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex align-items-center gap-2 text-success">
|
||||
<span class="fw-bold" data-feather="trending-up" style="width: 24px; height: 24px"></span>
|
||||
<p class="mb-0 fw-bold">03.90%</p>
|
||||
</div>
|
||||
</div>
|
||||
<hr class="d-none" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -252,9 +159,7 @@
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
/* Car Chart By Make */
|
||||
const getColor = (name, dom = document.documentElement) => {
|
||||
return getComputedStyle(dom).getPropertyValue(`--phoenix-${name}`).trim();
|
||||
};
|
||||
const { getColor, rgbaColor } = window.phoenix.utils;
|
||||
const handleTooltipPosition = ([pos, , dom, , size]) => {
|
||||
// only for mobile device
|
||||
if (window.innerWidth <= 540) {
|
||||
@ -326,9 +231,7 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
car_chart.setOption(option);
|
||||
|
||||
/* Car Status Chart */
|
||||
const chartElContainer = document.querySelector('.echart-cars-by-status-container');
|
||||
const car_status = echarts.init(document.getElementById('echart-cars-by-status'));
|
||||
const chartLabel = chartElContainer.querySelector('[data-label]');
|
||||
const data = [
|
||||
{value: {{available_cars}}, name: '{{ _("Available") }}'},
|
||||
{value: {{sold_cars}}, name: '{{ _("Sold")}}'},
|
||||
@ -337,24 +240,21 @@ document.addEventListener("DOMContentLoaded", function () {
|
||||
{value: {{hold_cars}}, name: '{{ _("Hold") }}'},
|
||||
{value: {{damaged_cars}}, name: '{{ _("Damaged") }}'}
|
||||
];
|
||||
const totalCars = data.reduce((acc, val) => val.value + acc, 0);
|
||||
if (chartLabel) {
|
||||
chartLabel.innerHTML = totalCars;
|
||||
}
|
||||
|
||||
option = {
|
||||
color: [
|
||||
getColor('success'),
|
||||
getColor('warning'),
|
||||
getColor('danger'),
|
||||
getColor('primary'),
|
||||
getColor('warning-lighter'),
|
||||
getColor('secondary-dark')
|
||||
rgbaColor(getColor('success'),0.7),
|
||||
rgbaColor(getColor('warning'),0.7),
|
||||
rgbaColor(getColor('danger'),0.7),
|
||||
rgbaColor(getColor('primary'),0.7),
|
||||
rgbaColor(getColor('warning-light'),0.7),
|
||||
rgbaColor(getColor('secondary-light'),0.7),
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
padding: [7, 10],
|
||||
backgroundColor: getColor('body-highlight-bg'),
|
||||
borderColor: getColor('border-color'),
|
||||
borderColor: getColor('body-bg'),
|
||||
textStyle: {color: getColor('light-text-emphasis')},
|
||||
borderWidth: 1,
|
||||
transitionDuration: 0,
|
||||
|
||||
@ -132,7 +132,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mt-md-0">
|
||||
<h3 class="text-body-highlight mb-2">{{ total_selling_price|currency_format }} {{ CURRENCY }}</h3>
|
||||
<h3 class="text-body-highlight mb-2">{{ total_selling_price|currency_format }} <span class="currency"> {{ CURRENCY }}</span> </h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -147,7 +147,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-3 mt-md-0">
|
||||
<h3 class="text-body-highlight mb-2">{{ total_profit|currency_format }} {{ CURRENCY }}</h3>
|
||||
<h3 class="text-body-highlight mb-2">{{ total_profit|currency_format }} <span class="currency"> {{ CURRENCY }}</span> </h3>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
25
templates/dealers/assign_car_makes.html
Normal file
@ -0,0 +1,25 @@
|
||||
{% extends "base.html" %}
|
||||
{% load crispy_forms_filters %}
|
||||
|
||||
{% block content %}
|
||||
<style>
|
||||
.car-makes-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
.car-makes-grid label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
<h2>{{ _("Select Car Makes You Sell")}}</h2>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
<div class="car-makes-grid">
|
||||
{{ form.car_makes }}
|
||||
</div>
|
||||
<button class="btn btn-phoenix-success btn-sm" type="submit">{{ _("Save") }}</button>
|
||||
</form>
|
||||
{% endblock %}
|
||||
@ -44,10 +44,14 @@
|
||||
<h6 class="mb-2 text-body-secondary">{% trans 'last login'|capfirst %}</h6>
|
||||
<h4 class="fs-7 text-body-highlight mb-0">{{ dealer.user.last_login|date:"D M d, Y H:i" }}</h4>
|
||||
</div>
|
||||
<div class="text-end">
|
||||
<div class="text-center me-1">
|
||||
<h6 class="mb-2 text-body-secondary">{% trans 'Total users'|capfirst %}</h6>
|
||||
<h4 class="fs-7 text-body-highlight mb-0">{{ dealer.staff_count }} / {{ allowed_users }}</h4>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<h6 class="mb-2 text-body-secondary">{% trans 'Total cars'|capfirst %}</h6>
|
||||
<h4 class="fs-7 text-body-highlight mb-0">{{ cars_count }} / {{ allowed_cars }}</h4>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -104,7 +108,7 @@
|
||||
</div>
|
||||
<p class="fs-9 text-body-tertiary">{% trans 'Active until' %}: {{ dealer.user.userplan.expire}}</p>
|
||||
<div class="d-flex align-items-end mb-md-5 mb-lg-0">
|
||||
<h4 class="fw-bolder me-1">{{ dealer.user.userplan.plan.planpricing_set.first.price }} {{ CURRENCY }}</h4>
|
||||
<h4 class="fw-bolder me-1">{{ dealer.user.userplan.plan.planpricing_set.first.price }}<span class="currency"> {{ CURRENCY }}</span></h4>
|
||||
<h5 class="fs-9 fw-normal text-body-tertiary ms-1">{{ _("Per month")}}</h5>
|
||||
</div>
|
||||
</div>
|
||||
@ -130,13 +134,26 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-3">
|
||||
<div class="card h-100">
|
||||
<div class="bg-holder" style="background-image:url({% static 'images/bg/bg-2.png' %});background-position:left bottom;background-size:auto;"></div>
|
||||
<div class="card-body d-flex flex-column justify-content-between position-relative">
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="mb-5 mb-md-0 mb-lg-5">
|
||||
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="bg-holder" style="background-image:url({% static 'images/bg/bg-left-20.png' %});background-position:left bottom;background-size:auto;"></div>
|
||||
<div class="card-body d-flex flex-column justify-content-center position-relative">
|
||||
<h4 class="mb-3">{{ _("Makes you are selling") }}</h4>
|
||||
<div class="d-flex justify-content-center ">
|
||||
<div class="text-center me-3">
|
||||
<div class="row">
|
||||
{% for make in car_makes %}
|
||||
<div class="col my-1">
|
||||
{% if make.logo %}
|
||||
<img src="{{ make.logo.url }}" alt="{{ make.get_local_name }}" class="rounded rounded-1" style="height: 64px;" />
|
||||
{% endif %}
|
||||
<p class="fs-10">{{ make.get_local_name }}</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -6,42 +6,23 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row my-5">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<!-- Form Header -->
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">{{ _("Update Dealer Information") }}</h4>
|
||||
</div>
|
||||
<div class="col-md-8">
|
||||
<!-- Form Header -->
|
||||
|
||||
<!-- Form Body -->
|
||||
<div class="card-body">
|
||||
<form method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<h4 class="mb-3">{{ _("Update Dealer Information") }}</h4>
|
||||
|
||||
<!-- Save Button -->
|
||||
<div class="d-grid gap-2 mt-3">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-save"></i> {{ _("Save Changes") }}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<form method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<div class="gap-2 mt-3">
|
||||
<button type="submit" class="btn btn-phoenix-primary btn-sm">
|
||||
<i class="fa fa-save"></i> {{ _("Save") }}
|
||||
</button>
|
||||
<a href="{% url 'dealer_detail' dealer.pk %}" class="btn btn-sm btn-phoenix-secondary">
|
||||
<i class="fas fa-times"></i> {{ _("Cancel") }}</a>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<!-- Cancel Button -->
|
||||
<div class="text-center mt-4">
|
||||
<a href="{% url 'dealer_detail' dealer.pk %}" class="btn btn-secondary">
|
||||
{{ _("Back") }}
|
||||
{% if LANGUAGE_CODE == 'ar' %}
|
||||
<i class="bi bi-arrow-left-circle"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-arrow-right-circle"></i>
|
||||
{% endif %}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||