This commit is contained in:
Marwan Alwali 2025-02-08 11:50:06 +03:00
parent 48d70dc084
commit c4ea8db46e
22 changed files with 1631 additions and 1416 deletions

View File

@ -35,6 +35,7 @@ admin.site.register(models.Notification)
admin.site.register(models.Lead)
admin.site.register(models.Activity)
admin.site.register(models.Schedule)
admin.site.register(models.Notes)
@admin.register(models.CarMake)
class CarMakeAdmin(admin.ModelAdmin):

View File

@ -737,7 +737,7 @@ class EstimateModelCreateForm(EstimateModelCreateFormBase):
'customer': forms.Select(attrs={
'id': 'djl-customer-estimate-customer-input',
'class': 'input',
'label': _('Customer MARWAN'),
'label': _('Customer'),
}),
'terms': forms.Select(attrs={
'id': 'djl-customer-estimate-terms-input',

View File

@ -1224,7 +1224,7 @@ class Schedule(models.Model):
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return f"Scheduled {self.purpose} with {self.customer.customer_name} on {self.scheduled_at}"
return f"Scheduled {self.purpose} with {self.lead.full_name} on {self.scheduled_at}"
def schedule_past_date(self):
if self.scheduled_at < timezone.now():

View File

@ -862,7 +862,7 @@ def create_activity_on_schedule_creation(sender, instance, created, **kwargs):
content_object=instance,
activity_type='Schedule Created',
created_by=instance.scheduled_by.user,
notes=f"New schedule created for {instance.purpose} with {instance.customer.customer_name} on {instance.scheduled_at}."
notes=f"New schedule created for {instance.purpose} with {instance.lead.full_name} on {instance.scheduled_at}."
)

View File

@ -93,8 +93,10 @@ urlpatterns = [
"crm/leads/<int:pk>/update/", views.LeadUpdateView.as_view(), name="lead_update"
),
path("crm/leads/<int:pk>/delete/", views.LeadDeleteView, name="lead_delete"),
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note"),
path("crm/leads/<int:pk>/lead-convert/", views.lead_convert, name="lead_convert"),
path("crm/leads/<int:pk>/add-note/", views.add_note_to_lead, name="add_note"),
path('crm/leads/<int:pk>/update-note/', views.update_note, name='update_note'),
path("crm/leads/<int:pk>/delete-note/", views.delete_note, name="delete_note"),
path(
"crm/leads/<int:pk>/add-activity/",
views.add_activity_to_lead,

View File

@ -2981,6 +2981,7 @@ def LeadDeleteView(request,pk):
return redirect("lead_list")
@login_required
def add_note_to_lead(request, pk):
lead = get_object_or_404(models.Lead, pk=pk)
if request.method == "POST":
@ -2988,14 +2989,45 @@ def add_note_to_lead(request, pk):
if form.is_valid():
note = form.save(commit=False)
note.content_object = lead
note.created_by = request.user
note.save()
return redirect("lead_detail", pk=pk)
messages.success(request, "Note added successfully!")
return redirect("lead_detail", pk=lead.pk)
else:
form = forms.NoteForm()
return render(request, "crm/add_note.html", {"form": form, "lead": lead})
return render(request, "crm/note_form.html", {"form": form, "lead": lead})
@login_required
def update_note(request, pk):
note = get_object_or_404(models.Notes, pk=pk, created_by=request.user)
lead_pk = note.content_object.pk
if request.method == "POST":
form = forms.NoteForm(request.POST, instance=note)
if form.is_valid():
updated_note = form.save(commit=False)
updated_note.content_object = note.content_object
updated_note.created_by = request.user
updated_note.save()
messages.success(request, "Note updated successfully!")
return redirect("lead_detail", pk=lead_pk)
else:
form = forms.NoteForm(instance=note)
return render(request, "crm/note_form.html", {"form": form, "note": note})
@login_required
def delete_note(request, pk):
note = get_object_or_404(models.Notes, pk=pk, created_by=request.user)
lead_pk = note.content_object.pk
note.delete()
messages.success(request, _("Note deleted successfully."))
return redirect("lead_detail", pk=lead_pk)
@login_required
def lead_convert(request, pk):
lead = get_object_or_404(models.Lead, pk=pk)
dealer = get_user_type(request)
@ -3007,6 +3039,8 @@ def lead_convert(request, pk):
messages.success(request, "Lead converted to customer successfully!")
return redirect("opportunity_create",pk=lead.pk)
@login_required
def schedule_lead(request, pk):
lead = get_object_or_404(models.Lead, pk=pk)
if request.method == "POST":
@ -3026,6 +3060,8 @@ def schedule_lead(request, pk):
form = forms.ScheduleForm()
return render(request, "crm/leads/schedule_lead.html", {"lead": lead, "form": form})
@login_required
def send_lead_email(request, pk):
lead = get_object_or_404(models.Lead, pk=pk)
dealer = get_user_type(request)
@ -3068,6 +3104,8 @@ def send_lead_email(request, pk):
{"lead": lead, "message": msg},
)
@login_required
def add_activity_to_lead(request, pk):
lead = get_object_or_404(models.Lead, pk=pk)
if request.method == "POST":

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -63,7 +63,7 @@
<!-- Down Payment Field -->
<div class="form-floating mb-3">
{{ form.down_payment|add_class:"form-control form-control-sm" }}
<label for="{{ form.down_payment.id_for_label }}">{{ _("Down Payment<")}}/label>
<label for="{{ form.down_payment.id_for_label }}">{{ _("Down Payment")}}</label>
{% if form.down_payment.errors %}
<div class="alert alert-danger mt-2">
{{ form.down_payment.errors }}

View File

@ -1,10 +0,0 @@
{% extends 'base.html' %}
{% load i18n static crispy_forms_filters %}
{% block content %}
<h1>Add Note to {{ lead.first_name }} {{ lead.last_name }}</h1>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-phoenix-primary" type="submit">Add Note</button>
</form>
{% endblock %}

File diff suppressed because it is too large Load Diff

View File

@ -2,7 +2,7 @@
{% load i18n static crispy_forms_filters %}
{% block content %}
<h1>{% if object %}{{ _("Update") }}{% else %}{{ _("Create") }}{% endif %}</h1>
<h3>{% if object %}{{ _("Update") }}{% else %}{{ _("Create") }}{% endif %}</h3>
<form method="post">
{% csrf_token %}
{{ form|crispy }}

View File

@ -0,0 +1,17 @@
{% load i18n static crispy_forms_filters %}
{% if form.instance.pk %}
<form method="post" action="{% url 'update_note' note.pk %}" enctype="multipart/form-data">
{% else %}
<form method="post" action="{% url 'add_note' lead.pk %}" enctype="multipart/form-data">
{% endif %}
{% csrf_token %}
{{ form|crispy }}
{% if form.instance.pk %}
<button type="submit" class="btn btn-sm btn-primary w-100">{{ _("Update") }}</button>
{% else %}
<button type="submit" class="btn btn-sm btn-success w-100">{{ _("Add") }}</button>
{% endif %}
</form>

View File

@ -8,7 +8,7 @@
<h2 class="mb-5">{{ _("Opportunities") }}</h2>
<div class="d-xl-flex justify-content-between">
<div class="mb-3">
<a class="btn btn-primary me-4" href="{% url 'opportunity_create' %}"><span class="fas fa-plus me-2"></span>{{ _("Add Opportunity") }}</a>
<a class="btn btn-sm btn-phoenix-primary me-4" href="{% url 'opportunity_create' %}"><span class="fas fa-plus me-2"></span>{{ _("Add Opportunity") }}</a>
</div>
</div>
</div>
@ -104,16 +104,16 @@
<p class="fs-9 mb-1 fw-bold"> {{ _("Probability") }}: %</p>
<div class="progress" style="height:16px">
{% if opportunity.probability >= 25 and opportunity.probability < 49 %}
<div class="progress-bar rounded-pill bg-danger" role="progressbar" style="width: {{ opportunity.probability }}%" aria-valuenow="{{ opportunity.probability }}" aria-valuemin="0" aria-valuemax="100">
<span class="fw-bold fs-10 text-sm-end text-secondary me-1">{{ opportunity.probability }}</span>
<div class="progress-bar rounded-pill bg-danger-dark" role="progressbar" style="width: {{ opportunity.probability }}%" aria-valuenow="{{ opportunity.probability }}" aria-valuemin="0" aria-valuemax="100">
<span class="fw-bolder fs-9 text-sm-end me-1">{{ opportunity.probability }}</span>
</div>
{% elif opportunity.probability >= 50 and opportunity.probability <= 74 %}
<div class="progress-bar rounded-pill bg-warning" role="progressbar" style="width: {{ opportunity.probability }}%" aria-valuenow="{{ opportunity.probability }}" aria-valuemin="0" aria-valuemax="100">
<span class="fw-bold fs-10 text-sm-end text-secondary me-1">{{ opportunity.probability }}</span>
<div class="progress-bar rounded-pill bg-warning-dark" role="progressbar" style="width: {{ opportunity.probability }}%" aria-valuenow="{{ opportunity.probability }}" aria-valuemin="0" aria-valuemax="100">
<span class="fw-bolder fs-9 text-sm-end me-1">{{ opportunity.probability }}</span>
</div>
{% elif opportunity.probability >= 75 and opportunity.probability <= 100 %}
<div class="progress-bar rounded-pill bg-success" role="progressbar" style="width: {{ opportunity.probability }}%" aria-valuenow="{{ opportunity.probability }}" aria-valuemin="0" aria-valuemax="100">
<span class="fw-bold fs-10 text-sm-end text-secondary me-1">{{ opportunity.probability }}</span>
<div class="progress-bar rounded-pill bg-success-dark" role="progressbar" style="width: {{ opportunity.probability }}%" aria-valuenow="{{ opportunity.probability }}" aria-valuemin="0" aria-valuemax="100">
<span class="fw-bolder fs-9 text-sm-end me-1">{{ opportunity.probability }}</span>
</div>
{% endif %}
</div>

View File

@ -11,7 +11,7 @@
<div class="col-auto">
<div class="d-md-flex justify-content-between">
<div>
<a href="{% url 'customer_create' %}" class="btn btn-primary me-4"><span class="fas fa-plus me-2"></span>{{ _("Add Customer") }}</a>
<a href="{% url 'customer_create' %}" class="btn btn-sm btn-phoenix-primary me-4"><span class="fas fa-plus me-2"></span>{{ _("Add Customer") }}</a>
</div>
</div>
</div>

View File

@ -364,7 +364,7 @@
</div>
<!-- Registration Modal -->
<div class="modal fade" id="registrationModal" tabindex="-1" aria-labelledby="cregistrationModalLabel" aria-hidden="true">
<div class="modal fade" id="registrationModal" tabindex="-1" aria-labelledby="registrationModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">

View File

@ -17,38 +17,35 @@
{% csrf_token %}
{% include 'partials/form_errors.html' %}
<div class="d-flex flex-column min-vh-100">
<div class="d-flex flex-column flex-sm-grow-1 p-4">
<main class="d-grid gap-4 p-1">
<div class="d-flex flex-column flex-sm-grow-1 p-0">
<div class="row g-4">
<h3 class="mb-3">{% trans 'Add Car' %}</h3>
<!-- VIN -->
<div class="col-lg-4 col-xl-6">
<div class="card h-100">
<div class="card bg-body">
<div class="card-body">
<div class="row">
<div>
<div class="form-floating">
<input type="text"
class="form-control"
class="form-control form-control-sm"
id="{{ form.vin.id_for_label }}" name="{{ form.vin.html_name }}" required/>
<label for="{{ form.vin.id_for_label }}">{% trans 'VIN' %}</label>
</div>
</div>
<div class="input-group input-group-sm my-2">
<button type="button"
class="btn btn-phoenix-warning fs-8 rounded-start"
class="btn btn-warning rounded-start"
id="scan-vin-btn"
data-bs-toggle="modal"
data-bs-target="#scannerModal">
<span class="fas fa-camera"></span>
<span class="fas fa-camera fs-9"></span>
</button>
<button type="button" class="btn btn-sm btn-phoenix-primary rounded-end" id="decodeVinBtn">
{% trans 'Search' %}
<button type="button" class="btn btn-sm btn-primary rounded-end ms-1" id="decodeVinBtn">
<span class="fas fa-search fs-9 fw-bolder"></span>
</button>
</div>
</div>
<div class="row my-3">
<div class="row mt-2">
<div class="col-6">
<div class="form-floating">
<span class="text-success fw-bold" id="year-check"></span>
@ -70,7 +67,7 @@
</div>
</div>
</div>
<div class="row my-3">
<div class="row mt-2">
<div class="col-6" id="make-row">
<div class="form-floating">
<span class="text-success fw-bold" id="make-check"></span>
@ -89,7 +86,7 @@
</div>
</div>
</div>
<div class="row my-3">
<div class="row mt-2">
<div class="col-6" id="model-row">
<div class="form-floating">
<span class="text-success fw-bold" id="model-check"></span>
@ -118,18 +115,18 @@
disabled>{% trans 'options'|capfirst %}
</button>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="row g-3">
<div class="col-lg-4 col-xl-6">
<div class="row g-3">
<div class="row">
<!--Vendor Field-->
<div class="col-lg-4 col-xl-4">
<div class="card h-100">
<div class="card bg-body my-1">
<div class="card-body">
<div class="form-floating">
{{ form.vendor|add_class:"form-select form-select-sm" }}
@ -142,7 +139,7 @@
</div>
<!-- Stock Type Card -->
<div class="col-lg-4 col-xl-4">
<div class="card h-100">
<div class="card bg-body my-1">
<div class="card-body">
<div class="form-floating">
{{ form.stock_type|add_class:"form-select form-select-sm" }}
@ -155,7 +152,7 @@
</div>
<!-- Mileage Card -->
<div class="col-lg-4 col-xl-4">
<div class="card h-100">
<div class="card bg-body my-1">
<div class="card-body">
<div class="form-floating">
{{ form.mileage|add_class:"form-control form-control-sm" }}
@ -167,7 +164,7 @@
<!-- Receiving Date Field -->
<div class="col-lg-4 col-xl-4">
<div class="card h-100 border-1 rounded shadow">
<div class="card bg-body my-3">
<div class="card-body">
<div class="form-floating">
{{ form.receiving_date|add_class:"form-control form-control-sm" }}
@ -182,7 +179,7 @@
<!-- Remarks Card -->
<div class="col-lg-4 col-xl-8">
<div class="card h-100">
<div class="card bg-body my-3">
<div class="card-body">
<div class="form-floating">
{{ form.remarks|add_class:"form-control form-control-sm" }}
@ -205,10 +202,10 @@
</div>
</div>
</div>
</main>
</div>
</div>
</form>
<!--Specification Modal-->
<div class="modal fade"
@ -292,6 +289,7 @@
</div>
</div>
</div>
</form>
</div>
<script>