This commit is contained in:
gitea 2025-02-27 15:02:41 +00:00
parent 91ef0da13b
commit 86d3c3d586
15 changed files with 273 additions and 203 deletions

2
.gitignore vendored
View File

@ -6,6 +6,8 @@ __pycache__
**/*__pycache__
db.sqlite
db.sqlite3
db.sqlite3.backup
db.sqlite*
media
car_inventory/settings.py
scripts/dsrpipe.py

View File

@ -944,7 +944,7 @@ class StaffTypes(models.TextChoices):
class Staff(models.Model, LocalizedNameMixin):
staff_member = models.OneToOneField(StaffMember, on_delete=models.CASCADE, related_name="staff")
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="staff")
dealer = models.ForeignKey(Dealer, on_delete=models.CASCADE, related_name="staff")
name = models.CharField(max_length=255, verbose_name=_("Name"))
arabic_name = models.CharField(max_length=255, verbose_name=_("Arabic Name"))
phone_number = PhoneNumberField(region="SA", verbose_name=_("Phone Number"))
@ -1225,6 +1225,8 @@ class Lead(models.Model):
def __str__(self):
return f"{self.first_name} {self.last_name}"
def get_user_model(self):
return User.objects.get(email=self.email) or None
@property
def is_converted(self):
return bool(self.customer)
@ -1247,20 +1249,20 @@ class Lead(models.Model):
customer = entity.get_customers().filter(email=self.email).first()
if entity and not customer:
customer = entity.create_customer(
customer_model_kwargs={
"customer_name": self.full_name,
"address_1": self.address,
"phone": self.phone_number,
"email": self.email,
}
)
customer.additional_info = {}
commit=False,
customer_model_kwargs={
"customer_name": self.full_name,
"address_1": self.address,
"phone": self.phone_number,
"email": self.email,
}
)
customer.additional_info.update({"info":self.to_dict()})
customer.additional_info.update({"stage":"qualified"})
customer.additional_info.update({"type":"customer"})
customer.save()
self.customer = customer
self.status = Status.QUALIFIED
customer.save()
self.save()
return customer
@ -1760,7 +1762,7 @@ class SaleOrder(models.Model):
('cash', 'Cash'),
('finance', 'Finance'),
('lease', 'Lease'),
])
])
comments = models.TextField(blank=True, null=True)
formatted_order_id = models.CharField(max_length=10, unique=True, editable=False)
created = models.DateTimeField(auto_now_add=True)

View File

@ -666,9 +666,13 @@ def create_ledger_vendor(sender, instance, created, **kwargs):
@receiver(post_save, sender=models.CustomerModel)
def create_customer_user(sender, instance, created, **kwargs):
if created:
first_name = instance.additional_info.get("customer_info").get("first_name")
last_name = instance.additional_info.get("customer_info").get("last_name")
user = User.objects.create(
username=instance.email,
email=instance.email,
first_name=first_name if first_name else '',
last_name=last_name if last_name else '',
)
instance.additional_info.update({"user_info": to_dict(user)})
user.set_unusable_password()

View File

@ -74,11 +74,11 @@ urlpatterns = [
"customers/create/", views.CustomerCreateView, name="customer_create"
),
path(
"customers/<str:pk>/update/",
"customers/<uuid:pk>/update/",
views.CustomerUpdateView,
name="customer_update",
),
path("customers/<str:pk>/delete/", views.delete_customer, name="customer_delete"),
path("customers/<uuid:pk>/delete/", views.delete_customer, name="customer_delete"),
path(
"customers/<str:customer_id>/opportunities/create/",
views.OpportunityCreateView.as_view(),

View File

@ -247,7 +247,6 @@ def get_financial_values(model):
additional_services = []
for i in data:
print(i)
if i.item_model.additional_info["additional_services"]:
additional_services.extend(
[

View File

@ -10,7 +10,7 @@ from calendar import month_name
from pyzbar.pyzbar import decode
from urllib.parse import urlparse, urlunparse
#####################################################################
from django.db.models.deletion import RestrictedError
# Django
from django.db.models import Q
from django.conf import settings
@ -1250,7 +1250,6 @@ class CustomerDetailView(LoginRequiredMixin, DetailView):
context["total"] = total
return context
def add_note_to_customer(request, customer_id):
customer = get_object_or_404(CustomerModel, pk=customer_id)
if request.method == "POST":
@ -1312,9 +1311,9 @@ def CustomerCreateView(request):
"email": form.cleaned_data["email"],
}
)
customer.additional_info = {}
customer.additional_info["customer_info"] = customer_dict
customer.additional_info["type"] = "customer"
# customer.additional_info = {}
customer.additional_info.update({"customer_info": customer_dict})
customer.additional_info.update({"type": "customer"})
customer.save()
messages.success(request, _("Customer created successfully."))
@ -1375,11 +1374,15 @@ def CustomerUpdateView(request, pk):
@login_required
def delete_customer(request, pk):
customer = get_object_or_404(models.Customer, pk=pk)
customer.delete()
customer = get_object_or_404(models.CustomerModel, pk=pk)
user = User.objects.get(email=customer.email)
customer.active = False
user.is_active = False
customer.save()
user.save()
messages.success(request, _("Customer deleted successfully."))
return redirect("customer_list")
@ -1581,10 +1584,11 @@ class UserCreateView(
email = form.cleaned_data["email"]
password = "Tenhal@123"
user = User.objects.create_user(username=form.cleaned_data["name"], email=email, password=password)
user.is_staff = True
user.save()
staff_member = StaffMember.objects.create(user=user)
staff_member = StaffMember.objects.create(user=user)
for service in form.cleaned_data["service_offered"]:
staff_member.services_offered.add(service)
staff = form.save(commit=False)
@ -1639,9 +1643,9 @@ class UserUpdateView(
return super().form_valid(form)
def UserDeleteview(request, pk):
user = get_object_or_404(models.Staff, pk=pk)
user.staff_member.delete()
user.delete()
staff = get_object_or_404(models.Staff, pk=pk)
staff.staff_member.delete()
staff.delete()
messages.success(request, _("User deleted successfully."))
return redirect("user_list")
@ -1767,9 +1771,14 @@ def OrganizationUpdateView(request,pk):
# success_message = "Organization deleted successfully."
def OrganizationDeleteView(request, pk):
organization = get_object_or_404(CustomerModel, pk=pk)
organization.delete()
messages.success(request, _("Organization deleted successfully."))
return redirect("organization_list")
try:
User.objects.get(email=organization.email).delete()
organization.delete()
messages.success(request, _("Organization deleted successfully."))
except Exception as e:
print("unable to delete user", e)
messages.error(request,_("Unable to delete organization"))
return redirect("organization_list")
class RepresentativeListView(LoginRequiredMixin, ListView):
model = models.Representative
@ -2157,7 +2166,7 @@ def create_estimate(request,pk=None):
form = forms.EstimateModelCreateForm(
entity_slug=entity.slug, user_model=entity.admin
)
form.fields["customer"].queryset = entity.get_customers().filter(active=True)
form.fields["customer"].queryset = entity.get_customers().filter(active=True,additional_info__type="customer")
if pk:
opportunity = models.Opportunity.objects.get(pk=pk)
@ -2181,7 +2190,8 @@ def create_estimate(request,pk=None):
}
for x in car_list
],
"opportunity_id": pk if pk else None
"opportunity_id": pk if pk else None,
"customer_count": entity.get_customers().count()
}
return render(request, "sales/estimates/estimate_form.html", context)
@ -2686,28 +2696,40 @@ def lead_create(request):
if form.is_valid():
instance = form.save(commit=False)
dealer = get_user_type(request)
instance.dealer = dealer
# staff = None
# if hasattr(request.user, "staffmember"):
# staff = request.user.staffmember.staff
# instance.staff = staff
instance.dealer = dealer
instance.staff = form.cleaned_data.get("staff")
# creating customer in ledger
customer = dealer.entity.get_customers().filter(email=instance.email).first()
if not customer:
customer = dealer.entity.create_customer(
commit=False,
customer_model_kwargs={
"customer_name": instance.full_name,
"address_1": instance.address,
"phone": instance.phone_number,
"email": instance.email,
"sales_tax_rate": 0.15,
"sales_tax_rate": 0.15,
}
)
customer.additional_info.update({'stage': 'lead'})
customer.save()
customer_info = {
"first_name": instance.first_name,
"last_name": instance.last_name,
"address": instance.address,
"phone_number": str(instance.phone_number),
"email": instance.email,
}
customer.additional_info.update({"customer_info": customer_info })
customer.additional_info.update({"type":"lead"})
customer.save()
instance.customer = customer
# try:
# user = User.objects.get(email=customer.email)
# user.first_name = instance.first_name
# user.last_name = instance.last_name
# user.save()
# except Exception as e:
# print(e)
instance.save()
messages.success(request, "Lead created successfully!")
return redirect("lead_list")
@ -2732,6 +2754,11 @@ class LeadUpdateView(UpdateView):
@login_required
def LeadDeleteView(request,pk):
lead = get_object_or_404(models.Lead, pk=pk)
try:
User.objects.get(email=lead.customer.email).delete()
lead.customer.delete()
except Exception as e:
print(e)
lead.delete()
messages.success(request, "Lead deleted successfully!")
return redirect("lead_list")
@ -2809,11 +2836,11 @@ def lead_convert(request, pk):
@login_required
def schedule_lead(request, pk):
if not hasattr(request.user,"staffmember"):
if not request.is_staff:
messages.error(request, "You do not have permission to schedule lead.")
return redirect("lead_list")
dealer = get_user_type(request)
lead = get_object_or_404(models.Lead, pk=pk, dealer=dealer)
if request.method == "POST":
form = forms.ScheduleForm(request.POST)
@ -2821,13 +2848,13 @@ def schedule_lead(request, pk):
instance = form.save(commit=False)
instance.lead = lead
instance.scheduled_by = request.user
instance.customer = lead.customer
# Create AppointmentRequest
service = Service.objects.filter(name=instance.scheduled_type).first()
if not service:
messages.error(request, "Service not found!")
return redirect("lead_list")
staff_member = request.user.staffmember
return redirect("lead_list")
try:
appointment_request = AppointmentRequest.objects.create(
@ -2835,7 +2862,7 @@ def schedule_lead(request, pk):
start_time=instance.scheduled_at.time(),
end_time=(instance.scheduled_at + instance.duration).time(),
service=service,
staff_member=staff_member,
staff_member=request.user.staffmember,
)
except ValidationError as e:
messages.error(request, str(e))
@ -3426,6 +3453,10 @@ class OrderListView(ListView):
template_name = "sales/orders/order_list.html"
context_object_name = "orders"
def get_queryset(self):
dealer = get_user_type(self.request)
qs = super().get_queryset()
return qs.filter(estimate__entity=dealer.entity)
# email
@login_required

View File

@ -104,7 +104,7 @@
<script src="{% static 'vendors/dayjs/dayjs.min.js' %}"></script>
<script src="{% static 'js/phoenix.js' %}"></script>
<script src="{% static 'js/apexcharts.js' %}"></script>
<script src="{% static 'vendors/echarts/echarts.min.js' %}"></script>
{% comment %} <script src="{% static 'vendors/echarts/echarts.min.js' %}"></script> {% endcomment %}
<script src="{% static 'js/crm-analytics.js' %}"></script>
<script src="{% static 'js/travel-agency-dashboard.js' %}"></script>
<script src="{% static 'js/crm-dashboard.js' %}"></script>

View File

@ -140,11 +140,11 @@
<div class="accordion" id="accordionExample">
<div class="accordion-item">
<h2 class="accordion-header" id="headingTwo">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapse{{lead.pk}}" aria-expanded="false" aria-controls="collapseTwo">
View Schedules ({{lead.get_latest_schedules.count}})
</button>
</h2>
<div class="accordion-collapse collapse" id="collapseTwo" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
<div class="accordion-collapse collapse" id="collapse{{lead.pk}}" aria-labelledby="headingTwo" data-bs-parent="#accordionExample">
<div class="accordion-body pt-0">
<div class="d-flex flex-column gap-2">
<table><tbody>

View File

@ -50,6 +50,11 @@
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div><span>{{ _("Address")|capfirst }}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="company" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div><span>{{ _("Active")|capfirst }}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase" scope="col" data-sort="date" style="width:15%;">
{{ _("Create date") }}</th>
<th class="sort text-end align-middle pe-0 ps-4" scope="col"></th>
@ -76,6 +81,13 @@
<td class="contact align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">{{ customer.additional_info.customer_info.national_id }}</td>
<td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight">
{{ customer.address_1 }}</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">
{% if customer.active %}
<span class="badge badge-phoenix badge-phoenix-success"><i class="fas fa-check"></i> {{customer.active}}</span>
{% else %}
<span class="badge badge-phoenix badge-phoenix-danger"><i class="fas fa-times"></i> {{customer.active}}</span>
{% endif %}
</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ customer.created|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<a href="{% url 'customer_update' customer.pk %}" class="btn btn-sm btn-phoenix-primary me-2" data-url="{% url 'customer_update' customer.pk %}">

View File

@ -18,12 +18,10 @@
{% include 'partials/form_errors.html' %}
<div class="d-flex flex-column">
<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="col-lg-12 col-xl-6">
<div class="card bg-body mb-3">
<div class="card-body">
<div class="form-floating">
@ -46,7 +44,7 @@
</div>
<div class="row mt-2">
<div class="col-6">
<div class="col-md-6">
<div class="form-floating">
<span class="text-success fw-bold" id="year-check"></span>
<input type="number"
@ -56,7 +54,7 @@
<label for="{{ form.year.id_for_label }}">{% trans 'Year' %}</label>
</div>
</div>
<div id="serie-row" class="col-6">
<div id="serie-row" class="col-md-6">
<div class="form-floating">
<select class="form-select form-select-sm"
id="{{ form.id_car_serie.id_for_label }}"
@ -68,14 +66,14 @@
</div>
</div>
<div class="row mt-2">
<div class="col-6" id="make-row">
<div class="col-md-6" id="make-row">
<div class="form-floating">
<span class="text-success fw-bold" id="make-check"></span>
{{ form.id_car_make|add_class:"form-select form-select-sm" }}
<label for="{{ form.id_car_make.id_for_label }}">{% trans 'make'|capfirst %}</label>
</div>
</div>
<div class="col-6" id="trim-row">
<div class="col-md-6" id="trim-row">
<div class="form-floating">
<select class="form-select form-select-sm"
id="{{ form.id_car_trim.id_for_label }}"
@ -87,7 +85,7 @@
</div>
</div>
<div class="row mt-2">
<div class="col-6" id="model-row">
<div class="col-md-6" id="model-row">
<div class="form-floating">
<span class="text-success fw-bold" id="model-check"></span>
<select class="form-select form-select-sm"
@ -122,7 +120,7 @@
</div>
</div>
<div class="row g-3">
<div class="col-lg-4 col-xl-6">
<div class="col-xl-6">
<div class="row">
<!--Vendor Field-->

View File

@ -1,149 +1,138 @@
{% extends 'base.html' %}
{% load i18n %}
{% load static %}
{% block title %}{% trans "Organizations" %}{% endblock title %}
{% block organizations %}<a class="nav-link active">{% trans "Organizations" %}</a>{% endblock %}
{% block title %}
{% trans 'Organizations' %}
{% endblock %}
{% block organizations %}
<a class="nav-link active">{% trans 'Organizations' %}</a>
{% endblock %}
{% block content %}
<section class="pt-5 pb-9">
<section class="pt-5 pb-9">
<div class="row">
<h2 class="mb-4">{% trans 'Organizations' %}</h2>
<div class="row">
<h2 class="mb-4">{% trans "Organizations" %}</h2>
<div class="row g-3 justify-content-between mb-4">
<div class="col-auto">
<div class="d-md-flex justify-content-between">
<div>
<a href="{% url 'organization_create' %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{% trans "add organization"|capfirst %}</a>
</div>
</div>
</div>
<div class="col-auto">
<div class="d-flex">
<div class="search-box me-2">
<form method="get" class="d-inline-block position-relative">
<input name="q" class="form-control search-input search" type="search" placeholder="{{ _('Enter Organization name') }}" aria-label="Search" value="{{ request.GET.q }}"/>
<span class="fas fa-search search-box-icon"></span>
{% if request.GET.q %}
<a href="{% url request.resolver_match.view_name %}" class="btn btn-outline-danger ms-1">
<i class="bi bi-x-lg"></i>
</a>
{% endif %}
</form>
</div>
</div>
</div>
<div class="row g-3 justify-content-between mb-4">
<div class="col-auto">
<div class="d-md-flex justify-content-between">
<div>
<a href="{% url 'organization_create' %}" class="btn btn-sm btn-phoenix-primary"><span class="fas fa-plus me-2"></span>{% trans 'add organization'|capfirst %}</a>
</div>
<div class="table-responsive scrollbar mx-n1 px-1">
<table class="table table-hover fs-9 mb-0">
<thead>
<tr>
<th class="sort white-space-nowrap align-middle text-uppercase ps-0" scope="col" data-sort="name" style="width:25%;">{{ _("Name")|capfirst }}</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="name" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-success-subtle rounded me-2"><span class="fs-7 text-success-dark far fa-file-alt"></span></div><span>{% trans "CRN" %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="phone" style="width:15%; min-width: 180px;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-primary-subtle rounded me-2"><span class="fs-7 text-primary-dark fas fa-money-check-alt"></span></div><span>{% trans "VRN" %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="contact" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-info-subtle rounded me-2"><span class="text-info-dark" data-feather="phone"></span></div><span>{% trans "Phone" %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="company" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2"><span class="text-warning-dark" data-feather="grid"></span></div><span>{% trans 'Address' %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase" scope="col" data-sort="date" style="width:15%;">
{{ _("Create date") }}</th>
<th class="sort text-end align-middle pe-0 ps-4" scope="col"></th>
</tr>
</thead>
<tbody class="list" id="leal-tables-body">
{% for org in organizations %}
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal"
data-bs-backdrop="static"
data-bs-keyboard="false"
tabindex="-1"
aria-labelledby="deleteModalLabel"
aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">
{% trans "Delete Vendor" %}
<span data-feather="alert-circle"></span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center">
<p class="mb-0 text-danger fw-bold">
{% trans "Are you sure you want to delete this Organization?" %}
</p>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">
{% trans "No" %}
</button>
<a type="button" class="btn btn-danger btn-sm" href="{% url 'organization_delete' org.pk %}">
{% trans "Yes" %}
</a>
</div>
</div>
</div>
</div>
</div>
<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 'organization_detail' org.pk %}">{{ org.customer_name }}</a>
<div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2"></p><span class="badge badge-phoenix badge-phoenix-primary">{{ org.customer_name}}</span>
<img src="{{ org.additional_info.organization_info.logo }}" width="80" height="80" alt="">
</div>
</div>
</div>
</td>
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.crn }}</td>
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.vrn }}</td>
<td class="phone align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight"><a class="text-body-highlight" href="tel:{{ org.phone }}">{{ org.phone }}</a></td>
<td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight">
{{ org.address_1 }}</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ org.created|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2"><a href="{% url 'organization_update' org.pk %}" class="dropdown-item text-success-dark">
{% trans "Edit" %}
</a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans "Delete" %}</button>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="col-auto">
<div class="d-flex">
<div class="search-box me-2">
<form method="get" class="d-inline-block position-relative">
<input name="q" class="form-control search-input search" type="search" placeholder="{{ _('Enter Organization name') }}" aria-label="Search" value="{{ request.GET.q }}" />
<span class="fas fa-search search-box-icon"></span>
{% if request.GET.q %}
<a href="{% url request.resolver_match.view_name %}" class="btn btn-outline-danger ms-1"><i class="bi bi-x-lg"></i></a>
{% endif %}
</form>
</div>
</div>
</div>
</div>
<div class="table-responsive scrollbar mx-n1 px-1">
<table class="table table-hover fs-9 mb-0">
<thead>
<tr>
<th class="sort white-space-nowrap align-middle text-uppercase ps-0" scope="col" data-sort="name" style="width:25%;">{{ _('Name')|capfirst }}</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="name" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-success-subtle rounded me-2">
<span class="fs-7 text-success-dark far fa-file-alt"></span>
</div><span>{% trans 'CRN' %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="phone" style="width:15%; min-width: 180px;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-primary-subtle rounded me-2">
<span class="fs-7 text-primary-dark fas fa-money-check-alt"></span>
</div><span>{% trans 'VRN' %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="contact" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-info-subtle rounded me-2">
<span class="text-info-dark" data-feather="phone"></span>
</div><span>{% trans 'Phone' %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase border-end border-translucent" scope="col" data-sort="company" style="width:15%;">
<div class="d-inline-flex flex-center">
<div class="d-flex align-items-center px-1 py-1 bg-warning-subtle rounded me-2">
<span class="text-warning-dark" data-feather="grid"></span>
</div><span>{% trans 'Address' %}</span>
</div>
</th>
<th class="sort align-middle ps-4 pe-5 text-uppercase" scope="col" data-sort="date" style="width:15%;">{{ _('Create date') }}</th>
<th class="sort text-end align-middle pe-0 ps-4" scope="col"></th>
</tr>
</thead>
<tbody class="list" id="leal-tables-body">
{% for org in organizations %}
<!-- Delete Modal -->
<div class="modal fade" id="deleteModal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="deleteModalLabel">
{% trans 'Delete Vendor' %}
<span data-feather="alert-circle"></span>
</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center">
<p class="mb-0 text-danger fw-bold">
{% trans 'Are you sure you want to delete this Organization?' %}
</p>
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">{% trans 'No' %}</button>
<a type="button" class="btn btn-danger btn-sm" href="{% url 'organization_delete' org.pk %}">{% trans 'Yes' %}</a>
</div>
</div>
</div>
</div>
<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 'organization_detail' org.pk %}">{{ org.customer_name }}</a>
<div class="d-flex align-items-center">
<p class="mb-0 text-body-highlight fw-semibold fs-9 me-2"></p><span class="badge badge-phoenix badge-phoenix-primary">{{ org.customer_name }}</span>
</div>
</div>
</div>
</td>
<td class="email align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.crn }}</td>
<td class="phone align-middle white-space-nowrap fw-semibold ps-4 border-end border-translucent">{{ org.additional_info.organization_info.vrn }}</td>
<td class="phone align-middle white-space-nowrap ps-4 border-end border-translucent fw-semibold text-body-highlight">
<a class="text-body-highlight" href="tel:{{ org.phone }}">{{ org.phone }}</a>
</td>
<td class="company align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 border-end border-translucent fw-semibold text-body-highlight">{{ org.address_1 }}</td>
<td class="date align-middle white-space-nowrap text-body-tertiary text-opacity-85 ps-4 text-body-tertiary">{{ org.created|date }}</td>
<td class="align-middle white-space-nowrap text-end pe-0 ps-4">
<div class="btn-reveal-trigger position-static">
<button class="btn btn-sm dropdown-toggle dropdown-caret-none transition-none btn-reveal fs-10" type="button" data-bs-toggle="dropdown" data-boundary="window" aria-haspopup="true" aria-expanded="false" data-bs-reference="parent"><span class="fas fa-ellipsis-h fs-10"></span></button>
<div class="dropdown-menu dropdown-menu-end py-2">
<a href="{% url 'organization_update' org.pk %}" class="dropdown-item text-success-dark">{% trans 'Edit' %}</a>
<div class="dropdown-divider"></div><button class="dropdown-item text-danger" data-bs-toggle="modal" data-bs-target="#deleteModal">{% trans 'Delete' %}</button>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div class="d-flex justify-content-center">
{% if is_paginated %}
{% include 'partials/pagination.html' %}
{% include 'partials/pagination.html' %}
{% endif %}
</div>
</div>
</div>
</section>
{% endblock %}
</section>
{% endblock %}

View File

@ -4,18 +4,40 @@
{% block title %}{{ _("Create Quotation") }}{% endblock title %}
{% block customCSS %}
<style>
.disabled{
opacity: 0.5;
pointer-events: none;
}
</style>
{% endblock customCSS %}
{% block content %}
<div class="row mt-4">
<div class="row mt-4">
{% 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.") }}</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.") }}</p>
<button class="btn-close" type="button" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endif %}
<form id="mainForm" method="post" class="needs-validation {% if not items or not customer_count %}disabled{% endif %}">
<h3 class="text-center"><i class="fa-regular fa-file-lines"></i> {% trans "Create Quotation" %}</h3>
<form id="mainForm" method="post" class="needs-validation">
{% csrf_token %}
<div class="row g-3">
<div class="row g-3 col-10">
{{ form|crispy }}
<div class="row mt-5">
<div id="formrow">
<h3 class="text-start"><i class="fa-solid fa-car-side"></i> {{ _("Cars") }}</h3>
<div class="form-row row g-3 mb-3 mt-5">
<div class="mb-2 col-sm-4">
<div class="mb-2 col-sm-4 col-md-6 col-lg-4">
<select class="form-control item" name="item[]" required>
{% for item in items %}
<option style="background-color: rgb({{ item.color }});" value="{{ item.hash }}">{{ item.make }} {{item.model}} {{item.serie}} {{item.trim}} {{item.color_name}} ({{item.hash_count}})</option>
@ -24,10 +46,10 @@
{% endfor %}
</select>
</div>
<div class="mb-2 col-sm-2">
<div class="mb-2 col-sm-4 col-md-3">
<input class="form-control quantity" type="number" placeholder="Quantity" name="quantity[]" required>
</div>
<div class="mb-2 col-sm-1">
<div class="mb-2 col-sm-3 col-md-3">
<button class="btn btn-sm btn-danger removeBtn"><i class="fa-solid fa-trash"></i> {{ _("Remove") }}</button>
</div>
</div>

View File

@ -14,6 +14,8 @@
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Order Number" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "Customer" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col">{% trans "For Quotation" %}</th>
<th class="sort white-space-nowrap align-middle" scope="col"></th>
<th class="sort white-space-nowrap align-middle" scope="col"></th>
</tr>
</thead>
<tbody class="list">

View File

@ -32,6 +32,10 @@
<td class="align-middle product white-space-nowrap py-0">
<a href="{% url 'invoice_detail' journal.ledger.invoicemodel.pk %}"><i class="fa-solid fa-receipt"></i> {{ journal.ledger.invoicemodel }}</a>
</td>
{% elif journal.ledger.billmodel %}
<td class="align-middle product white-space-nowrap py-0">
<a href="{% url 'bill_detail' journal.ledger.billmodel.pk %}"><i class="fa-solid fa-money-bill"></i> {{ journal.ledger.billmodel }}</a>
</td>
{% else %}
<td class="align-middle product white-space-nowrap py-0"></td>
{% endif %}

View File

@ -27,8 +27,13 @@
<form class="row g-3 mb-9" method="post" class="form" novalidate>
{% csrf_token %}
{{ redirect_field }}
{{ form|crispy }}
{{ redirect_field }}
{{ form.name|as_crispy_field }}
{{ form.arabic_name|as_crispy_field }}
{{ form.email|as_crispy_field }}
{{ form.phone_number|as_crispy_field }}
{{ form.staff_type|as_crispy_field }}
{{ form.service_offered|as_crispy_field }}
{% for error in form.errors %}
<div class="text-danger">{{ error }}</div>
{% endfor %}