Compare commits
6 Commits
e6e2ed0150
...
009fcce730
| Author | SHA1 | Date | |
|---|---|---|---|
| 009fcce730 | |||
| 095885c9b7 | |||
| 08dbd10465 | |||
| 7e9b5af912 | |||
| 0ea27d0826 | |||
| 4cc61a6fa3 |
@ -432,25 +432,20 @@ class CarFinanceForm(forms.ModelForm):
|
||||
additional services associated with a car finance application.
|
||||
"""
|
||||
|
||||
# additional_finances = forms.ModelMultipleChoiceField(
|
||||
# queryset=AdditionalServices.objects.all(),
|
||||
# widget=forms.CheckboxSelectMultiple(attrs={"class": "form-check-input"}),
|
||||
# required=False,
|
||||
# )
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
cost_price = cleaned_data.get("cost_price")
|
||||
marked_price = cleaned_data.get("marked_price")
|
||||
|
||||
if cost_price > marked_price:
|
||||
raise forms.ValidationError({"cost_price": "Cost price should not be greater than marked price"})
|
||||
|
||||
return cleaned_data
|
||||
|
||||
class Meta:
|
||||
model = CarFinance
|
||||
fields = ["cost_price","marked_price"]
|
||||
|
||||
# def save(self, commit=True):
|
||||
# instance = super().save()
|
||||
# try:
|
||||
# instance.additional_services.set(self.cleaned_data["additional_finances"])
|
||||
# except KeyError:
|
||||
# pass
|
||||
# instance.save()
|
||||
# return instance
|
||||
|
||||
|
||||
class CarLocationForm(forms.ModelForm):
|
||||
"""
|
||||
@ -1172,6 +1167,7 @@ class ScheduleForm(forms.ModelForm):
|
||||
scheduled_at = forms.DateTimeField(
|
||||
widget=DateTimeInput(attrs={"type": "datetime-local"})
|
||||
)
|
||||
reminder = forms.BooleanField(help_text=_("Send a reminder?"),required=False)
|
||||
|
||||
class Meta:
|
||||
model = Schedule
|
||||
|
||||
@ -742,7 +742,9 @@ class Car(Base):
|
||||
)
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
@property
|
||||
def invoice(self):
|
||||
return self.item_model.invoicemodel_set.first if self.item_model.invoicemodel_set.first() else None
|
||||
def get_transfer(self):
|
||||
return self.transfer_logs.filter(active=True).first()
|
||||
|
||||
|
||||
@ -1282,14 +1282,18 @@ urlpatterns = [
|
||||
),
|
||||
path('car-sale-report/<slug:dealer_slug>/csv/', views.car_sale_report_csv_export, name='car-sale-report-csv-export'),
|
||||
|
||||
path('feature/recalls/', views.RecallListView.as_view(), name='recall_list'),
|
||||
path('feature/recall/', views.RecallFilterView, name='recall_filter'),
|
||||
path('feature/recall/', views.RecallListView.as_view(), name='recall_list'),
|
||||
path('feature/recall/filter/', views.RecallFilterView, name='recall_filter'),
|
||||
path('feature/recall/<int:pk>/view/', views.RecallDetailView.as_view(), name='recall_detail'),
|
||||
path('feature/recall/create/', views.RecallCreateView.as_view(), name='recall_create'),
|
||||
path('feature/recall/success/', views.RecallSuccessView.as_view(), name='recall_success'),
|
||||
|
||||
path('<slug:dealer_slug>/schedules/calendar/', views.schedule_calendar, name='schedule_calendar'),
|
||||
|
||||
# staff profile
|
||||
path('<slug:dealer_slug>/staff/<slug:slug>detail/', views.StaffDetailView.as_view(), name='staff_detail'),
|
||||
|
||||
|
||||
]
|
||||
|
||||
handler404 = "inventory.views.custom_page_not_found_view"
|
||||
|
||||
@ -2197,6 +2197,35 @@ class DealerUpdateView(
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse("dealer_detail", kwargs={"slug": self.object.slug})
|
||||
|
||||
class StaffDetailView(LoginRequiredMixin, DetailView):
|
||||
"""
|
||||
Represents a detailed view for a Dealer model.
|
||||
|
||||
This class extends Django's `DetailView` to provide a detailed view of a dealer.
|
||||
It includes additional context data such as the count of staff members, cars
|
||||
associated with the dealer, available car makes, and dynamically fetched quotas.
|
||||
The class also ensures that users must be logged in to access the detailed view.
|
||||
|
||||
:ivar model: The model associated with this view (Dealer model).
|
||||
:type model: django.db.models.Model
|
||||
:ivar template_name: Path to the template used to render the view.
|
||||
:type template_name: str
|
||||
:ivar context_object_name: The name used to refer to the object in the template context.
|
||||
:type context_object_name: str
|
||||
"""
|
||||
|
||||
model = models.Staff
|
||||
template_name = "staff/staff_detail.html"
|
||||
context_object_name = "staff"
|
||||
|
||||
|
||||
|
||||
def dealer_vat_rate_update(request, slug):
|
||||
dealer = get_object_or_404(models.Dealer, slug=slug)
|
||||
models.VatRate.objects.filter(dealer=dealer).update(rate=request.POST.get("rate"))
|
||||
messages.success(request, _("VAT rate updated successfully"))
|
||||
return redirect("dealer_detail", slug=slug)
|
||||
|
||||
|
||||
class CustomerListView(LoginRequiredMixin, PermissionRequiredMixin, ListView):
|
||||
@ -4912,10 +4941,19 @@ def update_estimate_discount(request, dealer_slug, pk):
|
||||
object_id=estimate.pk,
|
||||
)
|
||||
discount_amount = request.POST.get("discount_amount", 0)
|
||||
calculator = CarFinanceCalculator(estimate)
|
||||
finance_data = calculator.get_finance_data()
|
||||
if Decimal(discount_amount) >= finance_data.get('cars')[0]['marked_price']:
|
||||
messages.error(request, _("Discount amount cannot be greater than marked price"))
|
||||
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
|
||||
|
||||
print(finance_data.get('cars')[0]['marked_price'] * Decimal('0.5'))
|
||||
if Decimal(discount_amount) > finance_data.get('cars')[0]['marked_price'] * Decimal('0.5'):
|
||||
messages.warning(request, _("Discount amount is greater than 50% of the marked price, proceed with caution."))
|
||||
else:
|
||||
messages.success(request, _("Discount updated successfully"))
|
||||
extra_info.data.update({"discount": Decimal(discount_amount)})
|
||||
extra_info.save()
|
||||
messages.success(request, "Discount updated successfully")
|
||||
return redirect("estimate_detail", dealer_slug=dealer_slug, pk=pk)
|
||||
|
||||
|
||||
@ -6535,6 +6573,7 @@ def schedule_event(request, dealer_slug, content_type, slug):
|
||||
|
||||
form = forms.ScheduleForm(request.POST)
|
||||
if form.is_valid():
|
||||
reminder = form.cleaned_data['reminder']
|
||||
instance = form.save(commit=False)
|
||||
instance.dealer = dealer
|
||||
instance.content_object = obj
|
||||
@ -6581,20 +6620,21 @@ def schedule_event(request, dealer_slug, content_type, slug):
|
||||
created_by=request.user,
|
||||
activity_type=instance.scheduled_type,
|
||||
)
|
||||
scheduled_at_aware = timezone.make_aware(instance.scheduled_at, timezone.get_current_timezone()) if timezone.is_naive(instance.scheduled_at) else instance.scheduled_at
|
||||
if reminder:
|
||||
scheduled_at_aware = timezone.make_aware(instance.scheduled_at, timezone.get_current_timezone()) if timezone.is_naive(instance.scheduled_at) else instance.scheduled_at
|
||||
|
||||
reminder_time = scheduled_at_aware - timezone.timedelta(minutes=15)
|
||||
# Only schedule if the reminder time is in the future
|
||||
# Reminder emails are scheduled to be sent 15 minutes before the scheduled time
|
||||
if reminder_time > timezone.now():
|
||||
DjangoQSchedule.objects.create(
|
||||
name=f"send_schedule_reminder_email_to_{instance.scheduled_by.email}_for_{content_type}_with_PK_{instance.pk}",
|
||||
func='inventory.tasks.send_schedule_reminder_email',
|
||||
args=f'"{instance.pk}"',
|
||||
schedule_type=DjangoQSchedule.ONCE,
|
||||
next_run=reminder_time,
|
||||
hook='inventory.tasks.log_email_status',
|
||||
)
|
||||
reminder_time = scheduled_at_aware - timezone.timedelta(minutes=15)
|
||||
# Only schedule if the reminder time is in the future
|
||||
# Reminder emails are scheduled to be sent 15 minutes before the scheduled time
|
||||
if reminder_time > timezone.now():
|
||||
DjangoQSchedule.objects.create(
|
||||
name=f"send_schedule_reminder_email_to_{instance.scheduled_by.email}_for_{content_type}_with_PK_{instance.pk}",
|
||||
func='inventory.tasks.send_schedule_reminder_email',
|
||||
args=f'"{instance.pk}"',
|
||||
schedule_type=DjangoQSchedule.ONCE,
|
||||
next_run=reminder_time,
|
||||
hook='inventory.tasks.log_email_status',
|
||||
)
|
||||
messages.success(request, _("Appointment Created Successfully"))
|
||||
|
||||
return redirect(f'{content_type}_detail',dealer_slug=dealer_slug, slug=slug)
|
||||
@ -9456,10 +9496,12 @@ def submit_plan(request, dealer_slug):
|
||||
tax=15,
|
||||
status=1,
|
||||
)
|
||||
logger.info(f"order created {order}")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
logger.error(e)
|
||||
if not order:
|
||||
messages.error(request, _("Error creating order"))
|
||||
logger.error("unable to create order")
|
||||
return redirect("pricing_page", dealer_slug=dealer_slug)
|
||||
transaction_url = handle_payment(request, order)
|
||||
return redirect(transaction_url)
|
||||
@ -9473,9 +9515,13 @@ def payment_callback(request, dealer_slug):
|
||||
payment_id = request.GET.get("id")
|
||||
history = models.PaymentHistory.objects.filter(transaction_id=payment_id).first()
|
||||
payment_status = request.GET.get("status")
|
||||
logger.info(f"Received payment callback for dealer_slug: {dealer_slug}, payment_id: {payment_id}, status: {payment_status}")
|
||||
order = Order.objects.filter(user=dealer.user, status=1).first() # Status 1 = NEW
|
||||
|
||||
print(order)
|
||||
if payment_status == "paid":
|
||||
logger.info(f"Payment successful for transaction ID {payment_id}. Processing order completion.")
|
||||
|
||||
billing_info, created = BillingInfo.objects.get_or_create(
|
||||
user=dealer.user,
|
||||
defaults={
|
||||
@ -9487,12 +9533,20 @@ def payment_callback(request, dealer_slug):
|
||||
'country': dealer.entity.country or " ",
|
||||
}
|
||||
)
|
||||
if created:
|
||||
logger.info(f"Created new billing info for user {dealer.user}.")
|
||||
else:
|
||||
logger.debug(f"Billing info already exists for user {dealer.user}.")
|
||||
|
||||
if not hasattr(order.user, 'userplan'):
|
||||
UserPlan.objects.create(
|
||||
user=order.user,
|
||||
plan=order.plan,
|
||||
expire=datetime.now().date() + timedelta(days=order.get_plan_pricing().pricing.period)
|
||||
)
|
||||
logger.info(f"Created new UserPlan for user {order.user} with plan {order.plan}.")
|
||||
else:
|
||||
logger.info(f"UserPlan already exists for user {order.user}.")
|
||||
|
||||
try:
|
||||
|
||||
@ -9510,7 +9564,7 @@ def payment_callback(request, dealer_slug):
|
||||
order.complete_order()
|
||||
history.status = "paid"
|
||||
history.save()
|
||||
|
||||
logger.info(f"Order {order.id} for user {order.user} completed successfully. Payment history updated.")
|
||||
invoice = order.get_invoices().first()
|
||||
return render(
|
||||
request,
|
||||
@ -9519,12 +9573,14 @@ def payment_callback(request, dealer_slug):
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.exception(f"Error completing order {order.id} for user {order.user}: {e}")
|
||||
logger.error(f"Plan activation failed: {str(e)}")
|
||||
history.status = "failed"
|
||||
history.save()
|
||||
return render(request, "payment_failed.html", {"message": "Plan activation error"})
|
||||
|
||||
elif payment_status == "failed":
|
||||
logger.warning(f"Payment failed for transaction ID {payment_id}. Message: {message}")
|
||||
history.status = "failed"
|
||||
history.save()
|
||||
return render(request, "payment_failed.html", {"message": message})
|
||||
@ -9769,10 +9825,10 @@ def update_task(request, dealer_slug, pk):
|
||||
@permission_required("inventory.change_schedule", raise_exception=True)
|
||||
def update_schedule(request, dealer_slug, pk):
|
||||
task = get_object_or_404(models.Schedule, pk=pk)
|
||||
|
||||
if request.method == "POST":
|
||||
task.completed = False if task.completed else True
|
||||
task.save()
|
||||
print("task")
|
||||
|
||||
return render(request, "partials/task.html", {"task": task})
|
||||
|
||||
|
||||
@ -138,7 +138,8 @@ html[dir="rtl"] .form-icon-container .form-control {
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 10;
|
||||
z-index: 9999;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#spinner-bg {
|
||||
@ -150,11 +151,14 @@ html[dir="rtl"] .form-icon-container .form-control {
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
opacity: 0;
|
||||
transition: opacity 500ms ease-in;
|
||||
z-index: 5;
|
||||
visibility: hidden;
|
||||
z-index: 10000;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#spinner-bg.htmx-request {
|
||||
opacity: .8;
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -84,10 +84,10 @@
|
||||
{% include "plans/expiration_messages.html" %}
|
||||
{% block period_navigation %}
|
||||
{% endblock period_navigation %}
|
||||
<div id="main_content" class="fade-me-in" hx-boost="false" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML transition:true" hx-select-oob="#toast-container" hx-history-elt>
|
||||
<div id="spinner" class="htmx-indicator spinner-bg">
|
||||
<div id="spinner" class="htmx-indicator spinner-bg">
|
||||
<img src="{% static 'spinner.svg' %}" width="100" height="100" alt="">
|
||||
</div>
|
||||
<div id="main_content" class="fade-me-in" hx-boost="false" hx-target="#main_content" hx-select="#main_content" hx-swap="outerHTML transition:true" hx-select-oob="#toast-container" hx-history-elt>
|
||||
{% block customCSS %}{% endblock %}
|
||||
{% block content %}{% endblock content %}
|
||||
{% block customJS %}{% endblock %}
|
||||
|
||||
@ -47,13 +47,13 @@
|
||||
{% endif %}
|
||||
|
||||
{% if perms.inventory.add_car %}
|
||||
<li class="nav-item">
|
||||
{% comment %} <li class="nav-item">
|
||||
<a class="nav-link" href="{% url 'upload_cars' request.dealer.slug %}">
|
||||
<div class="d-flex align-items-center">
|
||||
<span class="nav-link-icon"><span class="fas fa-file-import"></span></span><span class="nav-link-text">{% trans "Bulk Upload"|capfirst %}</span>
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</li> {% endcomment %}
|
||||
{% endif %}
|
||||
{% if perms.django_ledger.view_purchaseordermodel %}
|
||||
<li class="nav-item">
|
||||
@ -504,7 +504,7 @@
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="nav-item">
|
||||
<a hx-boost="false" class="nav-link px-3 d-block" href="{% url 'appointment:user_profile' request.user.id %}"> <span class="me-2 text-body align-bottom" data-feather="user"></span><span>{% translate 'profile'|capfirst %}</span></a>
|
||||
<a hx-boost="false" class="nav-link px-3 d-block" href="{% url 'staff_detail' request.dealer.slug request.staff.slug %}"> <span class="me-2 text-body align-bottom" data-feather="user"></span><span>{% translate 'profile'|capfirst %}</span></a>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% if request.is_dealer %}
|
||||
@ -530,7 +530,7 @@
|
||||
</li>
|
||||
{% if request.is_staff %}
|
||||
<li class="nav-item">
|
||||
<a hx-boost="false" class="nav-link px-3 d-block" href="{% url 'appointment:get_user_appointments' %}"> <span class="me-2 text-body align-bottom" data-feather="calendar"></span>{{ _("My Calendar") }}</a>
|
||||
<a hx-boost="false" class="nav-link px-3 d-block" href="{% url 'schedule_calendar' request.dealer.slug%}"> <span class="me-2 text-body align-bottom" data-feather="calendar"></span>{{ _("My Calendar") }}</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
<!--<li class="nav-item"><a class="nav-link px-3 d-block" href=""> Language</a></li>-->
|
||||
|
||||
@ -127,7 +127,11 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<section id="sale-details" class="mb-3">
|
||||
@ -137,47 +141,48 @@
|
||||
<i class="bi bi-download me-2"></i>{% trans 'Download as CSV' %}
|
||||
</a>
|
||||
</div>
|
||||
<div class="table-responsive ">
|
||||
<table class="table table-sm table-striped table-hover ">
|
||||
<thead class="bg-body-highlight">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover table-bordered table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{% trans 'VIN' %}</th>
|
||||
<th scope="col">{% trans 'Make' %}</th>
|
||||
<th scope="col">{% trans 'Model' %}</th>
|
||||
<th scope="col">{% trans 'Year' %}</th>
|
||||
<th scope="col">{% trans 'Serie' %}</th>
|
||||
<th scope="col">{% trans 'Trim' %}</th>
|
||||
<th scope="col">{% trans 'Mileage' %}</th>
|
||||
<th scope="col">{% trans 'Stock Type' %}</th>
|
||||
<th scope="col">{% trans 'Created Date' %}</th>
|
||||
<th scope="col">{% trans 'Sold Date' %}</th>
|
||||
<th scope="col">{% trans 'Cost Price' %}</th>
|
||||
<th scope="col">{% trans 'Marked Price' %}</th>
|
||||
<th scope="col">{% trans 'Discount Amount' %}</th>
|
||||
<th scope="col">{% trans 'Selling Price' %}</th>
|
||||
<th scope="col">{% trans 'Tax Amount' %}</th>
|
||||
<th scope="col">{% trans 'Invoice Number' %}</th>
|
||||
<th class="fs-9">{% trans 'VIN' %}</th>
|
||||
<th class="fs-9">{% trans 'Make' %}</th>
|
||||
<th class="fs-9">{% trans 'Model' %}</th>
|
||||
<th class="fs-9">{% trans 'Year' %}</th>
|
||||
<th class="fs-9">{% trans 'Serie' %}</th>
|
||||
<th class="fs-9">{% trans 'Trim' %}</th>
|
||||
<th class="fs-9">{% trans 'Mileage' %}</th>
|
||||
<th class="fs-9">{% trans 'Stock Type' %}</th>
|
||||
<th class="fs-9">{% trans 'Created Date' %}</th>
|
||||
<th class="fs-9">{% trans 'Sold Date' %}</th>
|
||||
<th class="fs-9">{% trans 'Cost Price' %}</th>
|
||||
<th class="fs-9">{% trans 'Marked Price' %}</th>
|
||||
<th class="fs-9">{% trans 'Discount Amount' %}</th>
|
||||
<th class="fs-9">{% trans 'Selling Price' %}</th>
|
||||
<th class="fs-9">{% trans 'Tax Amount' %}</th>
|
||||
<th class="fs-9">{% trans 'Invoice Number' %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for car in cars_sold %}
|
||||
|
||||
<tr>
|
||||
<td>{{ car.vin }}</td>
|
||||
<td>{{ car.id_car_make.name }}</td>
|
||||
<td>{{ car.id_car_model.name }}</td>
|
||||
<td>{{ car.year }}</td>
|
||||
<td>{{ car.id_car_serie.name }}</td>
|
||||
<td>{{ car.id_car_trim.name }}</td>
|
||||
<td>{{ car.mileage }}</td>
|
||||
<td>{{ car.stock_type }}</td>
|
||||
<td>{{ car.created_at }}</td>
|
||||
<td>{{ car.sold_date }}</td>
|
||||
<td>{{ car.finances.cost_price }}</td>
|
||||
<td>{{ car.finances.marked_price }}</td>
|
||||
<td>{{ car.finances.discount_amount }}</td>
|
||||
<td>{{ car.finances.selling_price }}</td>
|
||||
<td>{{ car.finances.vat_amount }}</td>
|
||||
<td>{{ car.item_model.invoicemodel_set.first.invoice_number }}</td>
|
||||
<td class="ps-1 fs-9">{{ car.vin }}</td>
|
||||
<td class="fs-9">{{ car.id_car_make.name }}</td>
|
||||
<td class="fs-9">{{ car.id_car_model.name }}</td>
|
||||
<td class="fs-9">{{ car.year }}</td>
|
||||
<td class="fs-9">{{ car.id_car_serie.name }}</td>
|
||||
<td class="fs-9">{{ car.id_car_trim.name }}</td>
|
||||
<td class="fs-9">{{ car.mileage }}</td>
|
||||
<td class="fs-9">{{ car.stock_type }}</td>
|
||||
<td class="fs-9">{{ car.created_at|date }}</td>
|
||||
<td class="fs-9">{{ car.invoice.date_paid|date|default_if_none:"-" }}</td>
|
||||
<td class="fs-9">{{ car.finances.cost_price }}</td>
|
||||
<td class="fs-9">{{ car.finances.marked_price }}</td>
|
||||
<td class="fs-9">{{ car.finances.discount_amount }}</td>
|
||||
<td class="fs-9">{{ car.finances.selling_price }}</td>
|
||||
<td class="fs-9">{{ car.finances.vat_amount }}</td>
|
||||
<td class="fs-9">{{ car.invoice.invoice_number }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@ -1,25 +1,13 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
|
||||
مرحباً {% firstof user.get_full_name user.username %}،
|
||||
{% trans "Hi" %} {% firstof user.get_full_name user.username %},
|
||||
|
||||
{% if userplan.expire != None %}
|
||||
خطتك الحالية هي {{ plan.name }} وستنتهي صلاحيتها في {{ userplan.expire }}.
|
||||
{% blocktrans with plan_name=plan.name expire=userplan.expire %}Your current plan is {{ plan_name }} and it will expire on {{ expire }}. {% endblocktrans %}
|
||||
{% else %}
|
||||
خطتك الحالية هي {{ plan.name }}.
|
||||
{% blocktrans with plan_name=plan.name %}Your current plan is {{ plan_name }}. {% endblocktrans %}
|
||||
{% endif %}
|
||||
|
||||
شكراً لك،
|
||||
فريق تنحل
|
||||
---------------------
|
||||
|
||||
Hi {% firstof user.get_full_name user.username %},
|
||||
|
||||
{% if userplan.expire != None %}
|
||||
Your current plan is {{ plan_name }} and it will expire on {{ expire }}. {% endblocktrans %}
|
||||
{% else %}
|
||||
Your current plan is {{ plan_name }}. {% endblocktrans %}
|
||||
{% endif %}
|
||||
|
||||
Thank you
|
||||
The Team at Tenhal
|
||||
{% endautoescape %}
|
||||
{% trans "Thank you" %}
|
||||
--
|
||||
{% blocktrans %}The Team at {{ site_name }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
||||
@ -1,3 +1 @@
|
||||
{% load i18n %}
|
||||
Your account {{ user }} has new plan {{ plan }}
|
||||
حسابك {{ user }} لديه خطة جديدة {{ plan }}
|
||||
{% load i18n %}{% blocktrans with user=user plan=plan.name %}Your account {{ user }} has new plan {{ plan }}{% endblocktrans %}
|
||||
@ -1,27 +1,14 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
مرحباً {% firstof user.get_full_name user.username %}،
|
||||
{% trans "Hi" %} {% firstof user.get_full_name user.username %},
|
||||
|
||||
لقد انتهت صلاحية حسابك للتو.
|
||||
{% blocktrans %}Your account has just expired.{% endblocktrans %}
|
||||
|
||||
يمكنك استعادة خطتك الحالية {{ userplan.plan.name }} من هنا:
|
||||
{% blocktrans with plan_name=userplan.plan.name %}You can restore your current plan {{ plan_name }} here:{% endblocktrans %}
|
||||
http://{{ site_domain }}{% url 'current_plan' %}
|
||||
أو يمكنك ترقية خطتك من هنا:
|
||||
{% blocktrans %}or you can upgrade your plan here:{% endblocktrans %}
|
||||
http://{{ site_domain }}{% url 'upgrade_plan' %}
|
||||
|
||||
شكراً لك،
|
||||
{% trans "Thank you" %}
|
||||
--
|
||||
فريق تنحل
|
||||
---------------------
|
||||
Hi {% firstof user.get_full_name user.username %},
|
||||
|
||||
Your account has just expired.
|
||||
|
||||
You can restore your current plan {{ userplan.plan.name }} here:
|
||||
http://{{ site_domain }}{% url 'current_plan' %}
|
||||
or you can upgrade your plan here:
|
||||
http://{{ site_domain }}{% url 'upgrade_plan' %}
|
||||
|
||||
Thank you,
|
||||
--
|
||||
The Team at TENHAL
|
||||
{% endautoescape %}
|
||||
{% blocktrans %}The Team at {{ site_name }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
||||
@ -1,5 +1 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
لقد انتهت صلاحية حسابك {{ user }} للتو.
|
||||
---------------------
|
||||
Your account {{ user }} has just expired.
|
||||
{% endautoescape %}
|
||||
{% load i18n %}{% blocktrans %}Your account {{ user }} has just expired{% endblocktrans %}
|
||||
@ -1,20 +1,11 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
مرحباً {% firstof user.get_full_name user.username %}،
|
||||
{% trans "Hi" %} {% firstof user.get_full_name user.username %},
|
||||
|
||||
تم تمديد صلاحية حسابك للتو لمدة {{ pricing.period }} يوم. خطتك الحالية هي {{plan.name}} وستنتهي في {{userplan.expire}}.
|
||||
{% blocktrans with days=pricing.period plan_name=plan.name expire=userplan.expire %}Your account has just been extended by {{ days }} days. Your current plan is {{ plan_name }} and it will expire on {{ expire }}. {% endblocktrans %}
|
||||
|
||||
سيتم إرسال فاتورة في رسالة بريد إلكتروني أخرى، في حال تم تقديم بيانات الفوترة.
|
||||
شكراً لك
|
||||
{% trans "An invoice will be sent with another e-mail, if billing data was provided." %}
|
||||
|
||||
{% trans "Thank you" %}
|
||||
--
|
||||
فريق تنحل
|
||||
---------------------
|
||||
Hi {% firstof user.get_full_name user.username %},
|
||||
|
||||
Your account has just been extended by {{ pricing.period }} days. Your current plan is {{plan.name}} and it will expire on {{userplan.expire}}.
|
||||
|
||||
An invoice will be sent with another e-mail, if billing data was provided.
|
||||
|
||||
Thank you
|
||||
--
|
||||
The Team at Tenhal
|
||||
{% endautoescape %}
|
||||
{% blocktrans %}The Team at {{ site_name }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
||||
@ -1,14 +1 @@
|
||||
{% load i18n %}
|
||||
{% autoescape off %}
|
||||
مرحباً {% firstof user.get_full_name user.username %}،
|
||||
تم تمديد حسابك {{ user }} لمدة {{ pricing.period }} يوم
|
||||
شكراً لك
|
||||
--
|
||||
فريق تنحل
|
||||
--------
|
||||
Hi {% firstof user.get_full_name user.username %},
|
||||
Your account {{ user }} has been extended by {{ pricing.period }} days
|
||||
Thank you
|
||||
--
|
||||
The Team at Tenhal
|
||||
{% endautoescape %}
|
||||
{% load i18n %}{% blocktrans with user=user days=pricing.period %}Your account {{ user }} has been extended by {{ days }} days{% endblocktrans %}
|
||||
@ -1,21 +1,14 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
مرحباً {% firstof user.get_full_name user.username %}،
|
||||
نكتب إليك لإعلامك، أنه قد تم إصدار {{ invoice_type }} رقم {{ invoice_number }}. يمكنك الاطلاع عليها وطباعتها عبر الرابط:
|
||||
{% trans "Hi" %} {% firstof user.get_full_name user.username %},
|
||||
|
||||
{% blocktrans %}We are writing to inform you, that {{ invoice_type }} {{ invoice_number }} has been issued. You can view it and print it at:
|
||||
http://{{ site_domain }}{{ url }}
|
||||
يمكنك الاطلاع على تفاصيل الطلب عبر الرابط:
|
||||
{% endblocktrans %}
|
||||
|
||||
{% trans "Details of the order can be see on:" %}:
|
||||
http://{{ site_domain }}{% url 'order' pk=order %}
|
||||
|
||||
شكراً لك
|
||||
{% trans "Thank you" %}
|
||||
--
|
||||
فريق تنحل
|
||||
---------------------
|
||||
Hi {% firstof user.get_full_name user.username %},
|
||||
We are writing to inform you, that {{ invoice_type }} {{ invoice_number }} has been issued. You can view it and print it at:
|
||||
http://{{ site_domain }}{{ url }}
|
||||
Details of the order can be see on:
|
||||
http://{{ site_domain }}{% url 'order' pk=order %}
|
||||
|
||||
Thank you
|
||||
--
|
||||
The Team at Tenhal
|
||||
{% endautoescape %}
|
||||
{% blocktrans %}The Team at {{ site_name }}{% endblocktrans %}
|
||||
{% endautoescape %}
|
||||
|
||||
@ -1,3 +1 @@
|
||||
{% load i18n %}
|
||||
Order {{ order }} - {{ invoice_type }} {{ invoice_number }} has been issued for {{ user }}
|
||||
تم إصدار {{ invoice_type }} {{ invoice_number }} للأمر {{ order }} باسم {{ user }}
|
||||
{% load i18n %}{% trans 'Order' %} {{ order }} - {% blocktrans with invoice_type=invoice_type invoice_number=invoice_number user=user %}{{ invoice_type }} {{ invoice_number }} has been issued for {{ user }}{% endblocktrans %}
|
||||
@ -1,29 +1,10 @@
|
||||
{% load i18n %}{% autoescape off %}
|
||||
Hi {% firstof user.get_full_name user.username %},
|
||||
{% trans "Hi" %} {% firstof user.get_full_name user.username %},
|
||||
|
||||
Your account will expire in {{ days }} days.
|
||||
{% blocktrans %}Your account will expire in {{ days }} days.{% endblocktrans %}
|
||||
|
||||
You can extend your current plan {{ userplan.plan.name }} on page:
|
||||
{% blocktrans with plan_name=userplan.plan.name %}You can extend your current plan {{ plan_name }} on page:{% endblocktrans %}
|
||||
http://{{ site_domain }}{% url 'current_plan' %}
|
||||
|
||||
or you can upgrade your plan here:
|
||||
http://{{ site_domain }}{% url 'upgrade_plan' %}
|
||||
|
||||
Thank you
|
||||
--
|
||||
The Team at Tenhal
|
||||
----------------
|
||||
مرحباً {% firstof full_name username %}،
|
||||
|
||||
سينتهي حسابك في غضون {{ days }} يوم.
|
||||
|
||||
يمكنك تمديد خطتك الحالية {{ userplan.plan.name }} على الصفحة التالية:
|
||||
http://{{ site_domain }}{% url 'current_plan' %}
|
||||
|
||||
أو يمكنك ترقية خطتك هنا:
|
||||
http://{{ site_domain }}{% url 'upgrade_plan' %}
|
||||
|
||||
شكراً لك
|
||||
--
|
||||
فريق تنحل
|
||||
{% endautoescape %}
|
||||
{% blocktrans %}or you can upgrade your plan here:{% endblocktrans %}
|
||||
http://{{ site_domain }}{% url 'upgrade_plan' %}
|
||||
@ -1,3 +1 @@
|
||||
{% load i18n %}
|
||||
Your account {{ user }} will expire in {{ days }} day.
|
||||
سينتهي حسابك {{ user }} في غضون {{ days }} يوم.
|
||||
{% load i18n %}{% blocktrans count days as days %}Your account {{ user }} will expire in {{ days }} day{% plural %}Your account {{ user }} will expire in {{ days }} days{% endblocktrans %}
|
||||
@ -7,9 +7,10 @@
|
||||
<input class="form-check-input"
|
||||
{% if task.completed %}checked{% endif %}
|
||||
type="checkbox"
|
||||
hx-post="{% url 'update_schedule' request.dealer.slug task.pk %}"
|
||||
hx-post="{% url 'update_schedule' request.dealer.slug task.pk %}"
|
||||
hx-trigger="change"
|
||||
hx-swap="outerHTML"
|
||||
hx-swap="none"
|
||||
hx-on:click="$(this).closest('tr').toggleClass('completed-task')"
|
||||
hx-target="#task-{{ task.pk }}" />
|
||||
</div>
|
||||
</td>
|
||||
@ -18,7 +19,7 @@
|
||||
<div class="fs-10 d-block">{{ task.scheduled_type|capfirst }}</div>
|
||||
</td>
|
||||
<td class="sent align-middle white-space-nowrap text-start fw-thin text-body-tertiary py-2">{{ task.notes }}</td>
|
||||
<td class="date align-middle white-space-nowrap text-body py-2">{{ task.created_at|naturalday|capfirst }}</td>
|
||||
<td class="date align-middle white-space-nowrap text-body py-2">{{ task.scheduled_at|naturaltime|capfirst }}</td>
|
||||
<td class="date align-middle white-space-nowrap text-body py-2">
|
||||
{% if task.completed %}
|
||||
<span class="badge badge-phoenix fs-10 badge-phoenix-success"><i class="fa-solid fa-check"></i></span>
|
||||
|
||||
@ -4,8 +4,10 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container mt-4">
|
||||
<h2>{% trans "Recall History" %}</h2>
|
||||
|
||||
<div class="d-flex justify-content-between mb-3">
|
||||
<h2>{% trans "Recall History" %}</h2>
|
||||
<a href="{% url 'recall_filter' %}" class="btn btn-primary">{% trans "Create Recall" %}</a>
|
||||
</div>
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
@ -13,7 +15,7 @@
|
||||
<thead class="thead-dark">
|
||||
<tr>
|
||||
<th>{% trans "Title" %}</th>
|
||||
<th>{% trans "Sent" %}</th>
|
||||
<th>{% trans "Sent At" %}</th>
|
||||
<th>{% trans "Make" %}</th>
|
||||
<th>{% trans "Model" %}</th>
|
||||
<th>{% trans "Series" %}</th>
|
||||
@ -28,7 +30,7 @@
|
||||
<td>{{ recall.title }}</td>
|
||||
<td>
|
||||
<span title="{{ recall.created_at }}">
|
||||
{{ recall.created_at|naturaltime }}
|
||||
{{ recall.created_at|date }}
|
||||
</span>
|
||||
</td>
|
||||
<td><img src="{{ recall.make.logo.url }}" width="50" height="50"> {{ recall.make|default:"-" }}</td>
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
<h4 class="alert-heading">{% trans "Recall Initiated Successfully!" %}</h4>
|
||||
<p>{% trans "The recall has been created and notifications have been sent to all affected dealers." %}</p>
|
||||
<hr>
|
||||
<a href="{% url 'recall_filter' %}" class="btn btn-primary">
|
||||
<a href="{% url 'recall_list' %}" class="btn btn-primary">
|
||||
{% trans "Back to Recall Management" %}
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@ -137,7 +137,7 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
<div>
|
||||
<div class="col-lg-8 col-md-10 needs-validation {% if not items or not customer_count %}d-none{% endif %}">
|
||||
<div class="col-lg-12 col-md-10 needs-validation {% if not items or not customer_count %}d-none{% endif %}">
|
||||
<div class="card shadow-sm border-0 rounded-3">
|
||||
<div class="card-header bg-gray-200 py-3 border-0 rounded-top-3">
|
||||
<h3 class="mb-0 fs-4 text-center">
|
||||
@ -148,7 +148,6 @@
|
||||
|
||||
|
||||
<form id="mainForm" method="post" class="needs-validation {% if not items and not customer_count %}d-none{% endif %}">
|
||||
|
||||
{% csrf_token %}
|
||||
<div class="row g-3 col-12">
|
||||
{{ form|crispy }}
|
||||
|
||||
107
templates/staff/staff_detail.html
Normal file
107
templates/staff/staff_detail.html
Normal file
@ -0,0 +1,107 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n static custom_filters crispy_forms_filters %}
|
||||
{% block title %}
|
||||
{% trans 'Profile' %} {% endblock %}
|
||||
{% block content %}
|
||||
<div class="container-fluid">
|
||||
<div class="row align-items-center justify-content-between g-3 mb-4">
|
||||
<div class="col-auto">
|
||||
<h2 class="mb-0">{% trans 'Profile' %}</h2>
|
||||
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="row g-2 g-sm-3">
|
||||
<div class="col-auto">
|
||||
<a class="btn btn-phoenix-primary"
|
||||
href="{% url 'user_update' request.dealer.slug request.staff.slug %}"><span class="fas fa-edit me-2 text-primary"></span>{{ _("Edit") }} </a>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="{% url 'staff_password_reset' request.dealer.slug staff.user.pk %}" 'staff_password_reset' request.dealer.slug user_.pk
|
||||
class="btn btn-phoenix-danger"><span class="fas fa-key me-2"></span>{{ _("Change Password") }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-lg-8">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<div class="border-bottom border-dashed pb-4">
|
||||
<div class="row align-items-center g-3 g-sm-5 text-center text-sm-start">
|
||||
<div class="col-12 col-sm-auto">
|
||||
<input class="d-none" id="avatarFile" type="file" />
|
||||
<label class="cursor-pointer avatar avatar-5xl" for="avatarFile">
|
||||
{% if staff.logo %}
|
||||
<img src="{{ staff.logo.url }}"
|
||||
alt="{{ staff.get_local_name }}"
|
||||
class="rounded-circle"
|
||||
style="max-width: 150px" />
|
||||
{% else %}
|
||||
<span class="rounded-circle feather feather-user text-body-tertiary"
|
||||
style="max-width: 150px"></span>
|
||||
<img src="{% static 'images/logos/logo.png' %}"
|
||||
alt="{{ staff.get_local_name }}"
|
||||
class=""
|
||||
style="max-width: 150px" />
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-auto flex-1">
|
||||
<h3>{{ staff.get_local_name }}</h3>
|
||||
<p class="text-body-secondary">{{staff.user.groups.name}}</p>
|
||||
<p class="text-body-secondary">{% trans 'Joined' %} {{ staff.created|timesince }} {% trans 'ago' %}</p>
|
||||
<div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-between-center pt-4">
|
||||
<div>
|
||||
<h6 class="mb-2 text-body-secondary">{% trans 'last login'|capfirst %}</h6>
|
||||
<h4 class="fs-7 text-body-highlight mb-0">{{ staff.user.last_login|date:"D M d, Y H:i" }}</h4>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-body">
|
||||
<div class="border-bottom border-dashed">
|
||||
<h4 class="mb-3">{% trans 'Default Address' %}</h4>
|
||||
</div>
|
||||
<div class="pt-4 mb-7 mb-lg-4 mb-xl-7">
|
||||
<div class="row justify-content-between">
|
||||
<div class="col-auto">
|
||||
<h5 class="text-body-highlight">{% trans 'Address' %}</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<p class="text-body-secondary">{{ staff.address }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="border-top border-dashed pt-4">
|
||||
<div class="row flex-between-center mb-2">
|
||||
<div class="col-auto">
|
||||
<h5 class="text-body-highlight mb-0">{% trans 'Email' %}</h5>
|
||||
</div>
|
||||
<div class="col-auto">{{ staff.user.email }}</div>
|
||||
</div>
|
||||
<div class="row flex-between-center">
|
||||
<div class="col-auto">
|
||||
<h5 class="text-body-highlight mb-0">{% trans 'Phone' %}</h5>
|
||||
</div>
|
||||
<div class="col-auto" dir="ltr">{{ staff.phone_number }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
@ -98,7 +98,7 @@
|
||||
<i class="fa-regular fa-circle-left"></i>
|
||||
</a>
|
||||
<a class="btn btn-sm btn-phoenix-secondary"
|
||||
href="{% url 'staff_password_reset' request.dealer.slug user_.pk %}">
|
||||
href="{% url 'staff_password_reset' request.dealer.slug %}">
|
||||
{{ _("Reset Password") }}
|
||||
<i class="fa-solid fa-key"></i>
|
||||
</a>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user