Compare commits

..

No commits in common. "76ecf45a8839186581adfced441aa848572927e1" and "933cdf6101e4ee3ea9c7b942f2ee6777db1a626d" have entirely different histories.

13 changed files with 44 additions and 130 deletions

View File

@ -186,17 +186,6 @@ class CustomerForm(forms.ModelForm):
"address",
'image',
]
widgets = {
'title': forms.Select(attrs={'class': 'form-control form-control-sm'}),
'first_name': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'last_name': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'email': forms.EmailInput(attrs={'class': 'form-control form-control-sm'}),
'phone_number': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'national_id': forms.TextInput(attrs={'class': 'form-control form-control-sm'}),
'dob': DateInput(attrs={'class': 'form-control form-control-sm', 'type': 'date'}),
'address': forms.Textarea(attrs={'class': 'form-control form-control-sm'}),
'image': forms.FileInput(attrs={'class': 'form-control form-control-sm'}),
}
# class CustomerForm(forms.Form):
# """
# Represents a form for collecting customer information.
@ -1036,7 +1025,6 @@ class LeadForm(forms.ModelForm):
"hx-swap": "outerHTML",
"hx-on::before-request": "document.querySelector('#id_id_car_model').setAttribute('disabled', true)",
"hx-on::after-request": "document.querySelector('#id_id_car_model').removeAttribute('disabled')",
"hx-indicator": "#spinner",
}
),
required=True,

View File

