This commit is contained in:
gitea 2025-03-04 21:14:47 +00:00
commit 034d0a9501
118 changed files with 1510 additions and 1505 deletions

View File

@ -65,6 +65,7 @@ admin.site.register(models.Lead)
admin.site.register(models.Activity) admin.site.register(models.Activity)
admin.site.register(models.Schedule) admin.site.register(models.Schedule)
admin.site.register(models.Notes) admin.site.register(models.Notes)
admin.site.register(models.UserActivityLog)
# admin.site.register(appointment_models.Client) # admin.site.register(appointment_models.Client)

View File

@ -25,6 +25,7 @@ from django_ledger.forms.bill import BillModelCreateForm as BillModelCreateFormB
from .models import ( from .models import (
Dealer, Dealer,
DealersMake,
# Branch, # Branch,
Vendor, Vendor,
Schedule, Schedule,
@ -187,6 +188,7 @@ class CarForm(
"vendor", "vendor",
] ]
widgets = { widgets = {
"id_car_make": forms.Select(attrs={"class": "form-select form-select-sm"}),
"receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}), "receiving_date": forms.DateTimeInput(attrs={"type": "datetime-local"}),
"remarks": forms.Textarea(attrs={"rows": 2}), "remarks": forms.Textarea(attrs={"rows": 2}),
} }
@ -930,4 +932,24 @@ class DealerSettingsForm(forms.ModelForm):
fields = "__all__" fields = "__all__"
class LeadTransferForm(forms.Form): 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)

View 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')},
},
),
]

View File

@ -53,7 +53,8 @@ class DealerUserManager(UserManager):
return user return user
# class DealerMakes(models.Model):
# car_make = models.ManyToManyField(CarMake, verbose_name=_("Car Make"), related_name="dealers")
class StaffUserManager(UserManager): class StaffUserManager(UserManager):
@ -924,6 +925,18 @@ class Dealer(models.Model, LocalizedNameMixin):
# return self.parent_dealer if self.parent_dealer else self # 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 # Additional staff types for later

View File

@ -7,7 +7,7 @@ import json
from django_ledger.models import EntityModel 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 pyvin import VIN
from django.conf import settings from django.conf import settings
from openai import OpenAI from openai import OpenAI
@ -43,8 +43,8 @@ def decodevin(vin):
return result return result
elif result:=elm(vin): elif result:=elm(vin):
return result return result
elif result:=decode_vin_haikalna(vin): # elif result:=decode_vin_haikalna(vin):
return result # return result
else: else:
return None return None
@ -85,12 +85,3 @@ def elm(vin):
} }
print(data) print(data)
return data if all([x for x in data.values()]) else None 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

View File

@ -184,7 +184,7 @@ def income_statement_table(context, io_model, from_date=None, to_date=None):
'tx_digest': io_digest.get_io_data() '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): def cash_flow_statement(context, io_model):
user_model = context['user'] user_model = context['user']
entity_slug = context['view'].kwargs.get('entity_slug') entity_slug = context['view'].kwargs.get('entity_slug')

View File

