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.Schedule)
admin.site.register(models.Notes)
admin.site.register(models.UserActivityLog)
# admin.site.register(appointment_models.Client)

View File

@ -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)

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

View File

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

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()
}
@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')

View File

@ -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"),

View File

@ -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)

View File

@ -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})

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 {
width: 64px;

View File

@ -1259,6 +1259,7 @@ progress {
font-weight: 600;
}
.display-1 {
font-size: calc(1.6018371582rem + 4.2220458984vw);
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 -->
<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 }}

View File

@ -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">

View File

@ -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">

View File

@ -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,

View File

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

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

View File

@ -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">
&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>
{% endblock %}

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