@ -53,11 +53,6 @@ urlpatterns = [
path("submit_plan/", views.submit_plan, name="submit_plan"),
path('payment-callback/', views.payment_callback, name='payment_callback'),
#
path(
"dealers/activity/",
views.UserActivityLogListView.as_view(),
name="dealer_activity",
),
path("dealers/<slug:slug>/settings/", views.DealerSettingsView, name="dealer_settings"),
path("dealers/assign-car-makes/", views.assign_car_makes, name="assign_car_makes"),
path("dashboards/manager/", views.ManagerDashboard.as_view(), name="manager_dashboard"),
@ -72,6 +67,11 @@ urlpatterns = [
views.DealerUpdateView.as_view(),
name="dealer_update",
),
path(
"dealers/activity/",
views.UserActivityLogListView.as_view(),
name="dealer_activity",
),
# path('dealers/<int:pk>/delete/', views.DealerDeleteView.as_view(), name='dealer_delete'),
# CRM URLs
path(

View File

@ -28,7 +28,6 @@ from django.contrib import messages
from django.http import Http404, JsonResponse, HttpResponseForbidden
from django.forms import HiddenInput, ValidationError
from django.shortcuts import HttpResponse
from django.db.models import Sum, F, Count
from django.core.paginator import Paginator
from django.contrib.auth.models import User
@ -2092,7 +2091,7 @@ class CustomerUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView
@login_required
def delete_customer(request, slug):
def delete_customer(request, pk):
"""
Deletes a customer from the system and deactivates the corresponding user account.
@ -2108,7 +2107,7 @@ def delete_customer(request, slug):
:return: A redirect response to the customer list page.
:rtype: HttpResponseRedirect
"""
customer = get_object_or_404(models.Customer, slug=slug)
customer = get_object_or_404(models.Customer, pk=pk)
customer.deactivate_account()
messages.success(request, _("Customer deactivated successfully"))
return redirect("customer_list")
@ -3761,7 +3760,7 @@ def create_sale_order(request, pk):
models.Car.objects.get(vin=item.item_model.name).mark_as_sold(request)
messages.success(request, "Sale Order created successfully")
return redirect("estimate_detail", pk=estimate.pk)
return redirect("estimate_detail", pk=pk)
form = forms.SaleOrderForm()
form.fields["estimate"].queryset = EstimateModel.objects.filter(pk=pk)
@ -4247,6 +4246,7 @@ def invoice_create(request, pk):
form = forms.InvoiceModelCreateForm(
entity_slug=entity.slug, user_model=entity.admin
)
form.initial.update(
{
"customer": estimate.customer,
@ -4255,7 +4255,6 @@ def invoice_create(request, pk):
"unearned_account": dealer.settings.invoice_unearned_account,
}
)
print(dir(form.fields["customer"]))
context = {
"form": form,
@ -4676,15 +4675,11 @@ def lead_create(request):
form = forms.LeadForm()
form.filter_qs(dealer=dealer)
if make := request.GET.get("id_car_make", None):
make = request.GET.get("id_car_make", None)
if make:
form.fields["id_car_model"].queryset = models.CarModel.objects.filter(
id_car_make=int(make)
)
else:
if first_make := form.fields["id_car_make"].queryset.first():
form.fields["id_car_model"].queryset = models.CarModel.objects.filter(
id_car_make=first_make.id_car_make
)
return render(request, "crm/leads/lead_form.html", {"form": form})
def lead_tracking(request):
@ -4774,7 +4769,7 @@ class LeadUpdateView(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
@login_required
@permission_required("inventory.delete_lead", raise_exception=True)
def LeadDeleteView(request, slug):
def LeadDeleteView(request, pk):
"""
Handles the deletion of a Lead along with its associated customer and potentially
a related user account in the system. Ensures proper permissions and login
@ -4785,7 +4780,7 @@ def LeadDeleteView(request, slug):
:param pk: The primary key identifier of the Lead to be deleted.
:return: An HTTP redirect response to the lead list page.
"""
lead = get_object_or_404(models.Lead, slug=slug)
lead = get_object_or_404(models.Lead, pk=pk)
try:
User.objects.get(email=lead.customer.email).delete()
lead.customer.delete()
@ -5198,12 +5193,6 @@ class OpportunityCreateView(CreateView,SuccessMessageMixin, LoginRequiredMixin):
dealer = get_user_type(self.request)
return context
# def get_form_kwargs(self):
# kwargs = super().get_form_kwargs()
# dealer = get_user_type(self.request)
# kwargs["car"].queryset = models.Car.objects.filter(dealer=dealer,)
# return kwargs
# def get_initial(self):
# initial = super().get_initial()
# if self.kwargs.get("pk", None):

View File

@ -1,26 +1,11 @@
{% extends 'base.html' %}
{% load i18n static crispy_forms_filters %}
{% block customcss %}
<style>
.htmx-indicator{
opacity:0;
transition: opacity 500ms ease-in;
}
.htmx-request .htmx-indicator{
opacity:1;
}
.htmx-request.htmx-indicator{
opacity:1;
}
</style>
{% endblock customcss %}
{% block content %}
<div class="container-fluid">
<h1>{% if object %}{{ _("Update") }}{% else %}{{ _("Create") }}{% endif %}</h1>
<div class="row mb-3">
<div class="col-sm-6 col-md-8">
<div class="col-sm-6 col-md-8">
<form class="form" method="post">
{% csrf_token %}
{{ form|crispy }}
@ -36,23 +21,4 @@
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// First, create the spinner div (or use the existing one)
const spinner = document.createElement('div');
spinner.id = 'spinner';
spinner.className = 'htmx-indicator spinner-border inline-spinner';
spinner.setAttribute('role', 'status');
spinner.innerHTML = '<span class="visually-hidden">Loading...</span>';
// Find the form field you want to place it next to
// Replace 'id_your_field_name' with the actual ID of your form field
const targetField = document.getElementById('div_id_id_car_model');
if (targetField) {
// Insert the spinner right after the target field
targetField.parentNode.insertBefore(spinner, targetField.nextSibling);
}
});
</script>
{% endblock %}

View File

@ -132,7 +132,9 @@
{{ _("Action") }}
</th>
<th class="text-end white-space-nowrap align-middle" scope="col"></th>
</tr>image
</tr>
</thead>
<tbody class="list" id="lead-tables-body">
{% for lead in leads %}
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
@ -157,7 +159,7 @@
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="name align-middle white-space-nowrap ps-0">
<div class="d-flex align-items-center">
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.slug %}">{{lead.full_name|capfirst}}</a>
<div><a class="fs-8 fw-bold" href="{% url 'lead_detail' lead.slug %}">{{lead.full_name}}</a>
<div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2"></p>
{% if lead.status == "new" %}

View File

@ -41,11 +41,7 @@
<div class="card-body d-flex flex-column justify-content-between pb-3">
<div class="row align-items-center g-5 mb-3 text-center text-sm-start">
<div class="col-12 col-sm-auto mb-sm-2">
<div class="avatar avatar-5xl">
{% if customer.image %}
<img class="rounded-circle" src="{{ customer.image.url }}" alt="" />
{% endif %}
</div>
<div class="avatar avatar-5xl"><img class="rounded-circle" src="{{ customer.image.url }}" alt="" /></div>
</div>
<div class="col-12 col-sm-auto flex-1">
<h3>{{ customer.full_name }}</h3>

View File

@ -20,7 +20,7 @@
{% if not car.ready %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>
<p class="mb-0 flex-1">{{ _("This car information is not complete , please add colors and finances before making it ready for sale .") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'add_color' car.slug %}"> {{ _("Add Color") }} </a></p>
<p class="mb-0 flex-1">{{ _("This car information is not complete , please add colors and finances before making it ready for sale .") }}</p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}

View File

@ -11,24 +11,23 @@
<a href="{% url 'bank_account_create' %}" class="btn btn-md btn-phoenix-primary"><i class="fa fa-plus me-2"></i>{% trans "Add Bank Account" %}</a>
</div>
<div class="table-responsive px-1 scrollbar mt-3">
<table class="table align-items-center table-flush">
<div class="table-responsive px-1 scrollbar">
<table class="table fs-9 mb-0 border-top border-translucent">
<thead>
<tr class="bg-body-highlight">
<th class="border-top border-translucent ps-3">{% trans "Name" %}</th>
<th class="border-top border-translucent ps-3">{% trans "Account Number" %}</th>
<th class="border-top border-translucent text-end pe-3">{% trans "Type" %}</th>
<th class="border-top border-translucent text-end pe-3" scope="col">{% trans "Action" %}</th>
<tr>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Name" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Account Number" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Type" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Action" %}</th>
</tr>
</thead>
<tbody class="list">
{% for bank in bank_accounts %}
<tr class="hover-actions-trigger btn-reveal-trigger position-static">
<td class="align-middle ps-3">{{ bank.name }}</td>
<td class="align-middle ps-3">{{ bank.account_number }}</td>
<td class="align-middle product text-end pe-3 ">{{ bank.account_type|capfirst }}</td>
<td class="align-middle product text-end pe-3 ">
<td class="align-middle product white-space-nowrap">{{ bank.name }}</td>
<td class="align-middle product white-space-nowrap py-0">{{ bank.account_number }}</td>
<td class="align-middle product white-space-nowrap py-0">{{ bank.account_type|capfirst }}</td>
<td class="">
<a href="{% url 'bank_account_update' bank.pk %}"
class="btn btn-sm btn-phoenix-success">
{% trans "Update" %}
@ -43,14 +42,8 @@
</tbody>
</table>
</div>
<div class="d-flex justify-content-center">
<div class="d-flex justify-content-center">
</div>
</div>
{% endblock %}

View File

@ -16,7 +16,7 @@
<div class="table-responsive px-1 scrollbar mt-3">
<table class="table fs-9 mb-0 border-top border-translucent">
<thead>
<tr class="bg-body-highlight">
<tr>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Ledger Name" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Journal Entries" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Created Date" %}</th>

View File

@ -10,7 +10,7 @@
<h1 class="mb-2 mb-lg-0">{% trans "Payment Failed" %}</h1>
<nav class="breadcrumbs">
<ol>
<li><a href="{% url 'home' %}">{% trans "Home"%}</a></li>
<li><a href="">{% trans "Home"%}</a></li>
<li class="current">{% trans "Failed"%}</li>
</ol>
</nav>
@ -22,13 +22,13 @@
<div class="container text-center" data-aos="fade-up">
<div class="py-5">
<i class="bi bi-x-circle-fill text-danger" style="font-size: 5rem;"></i>
<h2 class="mt-4">{% trans "Payment Failed"%}</h2>
<h2 class="mt-4">{% trans "Payment Failed"%}</h2>
{% if message %}
<p class="lead">{{message}}.</p>
{% else %}
<p class="lead">{% trans "We couldn't process your payment. Please try again"%}.</p>
{% endif %}
<a href="{% url 'home' %}" class="btn btn-primary mt-3">
<a href="" class="btn btn-primary mt-3">
<i class="bi bi-house-door"></i> {% trans "Back to Home"%}
</a>
</div>

View File

@ -29,7 +29,7 @@
<i class="bi bi-house-door"></i> View Invoice
</a>
{% endif %}
<a href="{% url 'home' %}" class="btn btn-primary mt-3">
<a href="" class="btn btn-primary mt-3">
<i class="bi bi-house-door"></i> Back to Home
</a>
</div>

View File

@ -52,6 +52,7 @@
<h1 class="text-center mb-5">{{ _("Choose Your Plan")}}</h1>
<form method="POST" action="{% url 'submit_plan' %}" id="wizardForm">
{% csrf_token %}
<!-- Step 1: Plan Selection -->
<div class="step" id="step1">
<h4 class="mb-4">1. {{ _("Select a Plan")}}</h4>
@ -207,27 +208,6 @@
{% block customJS %}
<script>
document.addEventListener("DOMContentLoaded", function () {
const form = document.getElementById('wizardForm');
form.addEventListener('submit', function(e) {
e.preventDefault(); // Prevent default form submission
// Show loading alert
Swal.fire({
title: 'Processing...',
html: 'Please wait while we submit your form',
allowOutsideClick: false,
didOpen: () => {
Swal.showLoading();
}
});
// Submit the form after a slight delay to ensure Swal is shown
setTimeout(() => {
form.submit();
}, 100);
});
const radios = document.querySelectorAll('.btn-check');
radios.forEach(radio => {
radio.addEventListener('change', function () {

View File

@ -17,14 +17,14 @@
{% if not items %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>
<p class="mb-0 flex-1">{{ _("Please add at least one car before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'car_add' %}"> {{ _("Add Car") }} </a></p>
<p class="mb-0 flex-1">{{ _("Please add at least one car before creating a quotation.") }}</p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
{% if not customer_count %}
<div class="alert alert-outline-warning d-flex align-items-center" role="alert">
<i class="fa-solid fa-circle-info fs-6"></i>&nbsp;&nbsp;
<p class="mb-0 flex-1"> {{ _("Please add at least one customer before creating a quotation.") }}<a class="ms-3 text-body-primary fs-9" href="{% url 'customer_create' %}"> {{ _("Add Customer") }} </a></p>
<p class="mb-0 flex-1"> {{ _("Please add at least one customer before creating a quotation.") }}</p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
@ -57,7 +57,7 @@
<div class="col-12">
<button id="addMoreBtn" class="btn btn-sm btn-primary"><i class="fa-solid fa-plus me-2"></i> {{ _("Add More")}}</button>
</div>
</div>
</div>
@ -67,7 +67,7 @@
<a href="{% url 'estimate_list' %}" class="btn btn-danger"><i class="fa-solid fa-ban me-1"></i> {% trans "Cancel" %}</a>
</div> {% endcomment %}
<div class="d-flex justify-content-center">
<button class="btn btn-sm btn-success me-2" type="submit"><i class="fa-solid fa-floppy-disk me-1"></i>
<!--<i class="bi bi-save"></i> -->
{{ _("Save") }}
@ -75,9 +75,9 @@
<a href="{{request.META.HTTP_REFERER}}" class="btn btn-sm btn-danger"><i class="fa-solid fa-ban me-1"></i>{% trans "Cancel" %}</a>
</div>
</form>
</div>
{% endblock content %}