@ -46,6 +46,7 @@ urlpatterns = [
# Dashboards # Dashboards
# path("user/<int:pk>/settings/", views.UserSettingsView.as_view(), name="user_settings"), # path("user/<int:pk>/settings/", views.UserSettingsView.as_view(), name="user_settings"),
path("dealer/<int:pk>/settings/", views.DealerSettingsView, name="dealer_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/manager/", views.ManagerDashboard.as_view(), name="manager_dashboard"),
path("dashboards/sales/", views.SalesDashboard.as_view(), name="sales_dashboard"), path("dashboards/sales/", views.SalesDashboard.as_view(), name="sales_dashboard"),
path("test/", views.TestView.as_view(), name="test"), path("test/", views.TestView.as_view(), name="test"),

View File

@ -846,4 +846,5 @@ def get_local_name(self):
""" """
if get_language() == 'ar': if get_language() == 'ar':
return getattr(self, 'arabic_name', None) return getattr(self, 'arabic_name', None)
return getattr(self, 'name', None) return getattr(self, 'name', None)

View File

@ -262,57 +262,57 @@ class HomeView(LoginRequiredMixin,TemplateView):
return redirect("welcome") return redirect("welcome")
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs): # def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) # context = super().get_context_data(**kwargs)
dealer = get_user_type(self.request) # dealer = get_user_type(self.request)
#
try: # try:
# Fetch car-related statistics # # Fetch car-related statistics
total_cars = models.Car.objects.filter(dealer=dealer).count() # total_cars = models.Car.objects.filter(dealer=dealer).count()
total_reservations = models.CarReservation.objects.filter( # total_reservations = models.CarReservation.objects.filter(
reserved_until__gte=timezone.now() # reserved_until__gte=timezone.now()
).count() # ).count()
cars_in_house = models.CarLocation.objects.filter( # cars_in_house = models.CarLocation.objects.filter(
owner=dealer, # owner=dealer,
).count() # ).count()
cars_outside = total_cars - cars_in_house # cars_outside = total_cars - cars_in_house
#
# Fetch financial statistics # # Fetch financial statistics
stats = models.CarFinance.objects.aggregate( # stats = models.CarFinance.objects.aggregate(
total_cost_price=Sum("cost_price"), # total_cost_price=Sum("cost_price"),
total_selling_price=Sum("selling_price"), # total_selling_price=Sum("selling_price"),
) # )
total_cost_price = stats.get("total_cost_price", 0) or 0 # total_cost_price = stats.get("total_cost_price", 0) or 0
total_selling_price = stats.get("total_selling_price", 0) or 0 # total_selling_price = stats.get("total_selling_price", 0) or 0
total_profit = total_selling_price - total_cost_price # total_profit = total_selling_price - total_cost_price
#
# Prepare context data # # Prepare context data
context.update({ # context.update({
"dealer": dealer, # "dealer": dealer,
"total_cars": total_cars, # "total_cars": total_cars,
"cars_in_house": cars_in_house, # "cars_in_house": cars_in_house,
"cars_outside": cars_outside, # "cars_outside": cars_outside,
"total_reservations": total_reservations, # "total_reservations": total_reservations,
"total_cost_price": total_cost_price, # "total_cost_price": total_cost_price,
"total_selling_price": total_selling_price, # "total_selling_price": total_selling_price,
"total_profit": total_profit, # "total_profit": total_profit,
}) # })
#
except Exception as e: # except Exception as e:
# Log the error (you can use Django's logging framework) # # Log the error (you can use Django's logging framework)
print(f"Error fetching data: {e}") # print(f"Error fetching data: {e}")
# Provide default values in case of an error # # Provide default values in case of an error
context.update({ # context.update({
"dealer": dealer, # "dealer": dealer,
"total_cars": 0, # "total_cars": 0,
"cars_in_house": 0, # "cars_in_house": 0,
"cars_outside": 0, # "cars_outside": 0,
"total_reservations": 0, # "total_reservations": 0,
"total_cost_price": 0, # "total_cost_price": 0,
"total_selling_price": 0, # "total_selling_price": 0,
"total_profit": 0, # "total_profit": 0,
}) # })
return context # return context
class TestView(TemplateView): class TestView(TemplateView):
template_name = "inventory/cars_list_api.html" template_name = "inventory/cars_list_api.html"
@ -321,15 +321,14 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
template_name = "dashboards/manager.html" template_name = "dashboards/manager.html"
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
if ( if not request.user.is_authenticated:
not request.user.is_authenticated
):
return redirect("welcome") return redirect("welcome")
return super().dispatch(request, *args, **kwargs) return super().dispatch(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
dealer = get_user_type(self.request) dealer = get_user_type(self.request)
entity = dealer.entity
total_cars = models.Car.objects.filter(dealer=dealer).count() total_cars = models.Car.objects.filter(dealer=dealer).count()
total_reservations = models.CarReservation.objects.filter( total_reservations = models.CarReservation.objects.filter(
reserved_until__gte=timezone.now() reserved_until__gte=timezone.now()
@ -355,6 +354,13 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
sold_percentage = sold_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') qs = models.Car.objects.values('id_car_make__name').annotate(count=Count('id')).order_by('id_car_make__name')
car_by_make = list(qs) 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["dealer"] = dealer
context["total_cars"] = total_cars context["total_cars"] = total_cars
@ -374,6 +380,13 @@ class ManagerDashboard(LoginRequiredMixin, TemplateView):
context['damaged_cars'] = damaged_cars context['damaged_cars'] = damaged_cars
context['transfer_cars'] = transfer_cars context['transfer_cars'] = transfer_cars
context['car'] = json.dumps(car_by_make) 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 return context
@ -425,6 +438,11 @@ class SalesDashboard(LoginRequiredMixin, TemplateView):
context['damaged_cars'] = damaged_cars context['damaged_cars'] = damaged_cars
context['transfer_cars'] = transfer_cars context['transfer_cars'] = transfer_cars
context['car'] = json.dumps(car_by_make) context['car'] = json.dumps(car_by_make)
# context['customers'] = customers
# context['staff'] = staff
# context['total_leads'] = total_leads
# context['invoices'] = invoices
return context return context
@ -1211,13 +1229,20 @@ class DealerDetailView(LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
dealer = self.object dealer = self.object
car_makes = models.CarMake.objects.filter(car_dealers__dealer=dealer)
# Fetch current staff count from the annotated queryset # Fetch current staff count from the annotated queryset
staff_count = dealer.staff_count staff_count = dealer.staff_count
cars_count = models.Car.objects.filter(dealer=dealer).count()
# Get the quota value dynamically # Get the quota value dynamically
quota_dict = get_user_quota(dealer.user) 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["staff_count"] = staff_count
context["cars_count"] = cars_count
context["allowed_users"] = allowed_users 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" context["quota_display"] = f"{staff_count}/{allowed_users}" if allowed_users is not None else "N/A"
return context return context
@ -2320,6 +2345,7 @@ class EstimatePreviewView(LoginRequiredMixin,PermissionRequiredMixin,DetailView)
permission_required = ['django_ledger.view_estimatemodel'] permission_required = ['django_ledger.view_estimatemodel']
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
dealer = get_user_type(self.request)
estimate = kwargs.get("object") estimate = kwargs.get("object")
if estimate.get_itemtxs_data(): if estimate.get_itemtxs_data():
data = get_financial_values(estimate) data = get_financial_values(estimate)
@ -2329,6 +2355,7 @@ class EstimatePreviewView(LoginRequiredMixin,PermissionRequiredMixin,DetailView)
kwargs["discount_amount"] = data["discount_amount"] kwargs["discount_amount"] = data["discount_amount"]
kwargs["vat"] = data["vat"] kwargs["vat"] = data["vat"]
kwargs["additional_services"] = data["additional_services"] kwargs["additional_services"] = data["additional_services"]
kwargs["dealer"] = dealer
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
@ -3557,53 +3584,50 @@ class OrderListView(LoginRequiredMixin,PermissionRequiredMixin, ListView):
@login_required @login_required
@permission_required("django_ledger.view_estimate", raise_exception=True) @permission_required("django_ledger.view_estimate", raise_exception=True)
def send_email_view(request, pk): def send_email_view(request, pk):
dealer = get_user_type(request)
estimate = get_object_or_404(EstimateModel, pk=pk) 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( if not estimate.get_itemtxs_data()[0]:
"manager@tenhal.com", messages.error(request, _("Quotation has no items"))
request.POST.get("to"),
request.POST.get("subject"),
request.POST.get("message"),
)
estimate.mark_as_review()
messages.success(request, _("Email sent successfully!"))
return redirect("estimate_detail", pk=estimate.pk) 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""" msg = f"""
السلام عليكم السلام عليكم
Dear {estimate.customer.customer_name}, 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} {link}
شكراً لاهتمامكم بهذا الأمر.
Thank you for your attention to this matter.
تحياتي, تحياتي,
Best regards, Best regards,
[Your Name] {dealer.get_local_name}
[Your Position] {dealer.phone_number}
[Your Company] هيكل | Haikal
[Your Contact Information]
""" """
return render(
request, send_email(
"sales/estimates/estimate_send.html", settings.DEFAULT_FROM_EMAIL,
{"estimate": estimate, "message": msg}, estimate.customer.email,
_("Quotation"),
msg,
) )
estimate.mark_as_review()
messages.success(request, _("Email sent successfully!"))
return redirect("estimate_detail", pk=estimate.pk)
# errors # errors
def custom_page_not_found_view(request, exception=None): def custom_page_not_found_view(request, exception=None):
@ -3993,4 +4017,20 @@ def schedule_cancel(request,pk):
schedule.save() schedule.save()
response = HttpResponse() response = HttpResponse()
response.status_code = 200 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})

Binary file not shown.

File diff suppressed because it is too large Load Diff

BIN
static/.DS_Store vendored

Binary file not shown.

Binary file not shown.

View 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 { .color-div {
width: 64px; width: 64px;

View File

@ -1259,6 +1259,7 @@ progress {
font-weight: 600; font-weight: 600;
} }
.display-1 { .display-1 {
font-size: calc(1.6018371582rem + 4.2220458984vw); font-size: calc(1.6018371582rem + 4.2220458984vw);
font-weight: 400; font-weight: 400;

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

File diff suppressed because one or more lines are too long

1
static/vendors/zxing/index.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -85,9 +85,9 @@
<!-- Currency Field --> <!-- Currency Field -->
<div class="form-floating mb-3"> <div class="form-floating mb-3">
<select name="currency" id="id_currency" class="form-select form-control-sm" > <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> </select>
<label for="id_currency">{{ _("Currency") }}</label> <label for="id_currency"><span class="currency"> {{ CURRENCY }}</span></label>
{% if form.currency.errors %} {% if form.currency.errors %}
<div class="alert alert-danger mt-2"> <div class="alert alert-danger mt-2">
{{ form.currency.errors }} {{ form.currency.errors }}

View File

@ -39,7 +39,7 @@
<h3 class="fw-bolder mb-2 line-clamp-1">{{ opportunity.customer.customer_name }}</h3> <h3 class="fw-bolder mb-2 line-clamp-1">{{ opportunity.customer.customer_name }}</h3>
<div class="d-flex align-items-center mb-4"> <div class="d-flex align-items-center mb-4">
{% if opportunity.car.finances %} {% 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 %} {% endif %}
</div> </div>
<div class="d-md-flex d-xl-block align-items-center justify-content-between mb-5"> <div class="d-md-flex d-xl-block align-items-center justify-content-between mb-5">

View File

@ -128,7 +128,7 @@
<p class="fw-semibold mb-0" >{{ _("Quotation") }}-<span class="fs-10 fw-medium">{{ estimate.estimate_number }}</span></p> <p class="fw-semibold mb-0" >{{ _("Quotation") }}-<span class="fs-10 fw-medium">{{ estimate.estimate_number }}</span></p>
</td> </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 class="payment_status align-middle white-space-nowrap text-start fw-bold text-body-tertiary">
</td> </td>
@ -150,7 +150,7 @@
<p class="fw-semibold mb-0" >{{ _("Invoice") }}-<span class="fs-10 fw-medium">{{ invoice.invoice_number }}</span></p> <p class="fw-semibold mb-0" >{{ _("Invoice") }}-<span class="fs-10 fw-medium">{{ invoice.invoice_number }}</span></p>
</td> </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"> <td class="payment_status align-middle white-space-nowrap text-start fw-bold text-body-tertiary">
{% if invoice.is_paid %} {% if invoice.is_paid %}
<span class="badge badge-phoenix fs-10 badge-phoenix-success"> <span class="badge badge-phoenix fs-10 badge-phoenix-success">

View File

@ -2,13 +2,96 @@
{% load i18n static custom_filters django_ledger%} {% load i18n static custom_filters django_ledger%}
{% block content %} {% block content %}
<div class="row justify-content-between mb-2">
<div class="row justify-content-between">
<div class="col-12 col-lg-6">
<div class="row g-2">
<h3 class="fs-4 fs-md-4 fs-xl-4 fw-black mb-4"> <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> <span class="text-gradient-info me-3">{{ dealer.get_local_name }}</span>
</h3> </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="card mb-3">
<div class="bg-holder" style="background-image:url({% static 'images/bg/38.png' %});background-position:left bottom;background-size:auto;"></div> <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>
<div class="col-sm-5 col-md-4 col-xxl-4 my-3 my-sm-0"> <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 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 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 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> </div>
</div> </div>
</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>
</div> </div>
@ -252,9 +159,7 @@
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
/* Car Chart By Make */ /* Car Chart By Make */
const getColor = (name, dom = document.documentElement) => { const { getColor, rgbaColor } = window.phoenix.utils;
return getComputedStyle(dom).getPropertyValue(`--phoenix-${name}`).trim();
};
const handleTooltipPosition = ([pos, , dom, , size]) => { const handleTooltipPosition = ([pos, , dom, , size]) => {
// only for mobile device // only for mobile device
if (window.innerWidth <= 540) { if (window.innerWidth <= 540) {
@ -326,9 +231,7 @@ document.addEventListener("DOMContentLoaded", function () {
car_chart.setOption(option); car_chart.setOption(option);
/* Car Status Chart */ /* Car Status Chart */
const chartElContainer = document.querySelector('.echart-cars-by-status-container');
const car_status = echarts.init(document.getElementById('echart-cars-by-status')); const car_status = echarts.init(document.getElementById('echart-cars-by-status'));
const chartLabel = chartElContainer.querySelector('[data-label]');
const data = [ const data = [
{value: {{available_cars}}, name: '{{ _("Available") }}'}, {value: {{available_cars}}, name: '{{ _("Available") }}'},
{value: {{sold_cars}}, name: '{{ _("Sold")}}'}, {value: {{sold_cars}}, name: '{{ _("Sold")}}'},
@ -337,24 +240,21 @@ document.addEventListener("DOMContentLoaded", function () {
{value: {{hold_cars}}, name: '{{ _("Hold") }}'}, {value: {{hold_cars}}, name: '{{ _("Hold") }}'},
{value: {{damaged_cars}}, name: '{{ _("Damaged") }}'} {value: {{damaged_cars}}, name: '{{ _("Damaged") }}'}
]; ];
const totalCars = data.reduce((acc, val) => val.value + acc, 0);
if (chartLabel) {
chartLabel.innerHTML = totalCars;
}
option = { option = {
color: [ color: [
getColor('success'), rgbaColor(getColor('success'),0.7),
getColor('warning'), rgbaColor(getColor('warning'),0.7),
getColor('danger'), rgbaColor(getColor('danger'),0.7),
getColor('primary'), rgbaColor(getColor('primary'),0.7),
getColor('warning-lighter'), rgbaColor(getColor('warning-light'),0.7),
getColor('secondary-dark') rgbaColor(getColor('secondary-light'),0.7),
], ],
tooltip: { tooltip: {
trigger: 'item', trigger: 'item',
padding: [7, 10], padding: [7, 10],
backgroundColor: getColor('body-highlight-bg'), backgroundColor: getColor('body-highlight-bg'),
borderColor: getColor('border-color'), borderColor: getColor('body-bg'),
textStyle: {color: getColor('light-text-emphasis')}, textStyle: {color: getColor('light-text-emphasis')},
borderWidth: 1, borderWidth: 1,
transitionDuration: 0, transitionDuration: 0,

View File

@ -132,7 +132,7 @@
</div> </div>
</div> </div>
<div class="mt-3 mt-md-0"> <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> </div>
</div> </div>
@ -147,7 +147,7 @@
</div> </div>
</div> </div>
<div class="mt-3 mt-md-0"> <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> </div>
</div> </div>

View 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 %}

View File

@ -44,10 +44,14 @@
<h6 class="mb-2 text-body-secondary">{% trans 'last login'|capfirst %}</h6> <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> <h4 class="fs-7 text-body-highlight mb-0">{{ dealer.user.last_login|date:"D M d, Y H:i" }}</h4>
</div> </div>
<div class="text-end"> <div class="text-center me-1">
<h6 class="mb-2 text-body-secondary">{% trans 'Total users'|capfirst %}</h6> <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> <h4 class="fs-7 text-body-highlight mb-0">{{ dealer.staff_count }} / {{ allowed_users }}</h4>
</div> </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> </div>
</div> </div>
@ -104,7 +108,7 @@
</div> </div>
<p class="fs-9 text-body-tertiary">{% trans 'Active until' %}: {{ dealer.user.userplan.expire}}</p> <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"> <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> <h5 class="fs-9 fw-normal text-body-tertiary ms-1">{{ _("Per month")}}</h5>
</div> </div>
</div> </div>
@ -130,13 +134,26 @@
</div> </div>
</div> </div>
</div> </div>
<div class="col-12 col-lg-3">
<div class="card h-100"> <div class="col-12 col-lg-6">
<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 h-100">
<div class="card-body d-flex flex-column justify-content-between position-relative"> <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="d-flex justify-content-between"> <div class="card-body d-flex flex-column justify-content-center position-relative">
<div class="mb-5 mb-md-0 mb-lg-5"> <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>
</div> </div>
</div> </div>

View File

@ -6,42 +6,23 @@
{% block content %} {% block content %}
<div class="row my-5"> <div class="row my-5">
<div class="row justify-content-center"> <div class="col-md-8">
<div class="col-md-8"> <!-- Form Header -->
<!-- Form Header -->
<div class="card shadow-sm">
<div class="card-header bg-primary text-white">
<h4 class="mb-0">{{ _("Update Dealer Information") }}</h4>
</div>
<!-- Form Body --> <h4 class="mb-3">{{ _("Update Dealer Information") }}</h4>
<div class="card-body">
<form method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
{% csrf_token %}
{{ form|crispy }}
<!-- Save Button --> <form method="post" enctype="multipart/form-data" class="needs-validation" novalidate>
<div class="d-grid gap-2 mt-3"> {% csrf_token %}
<button type="submit" class="btn btn-primary btn-lg"> {{ form|crispy }}
<i class="bi bi-save"></i> {{ _("Save Changes") }} <div class="gap-2 mt-3">
</button> <button type="submit" class="btn btn-phoenix-primary btn-sm">
</div> <i class="fa fa-save"></i> {{ _("Save") }}
</form> </button>
</div> <a href="{% url 'dealer_detail' dealer.pk %}" class="btn btn-sm btn-phoenix-secondary">
</div> <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">
&nbsp;{{ _("Back") }}&nbsp;
{% 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>
</div> </div>
{% endblock %} {% endblock %}

Some files were not shown because too many files have changed in this diff Show More